updartes.
[silc.git] / apps / irssi / src / silc / core / silc-channels.c
1 /*
2   silc-channels.c : irssi
3
4   Copyright (C) 2000 - 2001 Timo Sirainen
5                             Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11   
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16   
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include "module.h"
23
24 #include "net-nonblock.h"
25 #include "net-sendbuffer.h"
26 #include "signals.h"
27 #include "servers.h"
28 #include "commands.h"
29 #include "levels.h"
30 #include "modules.h"
31 #include "rawlog.h"
32 #include "misc.h"
33 #include "settings.h"
34
35 #include "channels-setup.h"
36
37 #include "silc-servers.h"
38 #include "silc-channels.h"
39 #include "silc-queries.h"
40 #include "silc-nicklist.h"
41 #include "window-item-def.h"
42
43 #include "fe-common/core/printtext.h"
44 #include "fe-common/silc/module-formats.h"
45
46 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
47                                       const char *name, int automatic)
48 {
49   SILC_CHANNEL_REC *rec;
50
51   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
52   g_return_val_if_fail(name != NULL, NULL);
53
54   rec = g_new0(SILC_CHANNEL_REC, 1);
55   rec->chat_type = SILC_PROTOCOL;
56   rec->name = g_strdup(name);
57   rec->server = server;
58
59   channel_init((CHANNEL_REC *) rec, automatic);
60   return rec;
61 }
62
63 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
64 {
65   if (!IS_SILC_CHANNEL(channel))
66     return;
67
68   if (channel->server != NULL && !channel->left && !channel->kicked) {
69     /* destroying channel record without actually
70        having left the channel yet */
71     silc_command_exec(channel->server, "PART", channel->name);
72   }
73 }
74
75 static void silc_channels_join(SILC_SERVER_REC *server,
76                                const char *channels, int automatic)
77 {
78   char **list, **tmp, *channel;
79   SILC_CHANNEL_REC *chanrec;
80
81   list = g_strsplit(channels, ",", -1);
82   for (tmp = list; *tmp != NULL; tmp++) {
83     channel = **tmp == '#' ? g_strdup(*tmp) :
84       g_strconcat("#", *tmp, NULL);
85
86     chanrec = silc_channel_find(server, channel);
87     if (chanrec) {
88       g_free(channel);
89       continue;
90     }
91
92     silc_command_exec(server, "JOIN", channel);
93     g_free(channel);
94   }
95
96   g_strfreev(list);
97 }
98
99 static void sig_connected(SILC_SERVER_REC *server)
100 {
101   if (IS_SILC_SERVER(server))
102     server->channels_join = (void *) silc_channels_join;
103 }
104
105 /* "server quit" signal from the core to indicate that QUIT command
106    was called. */
107
108 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
109 {
110   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
111     silc_command_exec(server, "QUIT", msg);
112 }
113
114 /*
115  * "event join". Joined to a channel.
116  */
117
118 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
119                                           SilcChannelEntry entry)
120 {
121   GSList *tmp;
122
123   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
124
125   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
126     SILC_CHANNEL_REC *rec = tmp->data;
127
128     if (rec->entry == entry)
129       return rec;
130   }
131
132   return NULL;
133 }
134
135 static void event_join(SILC_SERVER_REC *server, va_list va)
136 {
137   SILC_CHANNEL_REC *chanrec;
138   SILC_NICK_REC *nickrec;
139   SilcClientEntry client;
140   SilcChannelEntry channel;
141   char userhost[256];
142
143   client = va_arg(va, SilcClientEntry);
144   channel = va_arg(va, SilcChannelEntry);
145
146   if (client == server->conn->local_entry) {
147     /* You joined to channel */
148     chanrec = silc_channel_find(server, channel->channel_name);
149     if (chanrec != NULL && !chanrec->joined)
150       chanrec->entry = channel;
151   } else {
152     chanrec = silc_channel_find_entry(server, channel);
153     if (chanrec != NULL) {
154       SilcChannelUser chu = silc_client_on_channel(channel, client);
155       if (chu)
156         nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
157     }
158   }
159
160   memset(userhost, 0, sizeof(userhost));
161   if (client->username)
162     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
163              client->username, client->hostname);
164   signal_emit("message join", 4, server, channel->channel_name,
165               client->nickname,
166               client->username == NULL ? "" : userhost);
167 }
168
169 /*
170  * "event leave". Left a channel.
171  */
172
173 static void event_leave(SILC_SERVER_REC *server, va_list va)
174 {
175   SILC_CHANNEL_REC *chanrec;
176   SILC_NICK_REC *nickrec;
177   SilcClientEntry client;
178   SilcChannelEntry channel;
179   char userhost[256];
180
181   client = va_arg(va, SilcClientEntry);
182   channel = va_arg(va, SilcChannelEntry);
183
184   memset(userhost, 0, sizeof(userhost));
185   if (client->username)
186     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
187              client->username, client->hostname);
188   signal_emit("message part", 5, server, channel->channel_name,
189               client->nickname,  client->username ?  userhost : "", 
190               client->nickname);
191
192   chanrec = silc_channel_find_entry(server, channel);
193   if (chanrec != NULL) {
194     nickrec = silc_nicklist_find(chanrec, client);
195     if (nickrec != NULL)
196       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
197   }
198 }
199
200 /*
201  * "event signoff". Left the network.
202  */
203
204 static void event_signoff(SILC_SERVER_REC *server, va_list va)
205 {
206   SilcClientEntry client;
207   GSList *nicks, *tmp;
208   char *message;
209   char userhost[256];
210
211   client = va_arg(va, SilcClientEntry);
212   message = va_arg(va, char *);
213
214   silc_server_free_ftp(server, client);
215
216   memset(userhost, 0, sizeof(userhost));
217   if (client->username)
218     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
219              client->username, client->hostname);
220   signal_emit("message quit", 4, server, client->nickname,
221               client->username ? userhost : "", 
222               message ? message : "");
223
224   nicks = nicklist_get_same_unique(SERVER(server), client);
225   for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
226     CHANNEL_REC *channel = tmp->data;
227     NICK_REC *nickrec = tmp->next->data;
228     
229     nicklist_remove(channel, nickrec);
230   }
231 }
232
233 /*
234  * "event topic". Changed topic.
235  */
236
237 static void event_topic(SILC_SERVER_REC *server, va_list va)
238 {
239   SILC_CHANNEL_REC *chanrec;
240   void *entry;
241   SilcClientEntry client;
242   SilcServerEntry server_entry;
243   SilcChannelEntry channel;
244   char *topic;
245   char userhost[256];
246   SilcIdType idtype;
247
248   idtype = va_arg(va, SilcIdType);
249   entry = va_arg(va, void *);
250   topic = va_arg(va, char *);
251   channel = va_arg(va, SilcChannelEntry);
252
253   chanrec = silc_channel_find_entry(server, channel);
254   if (chanrec != NULL) {
255     g_free_not_null(chanrec->topic);
256     chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
257     signal_emit("channel topic changed", 1, chanrec);
258   }
259
260   if (idtype == SILC_ID_CLIENT) {
261     client = (SilcClientEntry)entry;
262     memset(userhost, 0, sizeof(userhost));
263     snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
264              client->username, client->hostname);
265     signal_emit("message topic", 5, server, channel->channel_name,
266                 topic, client->nickname, userhost);
267   } else if (idtype == SILC_ID_SERVER) {
268     server_entry = (SilcServerEntry)entry;
269     signal_emit("message topic", 5, server, channel->channel_name,
270                 topic, server_entry->server_name, 
271                 server_entry->server_name);
272   } else {
273     channel = (SilcChannelEntry)entry;
274     signal_emit("message topic", 5, server, channel->channel_name,
275                 topic, channel->channel_name, channel->channel_name);
276   }
277 }
278
279 /*
280  * "event invite". Invited or modified invite list.
281  */
282
283 static void event_invite(SILC_SERVER_REC *server, va_list va)
284 {
285   SilcClientEntry client;
286   SilcChannelEntry channel;
287   char *channel_name;
288   char userhost[256];
289   
290   channel = va_arg(va, SilcChannelEntry);
291   channel_name = va_arg(va, char *);
292   client = va_arg(va, SilcClientEntry);
293
294   memset(userhost, 0, sizeof(userhost));
295   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
296            client->username, client->hostname);
297   signal_emit("message invite", 4, server, channel ? channel->channel_name :
298               channel_name, client->nickname, userhost);
299 }
300
301 /*
302  * "event nick". Changed nickname.
303  */
304
305 static void event_nick(SILC_SERVER_REC *server, va_list va)
306 {
307   SilcClientEntry oldclient, newclient;
308   char userhost[256];
309
310   oldclient = va_arg(va, SilcClientEntry);
311   newclient = va_arg(va, SilcClientEntry);
312
313   nicklist_rename_unique(SERVER(server),
314                          oldclient, oldclient->nickname,
315                          newclient, newclient->nickname);
316
317   memset(userhost, 0, sizeof(userhost));
318   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
319            newclient->username, newclient->hostname);
320   signal_emit("message nick", 4, server, newclient->nickname, 
321               oldclient->nickname, userhost);
322 }
323
324 /*
325  * "event cmode". Changed channel mode.
326  */
327
328 static void event_cmode(SILC_SERVER_REC *server, va_list va)
329 {
330   SILC_CHANNEL_REC *chanrec;
331   void *entry;
332   SilcClientEntry client;
333   SilcServerEntry server_entry;
334   SilcChannelEntry channel;
335   char *mode;
336   uint32 modei;
337   SilcIdType idtype;
338
339   idtype = va_arg(va, int);
340   entry = va_arg(va, void *);
341   modei = va_arg(va, uint32);
342   (void)va_arg(va, char *);
343   (void)va_arg(va, char *);
344   channel = va_arg(va, SilcChannelEntry);
345
346   mode = silc_client_chmode(modei, 
347                             channel->channel_key ? 
348                             channel->channel_key->cipher->name : "",
349                             channel->hmac ? 
350                             silc_hmac_get_name(channel->hmac) : "");
351
352   chanrec = silc_channel_find_entry(server, channel);
353   if (chanrec != NULL) {
354     g_free_not_null(chanrec->mode);
355     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
356     signal_emit("channel mode changed", 1, chanrec);
357   }
358   
359   if (idtype == SILC_ID_CLIENT) {
360     client = (SilcClientEntry)entry;
361     printformat_module("fe-common/silc", server, channel->channel_name,
362                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
363                        channel->channel_name, mode ? mode : "removed all",
364                        client->nickname);
365   } else {
366     server_entry = (SilcServerEntry)entry;
367     printformat_module("fe-common/silc", server, channel->channel_name,
368                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
369                        channel->channel_name, mode ? mode : "removed all",
370                        server_entry->server_name);
371   }
372   
373   g_free(mode);
374 }
375
376 /*
377  * "event cumode". Changed user's mode on channel.
378  */
379
380 static void event_cumode(SILC_SERVER_REC *server, va_list va)
381 {
382   SILC_CHANNEL_REC *chanrec;
383   SilcClientEntry client, destclient;
384   SilcChannelEntry channel;
385   int mode;
386   char *modestr;
387   
388   client = va_arg(va, SilcClientEntry);
389   mode = va_arg(va, uint32);
390   destclient = va_arg(va, SilcClientEntry);
391   channel = va_arg(va, SilcChannelEntry);
392   
393   modestr = silc_client_chumode(mode);
394   chanrec = silc_channel_find_entry(server, channel);
395   if (chanrec != NULL) {
396     SILC_NICK_REC *nick;
397     
398     if (destclient == server->conn->local_entry) {
399       chanrec->chanop =
400         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
401     }
402
403     nick = silc_nicklist_find(chanrec, destclient);
404     if (nick != NULL) {
405       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
406       nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
407       signal_emit("nick mode changed", 2, chanrec, nick);
408     }
409   }
410   
411   printformat_module("fe-common/silc", server, channel->channel_name,
412                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
413                      channel->channel_name, destclient->nickname, 
414                      modestr ? modestr : "removed all",
415                      client->nickname);
416
417   if (mode & SILC_CHANNEL_UMODE_CHANFO)
418     printformat_module("fe-common/silc", 
419                        server, channel->channel_name, MSGLEVEL_CRAP,
420                        SILCTXT_CHANNEL_FOUNDER,
421                        channel->channel_name, destclient->nickname);
422
423   g_free(modestr);
424 }
425
426 /*
427  * "event motd". Received MOTD.
428  */
429
430 static void event_motd(SILC_SERVER_REC *server, va_list va)
431 {
432   char *text = va_arg(va, char *);
433
434   if (!settings_get_bool("skip_motd"))
435     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
436 }
437
438 /*
439  * "event channel_change". Channel ID has changed.
440  */
441
442 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
443 {
444   /* Nothing interesting to do */
445 }
446
447 /*
448  * "event server_signoff". Server has quit the network.
449  */
450
451 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
452 {
453   SilcClientEntry *clients;
454   uint32 clients_count;
455   int i;
456   char userhost[256];
457   
458   (void)va_arg(va, void *);
459   clients = va_arg(va, SilcClientEntry *);
460   clients_count = va_arg(va, uint32);
461   
462   for (i = 0; i < clients_count; i++) {
463     GSList *nicks, *tmp;
464
465     memset(userhost, 0, sizeof(userhost));
466     if (clients[i]->username)
467       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
468                clients[i]->username, clients[i]->hostname);
469     signal_emit("message quit", 4, server, clients[i]->nickname,
470                 clients[i]->username ? userhost : "", 
471                 "server signoff");
472
473     silc_server_free_ftp(server, clients[i]);
474
475     nicks = nicklist_get_same_unique(SERVER(server), clients[i]);
476     for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
477       CHANNEL_REC *channel = tmp->data;
478       NICK_REC *nickrec = tmp->next->data;
479       nicklist_remove(channel, nickrec);
480     }
481   }
482 }
483
484 /*
485  * "event kick". Someone was kicked from channel.
486  */
487
488 static void event_kick(SILC_SERVER_REC *server, va_list va)
489 {
490   SilcClientConnection conn = server->conn;
491   SilcClientEntry client_entry, kicker;
492   SilcChannelEntry channel_entry;
493   char *tmp;
494   SILC_CHANNEL_REC *chanrec;
495
496   client_entry = va_arg(va, SilcClientEntry);
497   tmp = va_arg(va, char *);
498   kicker = va_arg(va, SilcClientEntry);
499   channel_entry = va_arg(va, SilcChannelEntry);
500
501   chanrec = silc_channel_find_entry(server, channel_entry);
502   
503   if (client_entry == conn->local_entry) {
504     printformat_module("fe-common/silc", server, channel_entry->channel_name,
505                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
506                        kicker->nickname,
507                        channel_entry->channel_name, tmp ? tmp : "");
508     if (chanrec) {
509       chanrec->kicked = TRUE;
510       channel_destroy((CHANNEL_REC *)chanrec);
511     }
512   } else {
513     printformat_module("fe-common/silc", server, channel_entry->channel_name,
514                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
515                        client_entry->nickname,
516                        kicker->nickname,
517                        channel_entry->channel_name, tmp ? tmp : "");
518
519     if (chanrec) {
520       SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
521       if (nickrec != NULL)
522         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
523     }
524   }
525 }
526
527 /*
528  * "event kill". Someone was killed from the network.
529  */
530
531 static void event_kill(SILC_SERVER_REC *server, va_list va)
532 {
533   SilcClientConnection conn = server->conn;
534   SilcClientEntry client_entry;
535   char *tmp;
536
537   client_entry = va_arg(va, SilcClientEntry);
538   tmp = va_arg(va, char *);
539   
540   if (client_entry == conn->local_entry) {
541     printformat_module("fe-common/silc", server, NULL,
542                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
543                        tmp ? tmp : "");
544   } else {
545     GSList *nicks, *tmpn;
546     nicks = nicklist_get_same_unique(SERVER(server), client_entry);
547     for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
548       CHANNEL_REC *channel = tmpn->data;
549       NICK_REC *nickrec = tmpn->next->data;
550       nicklist_remove(channel, nickrec);
551     }
552
553     printformat_module("fe-common/silc", server, NULL,
554                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
555                        client_entry->nickname,
556                        tmp ? tmp : "");
557   }
558 }
559
560 /* PART (LEAVE) command. */
561
562 static void command_part(const char *data, SILC_SERVER_REC *server,
563                          WI_ITEM_REC *item)
564 {
565   SILC_CHANNEL_REC *chanrec;
566   char userhost[256];
567   
568   if (!IS_SILC_SERVER(server) || !server->connected)
569     cmd_return_error(CMDERR_NOT_CONNECTED);
570
571   if (!strcmp(data, "*") || *data == '\0') {
572     if (!IS_SILC_CHANNEL(item))
573       cmd_return_error(CMDERR_NOT_JOINED);
574     data = item->name;
575   }
576
577   chanrec = silc_channel_find(server, data);
578   if (chanrec == NULL) 
579     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
580
581   memset(userhost, 0, sizeof(userhost));
582   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
583            server->conn->local_entry->username, 
584            server->conn->local_entry->hostname);
585   signal_emit("message part", 5, server, chanrec->name,
586               server->nick, userhost, "");
587   
588   silc_command_exec(server, "LEAVE", chanrec->name);
589   signal_stop();
590   
591   channel_destroy(CHANNEL(chanrec));
592 }
593
594 /* ME local command. */
595
596 static void command_me(const char *data, SILC_SERVER_REC *server,
597                        WI_ITEM_REC *item)
598 {
599   SILC_CHANNEL_REC *chanrec;
600   char *tmpcmd = "ME", *tmp;
601   uint32 argc = 0;
602   unsigned char **argv;
603   uint32 *argv_lens, *argv_types;
604   int i;
605  
606   if (!IS_SILC_SERVER(server) || !server->connected)
607     cmd_return_error(CMDERR_NOT_CONNECTED);
608
609   if (!IS_SILC_CHANNEL(item))
610     cmd_return_error(CMDERR_NOT_JOINED);
611
612   /* Now parse all arguments */
613   tmp = g_strconcat(tmpcmd, " ", data, NULL);
614   silc_parse_command_line(tmp, &argv, &argv_lens,
615                           &argv_types, &argc, 2);
616   g_free(tmp);
617
618   if (argc < 2)
619     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
620
621   chanrec = silc_channel_find(server, item->name);
622   if (chanrec == NULL) 
623     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
624
625   /* Send the action message */
626   silc_client_send_channel_message(silc_client, server->conn, 
627                                    chanrec->entry, NULL,
628                                    SILC_MESSAGE_FLAG_ACTION, 
629                                    argv[1], argv_lens[1], TRUE);
630
631   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
632                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
633                      server->conn->local_entry->nickname, argv[1]);
634
635   for (i = 0; i < argc; i++)
636     silc_free(argv[i]);
637   silc_free(argv_lens);
638   silc_free(argv_types);
639 }
640
641 /* ACTION local command. Same as ME but takes the channel as mandatory
642    argument. */
643
644 static void command_action(const char *data, SILC_SERVER_REC *server,
645                            WI_ITEM_REC *item)
646 {
647   SILC_CHANNEL_REC *chanrec;
648   char *tmpcmd = "ME", *tmp;
649   uint32 argc = 0;
650   unsigned char **argv;
651   uint32 *argv_lens, *argv_types;
652   int i;
653  
654   if (!IS_SILC_SERVER(server) || !server->connected)
655     cmd_return_error(CMDERR_NOT_CONNECTED);
656
657   if (!IS_SILC_CHANNEL(item))
658     cmd_return_error(CMDERR_NOT_JOINED);
659
660   /* Now parse all arguments */
661   tmp = g_strconcat(tmpcmd, " ", data, NULL);
662   silc_parse_command_line(tmp, &argv, &argv_lens,
663                           &argv_types, &argc, 3);
664   g_free(tmp);
665
666   if (argc < 3)
667     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
668
669   chanrec = silc_channel_find(server, argv[1]);
670   if (chanrec == NULL) 
671     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
672
673   /* Send the action message */
674   silc_client_send_channel_message(silc_client, server->conn, 
675                                    chanrec->entry, NULL,
676                                    SILC_MESSAGE_FLAG_ACTION, 
677                                    argv[2], argv_lens[2], TRUE);
678
679   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
680                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
681                      server->conn->local_entry->nickname, argv[2]);
682
683   for (i = 0; i < argc; i++)
684     silc_free(argv[i]);
685   silc_free(argv_lens);
686   silc_free(argv_types);
687 }
688
689 /* NOTICE local command. */
690
691 static void command_notice(const char *data, SILC_SERVER_REC *server,
692                            WI_ITEM_REC *item)
693 {
694   SILC_CHANNEL_REC *chanrec;
695   char *tmpcmd = "ME", *tmp;
696   uint32 argc = 0;
697   unsigned char **argv;
698   uint32 *argv_lens, *argv_types;
699   int i;
700  
701   if (!IS_SILC_SERVER(server) || !server->connected)
702     cmd_return_error(CMDERR_NOT_CONNECTED);
703
704   if (!IS_SILC_CHANNEL(item))
705     cmd_return_error(CMDERR_NOT_JOINED);
706
707   /* Now parse all arguments */
708   tmp = g_strconcat(tmpcmd, " ", data, NULL);
709   silc_parse_command_line(tmp, &argv, &argv_lens,
710                           &argv_types, &argc, 2);
711   g_free(tmp);
712
713   if (argc < 2)
714     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
715
716   chanrec = silc_channel_find(server, item->name);
717   if (chanrec == NULL) 
718     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
719
720   /* Send the action message */
721   silc_client_send_channel_message(silc_client, server->conn, 
722                                    chanrec->entry, NULL,
723                                    SILC_MESSAGE_FLAG_NOTICE, 
724                                    argv[1], argv_lens[1], TRUE);
725
726   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
727                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
728                      server->conn->local_entry->nickname, argv[1]);
729
730   for (i = 0; i < argc; i++)
731     silc_free(argv[i]);
732   silc_free(argv_lens);
733   silc_free(argv_types);
734 }
735
736 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
737    flag. */
738
739 static void command_away(const char *data, SILC_SERVER_REC *server,
740                          WI_ITEM_REC *item)
741 {
742   bool set;
743
744   if (!IS_SILC_SERVER(server) || !server->connected)
745     cmd_return_error(CMDERR_NOT_CONNECTED);
746
747   if (*data == '\0') {
748     /* Remove any possible away message */
749     silc_client_set_away_message(silc_client, server->conn, NULL);
750     set = FALSE;
751
752     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
753                        SILCTXT_UNSET_AWAY);
754   } else {
755     /* Set the away message */
756     silc_client_set_away_message(silc_client, server->conn, (char *)data);
757     set = TRUE;
758
759     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
760                        SILCTXT_SET_AWAY, data);
761   }
762
763   signal_emit("away mode changed", 1, server);
764
765   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
766 }
767
768 typedef struct {
769   int type;                     /* 1 = msg, 2 = channel */
770   bool responder;
771   SILC_SERVER_REC *server;
772 } *KeyInternal;
773
774 /* Key agreement callback that is called after the key agreement protocol
775    has been performed. This is called also if error occured during the
776    key agreement protocol. The `key' is the allocated key material and
777    the caller is responsible of freeing it. The `key' is NULL if error
778    has occured. The application can freely use the `key' to whatever
779    purpose it needs. See lib/silcske/silcske.h for the definition of
780    the SilcSKEKeyMaterial structure. */
781
782 static void keyagr_completion(SilcClient client,
783                               SilcClientConnection conn,
784                               SilcClientEntry client_entry,
785                               SilcKeyAgreementStatus status,
786                               SilcSKEKeyMaterial *key,
787                               void *context)
788 {
789   KeyInternal i = (KeyInternal)context;
790
791   switch(status) {
792   case SILC_KEY_AGREEMENT_OK:
793     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
794                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
795
796     if (i->type == 1) {
797       /* Set the private key for this client */
798       silc_client_del_private_message_key(client, conn, client_entry);
799       silc_client_add_private_message_key_ske(client, conn, client_entry,
800                                               NULL, key, i->responder);
801       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
802                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
803                          client_entry->nickname);
804       silc_ske_free_key_material(key);
805     }
806     
807     break;
808     
809   case SILC_KEY_AGREEMENT_ERROR:
810     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
811                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
812     break;
813     
814   case SILC_KEY_AGREEMENT_FAILURE:
815     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
816                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
817     break;
818     
819   case SILC_KEY_AGREEMENT_TIMEOUT:
820     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
821                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
822     break;
823     
824   case SILC_KEY_AGREEMENT_ABORTED:
825     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
826                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
827     break;
828     
829   default:
830     break;
831   } 
832
833   if (i)
834     silc_free(i);
835 }
836
837 /* Local command KEY. This command is used to set and unset private
838    keys for channels, set and unset private keys for private messages
839    with remote clients and to send key agreement requests and
840    negotiate the key agreement protocol with remote client.  The
841    key agreement is supported only to negotiate private message keys,
842    it currently cannot be used to negotiate private keys for channels,
843    as it is not convenient for that purpose. */
844
845 typedef struct {
846   SILC_SERVER_REC *server;
847   char *data;
848   char *nick;
849   WI_ITEM_REC *item;
850 } *KeyGetClients;
851
852 /* Callback to be called after client information is resolved from the
853    server. */
854
855 static void silc_client_command_key_get_clients(SilcClient client,
856                                                 SilcClientConnection conn,
857                                                 SilcClientEntry *clients,
858                                                 uint32 clients_count,
859                                                 void *context)
860 {
861   KeyGetClients internal = (KeyGetClients)context;
862
863   if (!clients) {
864     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
865               internal->nick);
866     silc_free(internal->data);
867     silc_free(internal->nick);
868     silc_free(internal);
869     return;
870   }
871
872   signal_emit("command key", 3, internal->data, internal->server,
873               internal->item);
874
875   silc_free(internal->data);
876   silc_free(internal->nick);
877   silc_free(internal);
878 }
879
880 static void command_key(const char *data, SILC_SERVER_REC *server,
881                         WI_ITEM_REC *item)
882 {
883   SilcClientConnection conn;
884   SilcClientEntry *entrys, client_entry = NULL;
885   uint32 entry_count;
886   SilcChannelEntry channel_entry = NULL;
887   char *nickname = NULL, *tmp;
888   int command = 0, port = 0, type = 0;
889   char *hostname = NULL;
890   KeyInternal internal = NULL;
891   uint32 argc = 0;
892   unsigned char **argv;
893   uint32 *argv_lens, *argv_types;
894   char *bindhost = NULL;
895  
896   if (!server || !IS_SILC_SERVER(server) || !server->connected)
897     cmd_return_error(CMDERR_NOT_CONNECTED);
898
899   conn = server->conn;
900
901   /* Now parse all arguments */
902   tmp = g_strconcat("KEY", " ", data, NULL);
903   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
904   g_free(tmp);
905
906   if (argc < 4)
907     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
908
909   /* Get type */
910   if (!strcasecmp(argv[1], "msg"))
911     type = 1;
912   if (!strcasecmp(argv[1], "channel"))
913     type = 2;
914
915   if (type == 0)
916     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
917
918   if (type == 1) {
919     if (argv[2][0] == '*') {
920       nickname = strdup("*");
921     } else {
922       /* Parse the typed nickname. */
923       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
924         printformat_module("fe-common/silc", server, NULL,
925                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
926         return;
927       }
928       
929       /* Find client entry */
930       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
931                                              argv[2], &entry_count);
932       if (!entrys) {
933         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
934         inter->server = server;
935         inter->data = strdup(data);
936         inter->nick = strdup(nickname);
937         inter->item = item;
938         silc_client_get_clients(silc_client, conn, nickname, argv[2],
939                                 silc_client_command_key_get_clients, inter);
940         goto out;
941       }
942       client_entry = entrys[0];
943       silc_free(entrys);
944     }
945   }
946
947   if (type == 2) {
948     /* Get channel entry */
949     char *name;
950
951     if (argv[2][0] == '*') {
952       if (!conn->current_channel) {
953         silc_free(nickname);
954         cmd_return_error(CMDERR_NOT_JOINED);
955       }
956       name = conn->current_channel->channel_name;
957     } else {
958       name = argv[2];
959     }
960
961     channel_entry = silc_client_get_channel(silc_client, conn, name);
962     if (!channel_entry) {
963       silc_free(nickname);
964       cmd_return_error(CMDERR_NOT_JOINED);
965     }
966   }
967
968   /* Set command */
969   if (!strcasecmp(argv[3], "set")) {
970     command = 1;
971
972     if (argc >= 5) {
973       if (type == 1 && client_entry) {
974         /* Set private message key */
975         
976         silc_client_del_private_message_key(silc_client, conn, client_entry);
977
978         if (argc >= 6)
979           silc_client_add_private_message_key(silc_client, conn, client_entry,
980                                               argv[5], argv[4],
981                                               argv_lens[4],
982                                               (argv[4][0] == '*' ?
983                                                TRUE : FALSE), FALSE);
984         else
985           silc_client_add_private_message_key(silc_client, conn, client_entry,
986                                               NULL, argv[4],
987                                               argv_lens[4],
988                                               (argv[4][0] == '*' ?
989                                                TRUE : FALSE), FALSE);
990
991         /* Send the key to the remote client so that it starts using it
992            too. */
993         silc_client_send_private_message_key(silc_client, conn, 
994                                              client_entry, TRUE);
995       } else if (type == 2) {
996         /* Set private channel key */
997         char *cipher = NULL, *hmac = NULL;
998
999         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
1000           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1001                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
1002                              channel_entry->channel_name);
1003           goto out;
1004         }
1005
1006         if (argc >= 6)
1007           cipher = argv[5];
1008         if (argc >= 7)
1009           hmac = argv[6];
1010
1011         if (!silc_client_add_channel_private_key(silc_client, conn, 
1012                                                  channel_entry,
1013                                                  cipher, hmac,
1014                                                  argv[4],
1015                                                  argv_lens[4])) {
1016           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1017                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
1018                              channel_entry->channel_name);
1019           goto out;
1020         }
1021
1022         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1023                            SILCTXT_CH_PRIVATE_KEY_ADD, 
1024                            channel_entry->channel_name);
1025       }
1026     }
1027
1028     goto out;
1029   }
1030   
1031   /* Unset command */
1032   if (!strcasecmp(argv[3], "unset")) {
1033     command = 2;
1034
1035     if (type == 1 && client_entry) {
1036       /* Unset private message key */
1037       silc_client_del_private_message_key(silc_client, conn, client_entry);
1038     } else if (type == 2) {
1039       /* Unset channel key(s) */
1040       SilcChannelPrivateKey *keys;
1041       uint32 keys_count;
1042       int number;
1043
1044       if (argc == 4)
1045         silc_client_del_channel_private_keys(silc_client, conn, 
1046                                              channel_entry);
1047
1048       if (argc > 4) {
1049         number = atoi(argv[4]);
1050         keys = silc_client_list_channel_private_keys(silc_client, conn, 
1051                                                      channel_entry,
1052                                                      &keys_count);
1053         if (!keys)
1054           goto out;
1055
1056         if (!number || number > keys_count) {
1057           silc_client_free_channel_private_keys(keys, keys_count);
1058           goto out;
1059         }
1060
1061         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
1062                                             keys[number - 1]);
1063         silc_client_free_channel_private_keys(keys, keys_count);
1064       }
1065
1066       goto out;
1067     }
1068   }
1069
1070   /* List command */
1071   if (!strcasecmp(argv[3], "list")) {
1072     command = 3;
1073
1074     if (type == 1) {
1075       SilcPrivateMessageKeys keys;
1076       uint32 keys_count;
1077       int k, i, len;
1078       char buf[1024];
1079
1080       keys = silc_client_list_private_message_keys(silc_client, conn, 
1081                                                    &keys_count);
1082       if (!keys)
1083         goto out;
1084
1085       /* list the private message key(s) */
1086       if (nickname[0] == '*') {
1087         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1088                            SILCTXT_PRIVATE_KEY_LIST);
1089         for (k = 0; k < keys_count; k++) {
1090           memset(buf, 0, sizeof(buf));
1091           strncat(buf, "  ", 2);
1092           len = strlen(keys[k].client_entry->nickname);
1093           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1094           if (len < 30)
1095             for (i = 0; i < 30 - len; i++)
1096               strcat(buf, " ");
1097           strcat(buf, " ");
1098           
1099           len = strlen(keys[k].cipher);
1100           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1101           if (len < 14)
1102             for (i = 0; i < 14 - len; i++)
1103               strcat(buf, " ");
1104           strcat(buf, " ");
1105
1106           if (keys[k].key)
1107             strcat(buf, "<hidden>");
1108           else
1109             strcat(buf, "*generated*");
1110
1111           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1112         }
1113       } else {
1114         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1115                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1116                            client_entry->nickname);
1117         for (k = 0; k < keys_count; k++) {
1118           if (keys[k].client_entry != client_entry)
1119             continue;
1120
1121           memset(buf, 0, sizeof(buf));
1122           strncat(buf, "  ", 2);
1123           len = strlen(keys[k].client_entry->nickname);
1124           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1125           if (len < 30)
1126             for (i = 0; i < 30 - len; i++)
1127               strcat(buf, " ");
1128           strcat(buf, " ");
1129           
1130           len = strlen(keys[k].cipher);
1131           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1132           if (len < 14)
1133             for (i = 0; i < 14 - len; i++)
1134               strcat(buf, " ");
1135           strcat(buf, " ");
1136
1137           if (keys[k].key)
1138             strcat(buf, "<hidden>");
1139           else
1140             strcat(buf, "*generated*");
1141
1142           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1143         }
1144       }
1145
1146       silc_client_free_private_message_keys(keys, keys_count);
1147
1148     } else if (type == 2) {
1149       SilcChannelPrivateKey *keys;
1150       uint32 keys_count;
1151       int k, i, len;
1152       char buf[1024];
1153
1154       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1155                                                    channel_entry,
1156                                                    &keys_count);
1157
1158       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1159                          SILCTXT_CH_PRIVATE_KEY_LIST,
1160                          channel_entry->channel_name);
1161
1162       if (!keys)
1163         goto out;
1164       
1165       for (k = 0; k < keys_count; k++) {
1166         memset(buf, 0, sizeof(buf));
1167         strncat(buf, "  ", 2);
1168
1169         len = strlen(keys[k]->cipher->cipher->name);
1170         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1171         if (len < 16)
1172           for (i = 0; i < 16 - len; i++)
1173             strcat(buf, " ");
1174         strcat(buf, " ");
1175         
1176         len = strlen(silc_hmac_get_name(keys[k]->hmac));
1177         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
1178         if (len < 16)
1179           for (i = 0; i < 16 - len; i++)
1180             strcat(buf, " ");
1181         strcat(buf, " ");
1182         
1183         strcat(buf, "<hidden>");
1184
1185         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1186       }
1187       
1188       silc_client_free_channel_private_keys(keys, keys_count);
1189     }
1190
1191     goto out;
1192   }
1193
1194   /* Send command is used to send key agreement */
1195   if (!strcasecmp(argv[3], "agreement")) {
1196     command = 4;
1197
1198     if (argc >= 5)
1199       hostname = argv[4];
1200     if (argc >= 6)
1201       port = atoi(argv[5]);
1202
1203     internal = silc_calloc(1, sizeof(*internal));
1204     internal->type = type;
1205     internal->server = server;
1206     
1207     if (!hostname) {
1208       if (settings_get_bool("use_auto_addr")) {
1209        
1210         hostname = (char *)settings_get_str("auto_public_ip");
1211
1212 /* If the hostname isn't set, treat this case as if auto_public_ip wasn't
1213  * set.
1214  */
1215         if ((hostname) && (*hostname == '\0')) {
1216            hostname = NULL;
1217         }
1218         else {
1219           bindhost = (char *)settings_get_str("auto_bind_ip");
1220             
1221 /* if the bind_ip isn't set, but the public_ip IS, then assume then
1222  * public_ip is the same value as the bind_ip.
1223  */
1224           if ((bindhost) && (*bindhost == '\0')) {
1225             bindhost = hostname;
1226           }
1227            port = settings_get_int("auto_bind_port");
1228         }
1229       }  /* if use_auto_addr */
1230     }
1231   }
1232
1233   /* Start command is used to start key agreement (after receiving the
1234      key_agreement client operation). */
1235   if (!strcasecmp(argv[3], "negotiate")) {
1236     command = 5;
1237
1238     if (argc >= 5)
1239       hostname = argv[4];
1240     if (argc >= 6)
1241       port = atoi(argv[5]);
1242
1243     internal = silc_calloc(1, sizeof(*internal));
1244     internal->type = type;
1245     internal->server = server;
1246   }
1247
1248   if (command == 0) {
1249     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1250              "Usage: /KEY msg|channel <nickname|channel> "
1251              "set|unset|agreement|negotiate [<arguments>]");
1252     goto out;
1253   }
1254
1255   if (command == 4 && client_entry) {
1256     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1257                        SILCTXT_KEY_AGREEMENT, argv[2]);
1258     internal->responder = TRUE;
1259     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1260                                    bindhost, port, 120, keyagr_completion, 
1261                                    internal);
1262     if (!hostname)
1263       silc_free(internal);
1264     goto out;
1265   }
1266
1267   if (command == 5 && client_entry && hostname) {
1268     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1269                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1270     internal->responder = FALSE;
1271     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1272                                       hostname, port, keyagr_completion, 
1273                                       internal);
1274     goto out;
1275   }
1276
1277  out:
1278   silc_free(nickname);
1279 }
1280
1281 /* Lists locally saved client and server public keys. */
1282
1283 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1284                              WI_ITEM_REC *item)
1285 {
1286
1287 }
1288
1289 void silc_channels_init(void)
1290 {
1291   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1292   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1293   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1294
1295   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1296   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1297   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1298   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1299   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1300   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1301   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1302   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1303   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1304   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1305   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1306   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1307   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1308   
1309   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1310   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1311   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1312   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1313   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1314   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1315   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1316
1317   silc_nicklist_init();
1318 }
1319
1320 void silc_channels_deinit(void)
1321 {
1322   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1323   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1324   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1325
1326   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1327   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1328   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1329   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1330   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1331   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1332   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1333   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1334   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1335   signal_remove("silc event channel_change", 
1336                 (SIGNAL_FUNC) event_channel_change);
1337   signal_remove("silc event server_signoff", 
1338                 (SIGNAL_FUNC) event_server_signoff);
1339   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1340   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1341   
1342   command_unbind("part", (SIGNAL_FUNC) command_part);
1343   command_unbind("me", (SIGNAL_FUNC) command_me);
1344   command_unbind("action", (SIGNAL_FUNC) command_action);
1345   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1346   command_unbind("away", (SIGNAL_FUNC) command_away);
1347   command_unbind("key", (SIGNAL_FUNC) command_key);
1348   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1349
1350   silc_nicklist_deinit();
1351 }