Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-common / core / fe-messages.c
1 /*
2  fe-messages.c : irssi
3
4     Copyright (C) 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 "levels.h"
26 #include "misc.h"
27 #include "special-vars.h"
28 #include "settings.h"
29
30 #include "servers.h"
31 #include "channels.h"
32 #include "nicklist.h"
33 #include "ignore.h"
34 #include "recode.h"
35
36 #include "window-items.h"
37 #include "fe-queries.h"
38 #include "hilight-text.h"
39 #include "printtext.h"
40
41 #define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c))
42 #define isnickchar(a) \
43         (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
44         (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
45         (a) == '|' || (a) == '\\' || (a) == '^')
46
47 GHashTable *printnicks;
48
49 /* convert _underlined_ and *bold* words (and phrases) to use real
50    underlining or bolding */
51 char *expand_emphasis(WI_ITEM_REC *item, const char *text)
52 {
53         GString *str;
54         char *ret;
55         int pos;
56
57         g_return_val_if_fail(text != NULL, NULL);
58
59         str = g_string_new(text);
60
61         for (pos = 0; pos < str->len; pos++) {
62                 char type, *bgn, *end;
63
64                 bgn = str->str + pos;
65
66                 if (*bgn == '*') 
67                         type = 2; /* bold */
68                 else if (*bgn == '_') 
69                         type = 31; /* underlined */
70                 else
71                         continue;
72
73                 /* check that the beginning marker starts a word, and
74                    that the matching end marker ends a word */
75                 if ((pos > 0 && bgn[-1] != ' ') || !ishighalnum(bgn[1]))
76                         continue;
77                 if ((end = strchr(bgn+1, *bgn)) == NULL)
78                         continue;
79                 if (!ishighalnum(end[-1]) || ishighalnum(end[1]) ||
80                     end[1] == type || end[1] == '*' || end[1] == '_')
81                         continue;
82
83                 if (IS_CHANNEL(item)) {
84                         /* check that this isn't a _nick_, we don't want to
85                            use emphasis on them. */
86                         int found;
87                         char c;
88                         char *end2; 
89
90                         /* check if _foo_ is a nick */
91                         c = end[1];
92                         end[1] = '\0';
93                         found = nicklist_find(CHANNEL(item), bgn) != NULL;
94                         end[1] = c;
95                         if (found) continue;
96                         
97                         /* check if the whole 'word' (e.g. "_foo_^") is a nick
98                            in "_foo_^ ", end will be the second _, end2 the ^ */
99                         end2 = end;
100                         while (isnickchar(end2[1]))
101                                 end2++;
102                         c = end2[1];
103                         end2[1] = '\0';
104                         found = nicklist_find(CHANNEL(item), bgn) != NULL;
105                         end2[1] = c;
106                         if (found) continue;
107                 }
108
109                 /* allow only *word* emphasis, not *multiple words* */
110                 if (!settings_get_bool("emphasis_multiword")) {
111                         char *c;
112                         for (c = bgn+1; c != end; c++) {
113                                 if (!ishighalnum(*c))
114                                         break;
115                         }
116                         if (c != end) continue;
117                 }
118
119                 if (settings_get_bool("emphasis_replace")) {
120                         *bgn = *end = type;
121                         pos += (end-bgn);
122                 } else {
123                         g_string_insert_c(str, pos, type);
124                         pos += (end - bgn) + 2;
125                         g_string_insert_c(str, pos++, type);
126                 }
127         }
128
129         ret = str->str;
130         g_string_free(str, FALSE);
131         return ret;
132 }
133
134 static char *channel_get_nickmode_rec(NICK_REC *nickrec)
135 {
136         char *emptystr;
137         static char nickmode[2]; /* FIXME: bad */
138
139         if (!settings_get_bool("show_nickmode"))
140                 return "";
141
142         emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
143
144         if (nickrec != NULL && nickrec->other) {
145                 nickmode[0] = nickrec->other;
146                 nickmode[1] = '\0';
147                 return nickmode;
148         }
149
150         return nickrec == NULL ? emptystr :
151                 nickrec->op ? "@" :
152                 nickrec->halfop ? "%" :
153                 nickrec->voice ? "+" :
154                 emptystr;
155 }
156
157 char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
158 {
159         g_return_val_if_fail(nick != NULL, NULL);
160
161         return channel_get_nickmode_rec(channel == NULL ? NULL :
162                                         nicklist_find(channel, nick));
163 }
164
165 static void sig_message_public(SERVER_REC *server, const char *msg,
166                                const char *nick, const char *address,
167                                const char *target, NICK_REC *nickrec)
168 {
169         CHANNEL_REC *chanrec;
170         const char *nickmode, *printnick;
171         int for_me, print_channel, level;
172         char *color, *freemsg = NULL;
173         HILIGHT_REC *hilight;
174
175         /* NOTE: this may return NULL if some channel is just closed with
176            /WINDOW CLOSE and server still sends the few last messages */
177         chanrec = channel_find(server, target);
178         if (nickrec == NULL && chanrec != NULL)
179                 nickrec = nicklist_find(chanrec, nick);
180
181         for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
182                 nick_match_msg(chanrec, msg, server->nick);
183         hilight = for_me ? NULL :
184                 hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
185         color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
186
187         print_channel = chanrec == NULL ||
188                 !window_item_is_active((WI_ITEM_REC *) chanrec);
189         if (!print_channel && settings_get_bool("print_active_channel") &&
190             window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
191                 print_channel = TRUE;
192
193         level = MSGLEVEL_PUBLIC;
194         if (for_me)
195                 level |= MSGLEVEL_HILIGHT;
196
197         if (settings_get_bool("emphasis"))
198                 msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
199
200         /* get nick mode & nick what to print the msg with
201            (in case there's multiple identical nicks) */
202         nickmode = channel_get_nickmode_rec(nickrec);
203         printnick = nickrec == NULL ? nick :
204                 g_hash_table_lookup(printnicks, nickrec);
205         if (printnick == NULL)
206                 printnick = nick;
207
208         if (color != NULL) {
209                 /* highlighted nick */
210                 TEXT_DEST_REC dest;
211                 format_create_dest(&dest, server, target, level, NULL);
212                 hilight_update_text_dest(&dest,hilight);
213                 if (!print_channel) /* message to active channel in window */
214                         printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color,
215                                          printnick, msg, nickmode);
216                 else /* message to not existing/active channel */
217                         printformat_dest(&dest, TXT_PUBMSG_HILIGHT_CHANNEL,
218                                          color, printnick, target, msg,
219                                          nickmode);
220         } else {
221                 if (!print_channel)
222                         printformat(server, target, level,
223                                     for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
224                                     printnick, msg, nickmode);
225                 else
226                         printformat(server, target, level,
227                                     for_me ? TXT_PUBMSG_ME_CHANNEL :
228                                     TXT_PUBMSG_CHANNEL,
229                                     printnick, target, msg, nickmode);
230         }                                               
231
232         g_free_not_null(freemsg);
233         g_free_not_null(color);
234 }
235
236 static void sig_message_private(SERVER_REC *server, const char *msg,
237                                 const char *nick, const char *address)
238 {
239         QUERY_REC *query;
240         char *freemsg = NULL;
241
242         query = query_find(server, nick);
243
244         if (settings_get_bool("emphasis"))
245                 msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
246
247         printformat(server, nick, MSGLEVEL_MSGS,
248                     query == NULL ? TXT_MSG_PRIVATE :
249                     TXT_MSG_PRIVATE_QUERY, nick, address, msg);
250
251         g_free_not_null(freemsg);
252 }
253
254 static void sig_message_own_public(SERVER_REC *server, const char *msg,
255                                    const char *target)
256 {
257         WINDOW_REC *window;
258         CHANNEL_REC *channel;
259         const char *nickmode;
260         char *freemsg = NULL, *recoded;
261         int print_channel;
262         channel = channel_find(server, target);
263         if (channel != NULL)
264                 target = channel->visible_name;
265
266         nickmode = channel_get_nickmode(channel, server->nick);
267
268         window = channel == NULL ? NULL :
269                 window_item_window((WI_ITEM_REC *) channel);
270
271         print_channel = window == NULL ||
272                 window->active != (WI_ITEM_REC *) channel;
273
274         if (!print_channel && settings_get_bool("print_active_channel") &&
275             window != NULL && g_slist_length(window->items) > 1)
276                 print_channel = TRUE;
277
278         if (settings_get_bool("emphasis"))
279                 msg = freemsg = expand_emphasis((WI_ITEM_REC *) channel, msg);
280
281         /* ugly: recode the sent message back for printing */ 
282         recoded = recode_in(server, msg, target);
283         
284         if (!print_channel) {
285                 printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
286                             TXT_OWN_MSG, server->nick, recoded, nickmode);
287         } else {
288                 printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
289                             TXT_OWN_MSG_CHANNEL, server->nick, target, recoded, nickmode);
290         }
291
292         g_free(recoded);
293         g_free_not_null(freemsg);
294 }
295
296 static void sig_message_own_private(SERVER_REC *server, const char *msg,
297                                     const char *target, const char *origtarget)
298 {
299         QUERY_REC *query;
300         char *freemsg = NULL, *recoded;
301
302         g_return_if_fail(server != NULL);
303         g_return_if_fail(msg != NULL);
304         if (target == NULL) {
305                 /* this should only happen if some special target failed and
306                    we should display some error message. currently the special
307                    targets are only ',' and '.'. */
308                 g_return_if_fail(strcmp(origtarget, ",") == 0 ||
309                                  strcmp(origtarget, ".") == 0);
310
311                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
312                             *origtarget == ',' ? TXT_NO_MSGS_GOT :
313                             TXT_NO_MSGS_SENT);
314                 signal_stop();
315                 return;
316         }
317
318         query = privmsg_get_query(server, target, TRUE, MSGLEVEL_MSGS);
319
320         if (settings_get_bool("emphasis"))
321                 msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
322
323         /* ugly: recode the sent message back for printing */
324         recoded = recode_in(server, msg, target);
325
326         printformat(server, target,
327                     MSGLEVEL_MSGS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
328                     query == NULL ? TXT_OWN_MSG_PRIVATE :
329                     TXT_OWN_MSG_PRIVATE_QUERY, target, recoded, server->nick);
330
331         g_free(recoded);
332         g_free_not_null(freemsg);
333 }
334
335 static void sig_message_join(SERVER_REC *server, const char *channel,
336                              const char *nick, const char *address)
337 {
338         printformat(server, channel, MSGLEVEL_JOINS,
339                     TXT_JOIN, nick, address, channel);
340 }
341
342 static void sig_message_part(SERVER_REC *server, const char *channel,
343                              const char *nick, const char *address,
344                              const char *reason)
345 {
346         printformat(server, channel, MSGLEVEL_PARTS,
347                     TXT_PART, nick, address, channel, reason);
348 }
349
350 static void sig_message_quit(SERVER_REC *server, const char *nick,
351                              const char *address, const char *reason)
352 {
353         WINDOW_REC *window;
354         GString *chans;
355         GSList *tmp, *windows;
356         char *print_channel, *recoded;
357         int once, count;
358
359         if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS))
360                 return;
361
362         print_channel = NULL;
363         once = settings_get_bool("show_quit_once");
364
365         count = 0; windows = NULL;
366         chans = g_string_new(NULL);
367         for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
368                 CHANNEL_REC *rec = tmp->data;
369
370                 if (!nicklist_find(rec, nick))
371                         continue;
372
373                 if (ignore_check(server, nick, address, rec->visible_name,
374                                  reason, MSGLEVEL_QUITS)) {
375                         count++;
376                         continue;
377                 }
378
379                 if (print_channel == NULL ||
380                     active_win->active == (WI_ITEM_REC *) rec)
381                         print_channel = rec->visible_name;
382
383                 if (once)
384                         g_string_sprintfa(chans, "%s,", rec->visible_name);
385                 else {
386                         window = window_item_window((WI_ITEM_REC *) rec);
387                         if (g_slist_find(windows, window) == NULL) {
388                                 windows = g_slist_append(windows, window);
389                                 recoded = recode_in(server, reason, rec->visible_name);
390                                 printformat(server, rec->visible_name,
391                                             MSGLEVEL_QUITS,
392                                             TXT_QUIT, nick, address, recoded,
393                                             rec->visible_name);
394                                 g_free(recoded);
395                         }
396                 }
397                 count++;
398         }
399         g_slist_free(windows);
400
401         if (!once) {
402                 /* check if you had query with the nick and
403                    display the quit there too */
404                 QUERY_REC *query = query_find(server, nick);
405                 if (query != NULL) {
406                         recoded = recode_in(server, reason, nick);
407                         printformat(server, nick, MSGLEVEL_QUITS,
408                                     TXT_QUIT, nick, address, recoded, "");
409                         g_free(recoded);
410                 }
411         }
412
413         if (once || count == 0) {
414                 if (chans->len > 0)
415                         g_string_truncate(chans, chans->len-1);
416                 /* at least recode_fallback will be used */
417                 recoded = recode_in(server, reason, nick);
418                 printformat(server, print_channel, MSGLEVEL_QUITS,
419                             count <= 1 ? TXT_QUIT : TXT_QUIT_ONCE,
420                             nick, address, recoded, chans->str);
421                 g_free(recoded);
422         }
423         g_string_free(chans, TRUE);
424 }
425
426 static void sig_message_kick(SERVER_REC *server, const char *channel,
427                              const char *nick, const char *kicker,
428                              const char *address, const char *reason)
429 {
430         printformat(server, channel, MSGLEVEL_KICKS,
431                     TXT_KICK, nick, channel, kicker, reason, address);
432 }
433
434 static void print_nick_change_channel(SERVER_REC *server, const char *channel,
435                                       const char *newnick, const char *oldnick,
436                                       const char *address,
437                                       int ownnick)
438 {
439         int level;
440
441         if (ignore_check(server, oldnick, address,
442                          channel, newnick, MSGLEVEL_NICKS))
443                 return;
444
445         level = MSGLEVEL_NICKS;
446         if (ownnick) level |= MSGLEVEL_NO_ACT;
447
448         printformat(server, channel, level,
449                     ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED,
450                     oldnick, newnick, channel, address);
451 }
452
453 static void print_nick_change(SERVER_REC *server, const char *newnick,
454                               const char *oldnick, const char *address,
455                               int ownnick)
456 {
457         GSList *tmp, *windows;
458         int msgprint;
459
460         msgprint = FALSE;
461
462         /* Print to each channel/query where the nick is.
463            Don't print more than once to the same window. */
464         windows = NULL;
465         for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
466                 CHANNEL_REC *channel = tmp->data;
467                 WINDOW_REC *window =
468                         window_item_window((WI_ITEM_REC *) channel);
469
470                 if (nicklist_find(channel, newnick) == NULL ||
471                     g_slist_find(windows, window) != NULL)
472                         continue;
473
474                 windows = g_slist_append(windows, window);
475                 print_nick_change_channel(server, channel->visible_name,
476                                           newnick, oldnick, address, ownnick);
477                 msgprint = TRUE;
478         }
479
480         g_slist_free(windows);
481
482         if (!msgprint && ownnick) {
483                 printformat(server, NULL, MSGLEVEL_NICKS,
484                             TXT_YOUR_NICK_CHANGED, oldnick, newnick, "",
485                             address);
486         }
487 }
488
489 static void sig_message_nick(SERVER_REC *server, const char *newnick,
490                              const char *oldnick, const char *address)
491 {
492         print_nick_change(server, newnick, oldnick, address, FALSE);
493 }
494
495 static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
496                                  const char *oldnick, const char *address)
497 {
498         if (!settings_get_bool("show_own_nickchange_once"))
499                 print_nick_change(server, newnick, oldnick, address, TRUE);
500         else {
501                 printformat(server, NULL, MSGLEVEL_NICKS,
502                             TXT_YOUR_NICK_CHANGED, oldnick, newnick, "",
503                             address);
504         }
505 }
506
507 static void sig_message_invite(SERVER_REC *server, const char *channel,
508                                const char *nick, const char *address)
509 {
510         char *str;
511
512         str = show_lowascii(channel);
513         printformat(server, NULL, MSGLEVEL_INVITES,
514                     TXT_INVITE, nick, str, address);
515         g_free(str);
516 }
517
518 static void sig_message_topic(SERVER_REC *server, const char *channel,
519                               const char *topic,
520                               const char *nick, const char *address)
521 {
522         printformat(server, channel, MSGLEVEL_TOPICS,
523                     *topic != '\0' ? TXT_NEW_TOPIC : TXT_TOPIC_UNSET,
524                     nick, channel, topic, address);
525 }
526
527 static int printnick_exists(NICK_REC *first, NICK_REC *ignore,
528                             const char *nick)
529 {
530         char *printnick;
531
532         while (first != NULL) {
533                 if (first != ignore) {
534                         printnick = g_hash_table_lookup(printnicks, first);
535                         if (printnick != NULL && strcmp(printnick, nick) == 0)
536                                 return TRUE;
537                 }
538
539                 first = first->next;
540         }
541
542         return FALSE;
543 }
544
545 static NICK_REC *printnick_find_original(NICK_REC *nick)
546 {
547         while (nick != NULL) {
548                 if (g_hash_table_lookup(printnicks, nick) == NULL)
549                         return nick;
550
551                 nick = nick->next;
552         }
553
554         return NULL;
555 }
556
557 static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
558 {
559         NICK_REC *firstnick;
560         GString *newnick;
561         char *nickhost, *p;
562         int n;
563
564         if (nick->host == NULL)
565                 return;
566
567         firstnick = g_hash_table_lookup(channel->nicks, nick->nick);
568         if (firstnick->next == NULL)
569                 return;
570
571         if (nick == channel->ownnick) {
572                 /* own nick is being added, might be a nick change and
573                    someone else having the original nick already in use.. */
574                 nick = printnick_find_original(firstnick->next);
575                 if (nick == NULL)
576                         return; /* nope, we have it */
577         }
578
579         /* identical nick already exists, have to change it somehow.. */
580         p = strchr(nick->host, '@');
581         if (p == NULL) p = nick->host; else p++;
582
583         nickhost = g_strdup_printf("%s@%s", nick->nick, p);
584         p = strchr(nickhost+strlen(nick->nick), '.');
585         if (p != NULL) *p = '\0';
586
587         if (!printnick_exists(firstnick, nick, nickhost)) {
588                 /* use nick@host */
589                 g_hash_table_insert(printnicks, nick, nickhost);
590                 return;
591         }
592
593         newnick = g_string_new(NULL);
594         n = 2;
595         do {
596                 g_string_sprintf(newnick, "%s%d", nickhost, n);
597                 n++;
598         } while (printnick_exists(firstnick, nick, newnick->str));
599
600         g_hash_table_insert(printnicks, nick, newnick->str);
601         g_string_free(newnick, FALSE);
602         g_free(nickhost);
603 }
604
605 static void sig_nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
606 {
607         char *nickname;
608
609         nickname = g_hash_table_lookup(printnicks, nick);
610         if (nickname != NULL) {
611                 g_free(nickname);
612                 g_hash_table_remove(printnicks, nick);
613         }
614 }
615
616 static void sig_nicklist_changed(CHANNEL_REC *channel, NICK_REC *nick)
617 {
618         sig_nicklist_remove(channel, nick);
619         sig_nicklist_new(channel, nick);
620 }
621
622 static void sig_channel_joined(CHANNEL_REC *channel)
623 {
624         NICK_REC *nick;
625         char *nickname;
626
627         /* channel->ownnick is set at this point - check if our own nick
628            has been changed, if it was set it back to the original nick and
629            change the previous original to something else */
630
631         nickname = g_hash_table_lookup(printnicks, channel->ownnick);
632         if (nickname == NULL)
633                 return;
634
635         g_free(nickname);
636         g_hash_table_remove(printnicks, channel->ownnick);
637
638         /* our own nick is guaranteed to be the first in list */
639         nick = channel->ownnick->next;
640         while (nick != NULL) {
641                 if (g_hash_table_lookup(printnicks, nick) == NULL) {
642                         sig_nicklist_new(channel, nick);
643                         break;
644                 }
645                 nick = nick->next;
646         }
647 }
648
649 static void g_hash_free_value(void *key, void *value)
650 {
651         g_free(value);
652 }
653
654 void fe_messages_init(void)
655 {
656         printnicks = g_hash_table_new((GHashFunc) g_direct_hash,
657                                       (GCompareFunc) g_direct_equal);
658
659         settings_add_bool("lookandfeel", "hilight_nick_matches", TRUE);
660         settings_add_bool("lookandfeel", "emphasis", TRUE);
661         settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
662         settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);
663         settings_add_bool("lookandfeel", "show_nickmode", TRUE);
664         settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE);
665         settings_add_bool("lookandfeel", "print_active_channel", FALSE);
666         settings_add_bool("lookandfeel", "show_quit_once", FALSE);
667         settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE);
668
669         signal_add_last("message public", (SIGNAL_FUNC) sig_message_public);
670         signal_add_last("message private", (SIGNAL_FUNC) sig_message_private);
671         signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public);
672         signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private);
673         signal_add_last("message join", (SIGNAL_FUNC) sig_message_join);
674         signal_add_last("message part", (SIGNAL_FUNC) sig_message_part);
675         signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit);
676         signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick);
677         signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick);
678         signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
679         signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite);
680         signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic);
681
682         signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
683         signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
684         signal_add("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
685         signal_add("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
686         signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined);
687 }
688
689 void fe_messages_deinit(void)
690 {
691         g_hash_table_foreach(printnicks, (GHFunc) g_hash_free_value, NULL);
692         g_hash_table_destroy(printnicks);
693
694         signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
695         signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
696         signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
697         signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
698         signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
699         signal_remove("message part", (SIGNAL_FUNC) sig_message_part);
700         signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit);
701         signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick);
702         signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick);
703         signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
704         signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite);
705         signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic);
706
707         signal_remove("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
708         signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
709         signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
710         signal_remove("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
711         signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
712 }