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