updates.
[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, int);
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->cipher->name,
348                             silc_hmac_get_name(channel->hmac));
349   
350   chanrec = silc_channel_find_entry(server, channel);
351   if (chanrec != NULL) {
352     g_free_not_null(chanrec->mode);
353     chanrec->mode = g_strdup(mode == NULL ? "" : mode);
354     signal_emit("channel mode changed", 1, chanrec);
355   }
356   
357   if (idtype == SILC_ID_CLIENT) {
358     client = (SilcClientEntry)entry;
359     printformat_module("fe-common/silc", server, channel->channel_name,
360                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
361                        channel->channel_name, mode ? mode : "removed all",
362                        client->nickname);
363   } else {
364     server_entry = (SilcServerEntry)entry;
365     printformat_module("fe-common/silc", server, channel->channel_name,
366                        MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
367                        channel->channel_name, mode ? mode : "removed all",
368                        server_entry->server_name);
369   }
370   
371   g_free(mode);
372 }
373
374 /*
375  * "event cumode". Changed user's mode on channel.
376  */
377
378 static void event_cumode(SILC_SERVER_REC *server, va_list va)
379 {
380   SILC_CHANNEL_REC *chanrec;
381   SilcClientEntry client, destclient;
382   SilcChannelEntry channel;
383   int mode;
384   char *modestr;
385   
386   client = va_arg(va, SilcClientEntry);
387   mode = va_arg(va, uint32);
388   destclient = va_arg(va, SilcClientEntry);
389   channel = va_arg(va, SilcChannelEntry);
390   
391   modestr = silc_client_chumode(mode);
392   chanrec = silc_channel_find_entry(server, channel);
393   if (chanrec != NULL) {
394     SILC_NICK_REC *nick;
395     
396     if (destclient == server->conn->local_entry) {
397       chanrec->chanop =
398         (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
399     }
400
401     nick = silc_nicklist_find(chanrec, destclient);
402     if (nick != NULL) {
403       nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
404       nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
405       signal_emit("nick mode changed", 2, chanrec, nick);
406     }
407   }
408   
409   printformat_module("fe-common/silc", server, channel->channel_name,
410                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
411                      channel->channel_name, destclient->nickname, 
412                      modestr ? modestr : "removed all",
413                      client->nickname);
414
415   if (mode & SILC_CHANNEL_UMODE_CHANFO)
416     printformat_module("fe-common/silc", 
417                        server, channel->channel_name, MSGLEVEL_CRAP,
418                        SILCTXT_CHANNEL_FOUNDER,
419                        channel->channel_name, destclient->nickname);
420
421   g_free(modestr);
422 }
423
424 /*
425  * "event motd". Received MOTD.
426  */
427
428 static void event_motd(SILC_SERVER_REC *server, va_list va)
429 {
430   char *text = va_arg(va, char *);
431
432   if (!settings_get_bool("skip_motd"))
433     printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
434 }
435
436 /*
437  * "event channel_change". Channel ID has changed.
438  */
439
440 static void event_channel_change(SILC_SERVER_REC *server, va_list va)
441 {
442   /* Nothing interesting to do */
443 }
444
445 /*
446  * "event server_signoff". Server has quit the network.
447  */
448
449 static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
450 {
451   SilcClientEntry *clients;
452   uint32 clients_count;
453   int i;
454   char userhost[256];
455   
456   (void)va_arg(va, void *);
457   clients = va_arg(va, SilcClientEntry *);
458   clients_count = va_arg(va, uint32);
459   
460   for (i = 0; i < clients_count; i++) {
461     GSList *nicks, *tmp;
462
463     memset(userhost, 0, sizeof(userhost));
464     if (clients[i]->username)
465       snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
466                clients[i]->username, clients[i]->hostname);
467     signal_emit("message quit", 4, server, clients[i]->nickname,
468                 clients[i]->username ? userhost : "", 
469                 "server signoff");
470
471     silc_server_free_ftp(server, clients[i]);
472
473     nicks = nicklist_get_same_unique(SERVER(server), clients[i]);
474     for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
475       CHANNEL_REC *channel = tmp->data;
476       NICK_REC *nickrec = tmp->next->data;
477       nicklist_remove(channel, nickrec);
478     }
479   }
480 }
481
482 /*
483  * "event kick". Someone was kicked from channel.
484  */
485
486 static void event_kick(SILC_SERVER_REC *server, va_list va)
487 {
488   SilcClientConnection conn = server->conn;
489   SilcClientEntry client_entry, kicker;
490   SilcChannelEntry channel_entry;
491   char *tmp;
492   SILC_CHANNEL_REC *chanrec;
493
494   client_entry = va_arg(va, SilcClientEntry);
495   tmp = va_arg(va, char *);
496   kicker = va_arg(va, SilcClientEntry);
497   channel_entry = va_arg(va, SilcChannelEntry);
498
499   chanrec = silc_channel_find_entry(server, channel_entry);
500   
501   if (client_entry == conn->local_entry) {
502     printformat_module("fe-common/silc", server, channel_entry->channel_name,
503                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
504                        kicker->nickname,
505                        channel_entry->channel_name, tmp ? tmp : "");
506     if (chanrec) {
507       chanrec->kicked = TRUE;
508       channel_destroy((CHANNEL_REC *)chanrec);
509     }
510   } else {
511     printformat_module("fe-common/silc", server, channel_entry->channel_name,
512                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
513                        client_entry->nickname,
514                        kicker->nickname,
515                        channel_entry->channel_name, tmp ? tmp : "");
516
517     if (chanrec) {
518       SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
519       if (nickrec != NULL)
520         nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
521     }
522   }
523 }
524
525 /*
526  * "event kill". Someone was killed from the network.
527  */
528
529 static void event_kill(SILC_SERVER_REC *server, va_list va)
530 {
531   SilcClientConnection conn = server->conn;
532   SilcClientEntry client_entry;
533   char *tmp;
534
535   client_entry = va_arg(va, SilcClientEntry);
536   tmp = va_arg(va, char *);
537   
538   if (client_entry == conn->local_entry) {
539     printformat_module("fe-common/silc", server, NULL,
540                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
541                        tmp ? tmp : "");
542   } else {
543     GSList *nicks, *tmpn;
544     nicks = nicklist_get_same_unique(SERVER(server), client_entry);
545     for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
546       CHANNEL_REC *channel = tmpn->data;
547       NICK_REC *nickrec = tmpn->next->data;
548       nicklist_remove(channel, nickrec);
549     }
550
551     printformat_module("fe-common/silc", server, NULL,
552                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
553                        client_entry->nickname,
554                        tmp ? tmp : "");
555   }
556 }
557
558 /* PART (LEAVE) command. */
559
560 static void command_part(const char *data, SILC_SERVER_REC *server,
561                          WI_ITEM_REC *item)
562 {
563   SILC_CHANNEL_REC *chanrec;
564   char userhost[256];
565   
566   if (!IS_SILC_SERVER(server) || !server->connected)
567     cmd_return_error(CMDERR_NOT_CONNECTED);
568
569   if (!strcmp(data, "*") || *data == '\0') {
570     if (!IS_SILC_CHANNEL(item))
571       cmd_return_error(CMDERR_NOT_JOINED);
572     data = item->name;
573   }
574
575   chanrec = silc_channel_find(server, data);
576   if (chanrec == NULL) 
577     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
578
579   memset(userhost, 0, sizeof(userhost));
580   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
581            server->conn->local_entry->username, 
582            server->conn->local_entry->hostname);
583   signal_emit("message part", 5, server, chanrec->name,
584               server->nick, userhost, "");
585   
586   silc_command_exec(server, "LEAVE", chanrec->name);
587   signal_stop();
588   
589   channel_destroy(CHANNEL(chanrec));
590 }
591
592 /* ME local command. */
593
594 static void command_me(const char *data, SILC_SERVER_REC *server,
595                        WI_ITEM_REC *item)
596 {
597   SILC_CHANNEL_REC *chanrec;
598   char *tmpcmd = "ME", *tmp;
599   uint32 argc = 0;
600   unsigned char **argv;
601   uint32 *argv_lens, *argv_types;
602   int i;
603  
604   if (!IS_SILC_SERVER(server) || !server->connected)
605     cmd_return_error(CMDERR_NOT_CONNECTED);
606
607   if (!IS_SILC_CHANNEL(item))
608     cmd_return_error(CMDERR_NOT_JOINED);
609
610   /* Now parse all arguments */
611   tmp = g_strconcat(tmpcmd, " ", data, NULL);
612   silc_parse_command_line(tmp, &argv, &argv_lens,
613                           &argv_types, &argc, 2);
614   g_free(tmp);
615
616   if (argc < 2)
617     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
618
619   chanrec = silc_channel_find(server, item->name);
620   if (chanrec == NULL) 
621     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
622
623   /* Send the action message */
624   silc_client_send_channel_message(silc_client, server->conn, 
625                                    chanrec->entry, NULL,
626                                    SILC_MESSAGE_FLAG_ACTION, 
627                                    argv[1], argv_lens[1], TRUE);
628
629   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
630                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
631                      server->conn->local_entry->nickname, argv[1]);
632
633   for (i = 0; i < argc; i++)
634     silc_free(argv[i]);
635   silc_free(argv_lens);
636   silc_free(argv_types);
637 }
638
639 /* ACTION local command. Same as ME but takes the channel as mandatory
640    argument. */
641
642 static void command_action(const char *data, SILC_SERVER_REC *server,
643                            WI_ITEM_REC *item)
644 {
645   SILC_CHANNEL_REC *chanrec;
646   char *tmpcmd = "ME", *tmp;
647   uint32 argc = 0;
648   unsigned char **argv;
649   uint32 *argv_lens, *argv_types;
650   int i;
651  
652   if (!IS_SILC_SERVER(server) || !server->connected)
653     cmd_return_error(CMDERR_NOT_CONNECTED);
654
655   if (!IS_SILC_CHANNEL(item))
656     cmd_return_error(CMDERR_NOT_JOINED);
657
658   /* Now parse all arguments */
659   tmp = g_strconcat(tmpcmd, " ", data, NULL);
660   silc_parse_command_line(tmp, &argv, &argv_lens,
661                           &argv_types, &argc, 3);
662   g_free(tmp);
663
664   if (argc < 3)
665     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
666
667   chanrec = silc_channel_find(server, argv[1]);
668   if (chanrec == NULL) 
669     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
670
671   /* Send the action message */
672   silc_client_send_channel_message(silc_client, server->conn, 
673                                    chanrec->entry, NULL,
674                                    SILC_MESSAGE_FLAG_ACTION, 
675                                    argv[2], argv_lens[2], TRUE);
676
677   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
678                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
679                      server->conn->local_entry->nickname, argv[2]);
680
681   for (i = 0; i < argc; i++)
682     silc_free(argv[i]);
683   silc_free(argv_lens);
684   silc_free(argv_types);
685 }
686
687 /* NOTICE local command. */
688
689 static void command_notice(const char *data, SILC_SERVER_REC *server,
690                            WI_ITEM_REC *item)
691 {
692   SILC_CHANNEL_REC *chanrec;
693   char *tmpcmd = "ME", *tmp;
694   uint32 argc = 0;
695   unsigned char **argv;
696   uint32 *argv_lens, *argv_types;
697   int i;
698  
699   if (!IS_SILC_SERVER(server) || !server->connected)
700     cmd_return_error(CMDERR_NOT_CONNECTED);
701
702   if (!IS_SILC_CHANNEL(item))
703     cmd_return_error(CMDERR_NOT_JOINED);
704
705   /* Now parse all arguments */
706   tmp = g_strconcat(tmpcmd, " ", data, NULL);
707   silc_parse_command_line(tmp, &argv, &argv_lens,
708                           &argv_types, &argc, 2);
709   g_free(tmp);
710
711   if (argc < 2)
712     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
713
714   chanrec = silc_channel_find(server, item->name);
715   if (chanrec == NULL) 
716     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
717
718   /* Send the action message */
719   silc_client_send_channel_message(silc_client, server->conn, 
720                                    chanrec->entry, NULL,
721                                    SILC_MESSAGE_FLAG_NOTICE, 
722                                    argv[1], argv_lens[1], TRUE);
723
724   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
725                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
726                      server->conn->local_entry->nickname, argv[1]);
727
728   for (i = 0; i < argc; i++)
729     silc_free(argv[i]);
730   silc_free(argv_lens);
731   silc_free(argv_types);
732 }
733
734 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
735    flag. */
736
737 static void command_away(const char *data, SILC_SERVER_REC *server,
738                          WI_ITEM_REC *item)
739 {
740   bool set;
741
742   if (!IS_SILC_SERVER(server) || !server->connected)
743     cmd_return_error(CMDERR_NOT_CONNECTED);
744
745   if (*data == '\0') {
746     /* Remove any possible away message */
747     silc_client_set_away_message(silc_client, server->conn, NULL);
748     set = FALSE;
749
750     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
751                        SILCTXT_UNSET_AWAY);
752   } else {
753     /* Set the away message */
754     silc_client_set_away_message(silc_client, server->conn, (char *)data);
755     set = TRUE;
756
757     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
758                        SILCTXT_SET_AWAY, data);
759   }
760
761   signal_emit("away mode changed", 1, server);
762
763   silc_command_exec(server, "UMODE", set ? "+g" : "-g");
764 }
765
766 typedef struct {
767   int type;                     /* 1 = msg, 2 = channel */
768   bool responder;
769   SILC_SERVER_REC *server;
770 } *KeyInternal;
771
772 /* Key agreement callback that is called after the key agreement protocol
773    has been performed. This is called also if error occured during the
774    key agreement protocol. The `key' is the allocated key material and
775    the caller is responsible of freeing it. The `key' is NULL if error
776    has occured. The application can freely use the `key' to whatever
777    purpose it needs. See lib/silcske/silcske.h for the definition of
778    the SilcSKEKeyMaterial structure. */
779
780 static void keyagr_completion(SilcClient client,
781                               SilcClientConnection conn,
782                               SilcClientEntry client_entry,
783                               SilcKeyAgreementStatus status,
784                               SilcSKEKeyMaterial *key,
785                               void *context)
786 {
787   KeyInternal i = (KeyInternal)context;
788
789   switch(status) {
790   case SILC_KEY_AGREEMENT_OK:
791     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
792                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
793
794     if (i->type == 1) {
795       /* Set the private key for this client */
796       silc_client_del_private_message_key(client, conn, client_entry);
797       silc_client_add_private_message_key_ske(client, conn, client_entry,
798                                               NULL, key, i->responder);
799       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
800                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
801                          client_entry->nickname);
802       silc_ske_free_key_material(key);
803     }
804     
805     break;
806     
807   case SILC_KEY_AGREEMENT_ERROR:
808     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
809                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
810     break;
811     
812   case SILC_KEY_AGREEMENT_FAILURE:
813     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
814                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
815     break;
816     
817   case SILC_KEY_AGREEMENT_TIMEOUT:
818     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
819                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
820     break;
821     
822   case SILC_KEY_AGREEMENT_ABORTED:
823     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
824                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
825     break;
826     
827   default:
828     break;
829   } 
830
831   if (i)
832     silc_free(i);
833 }
834
835 /* Local command KEY. This command is used to set and unset private
836    keys for channels, set and unset private keys for private messages
837    with remote clients and to send key agreement requests and
838    negotiate the key agreement protocol with remote client.  The
839    key agreement is supported only to negotiate private message keys,
840    it currently cannot be used to negotiate private keys for channels,
841    as it is not convenient for that purpose. */
842
843 typedef struct {
844   SILC_SERVER_REC *server;
845   char *data;
846   char *nick;
847   WI_ITEM_REC *item;
848 } *KeyGetClients;
849
850 /* Callback to be called after client information is resolved from the
851    server. */
852
853 static void silc_client_command_key_get_clients(SilcClient client,
854                                                 SilcClientConnection conn,
855                                                 SilcClientEntry *clients,
856                                                 uint32 clients_count,
857                                                 void *context)
858 {
859   KeyGetClients internal = (KeyGetClients)context;
860
861   if (!clients) {
862     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
863               internal->nick);
864     silc_free(internal->data);
865     silc_free(internal->nick);
866     silc_free(internal);
867     return;
868   }
869
870   signal_emit("command key", 3, internal->data, internal->server,
871               internal->item);
872
873   silc_free(internal->data);
874   silc_free(internal->nick);
875   silc_free(internal);
876 }
877
878 static void command_key(const char *data, SILC_SERVER_REC *server,
879                         WI_ITEM_REC *item)
880 {
881   SilcClientConnection conn;
882   SilcClientEntry *entrys, client_entry = NULL;
883   uint32 entry_count;
884   SilcChannelEntry channel_entry = NULL;
885   char *nickname = NULL, *tmp;
886   int command = 0, port = 0, type = 0;
887   char *hostname = NULL;
888   KeyInternal internal = NULL;
889   uint32 argc = 0;
890   unsigned char **argv;
891   uint32 *argv_lens, *argv_types;
892   char *bindhost = NULL;
893  
894   if (!server || !IS_SILC_SERVER(server) || !server->connected)
895     cmd_return_error(CMDERR_NOT_CONNECTED);
896
897   conn = server->conn;
898
899   /* Now parse all arguments */
900   tmp = g_strconcat("KEY", " ", data, NULL);
901   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
902   g_free(tmp);
903
904   if (argc < 4)
905     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
906
907   /* Get type */
908   if (!strcasecmp(argv[1], "msg"))
909     type = 1;
910   if (!strcasecmp(argv[1], "channel"))
911     type = 2;
912
913   if (type == 0)
914     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
915
916   if (type == 1) {
917     if (argv[2][0] == '*') {
918       nickname = strdup("*");
919     } else {
920       /* Parse the typed nickname. */
921       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
922         printformat_module("fe-common/silc", server, NULL,
923                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
924         return;
925       }
926       
927       /* Find client entry */
928       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
929                                              argv[2], &entry_count);
930       if (!entrys) {
931         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
932         inter->server = server;
933         inter->data = strdup(data);
934         inter->nick = strdup(nickname);
935         inter->item = item;
936         silc_client_get_clients(silc_client, conn, nickname, argv[2],
937                                 silc_client_command_key_get_clients, inter);
938         goto out;
939       }
940       client_entry = entrys[0];
941       silc_free(entrys);
942     }
943   }
944
945   if (type == 2) {
946     /* Get channel entry */
947     char *name;
948
949     if (argv[2][0] == '*') {
950       if (!conn->current_channel) {
951         silc_free(nickname);
952         cmd_return_error(CMDERR_NOT_JOINED);
953       }
954       name = conn->current_channel->channel_name;
955     } else {
956       name = argv[2];
957     }
958
959     channel_entry = silc_client_get_channel(silc_client, conn, name);
960     if (!channel_entry) {
961       silc_free(nickname);
962       cmd_return_error(CMDERR_NOT_JOINED);
963     }
964   }
965
966   /* Set command */
967   if (!strcasecmp(argv[3], "set")) {
968     command = 1;
969
970     if (argc >= 5) {
971       if (type == 1 && client_entry) {
972         /* Set private message key */
973         
974         silc_client_del_private_message_key(silc_client, conn, client_entry);
975
976         if (argc >= 6)
977           silc_client_add_private_message_key(silc_client, conn, client_entry,
978                                               argv[5], argv[4],
979                                               argv_lens[4],
980                                               (argv[4][0] == '*' ?
981                                                TRUE : FALSE), FALSE);
982         else
983           silc_client_add_private_message_key(silc_client, conn, client_entry,
984                                               NULL, argv[4],
985                                               argv_lens[4],
986                                               (argv[4][0] == '*' ?
987                                                TRUE : FALSE), FALSE);
988
989         /* Send the key to the remote client so that it starts using it
990            too. */
991         silc_client_send_private_message_key(silc_client, conn, 
992                                              client_entry, TRUE);
993       } else if (type == 2) {
994         /* Set private channel key */
995         char *cipher = NULL, *hmac = NULL;
996
997         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
998           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
999                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
1000                              channel_entry->channel_name);
1001           goto out;
1002         }
1003
1004         if (argc >= 6)
1005           cipher = argv[5];
1006         if (argc >= 7)
1007           hmac = argv[6];
1008
1009         if (!silc_client_add_channel_private_key(silc_client, conn, 
1010                                                  channel_entry,
1011                                                  cipher, hmac,
1012                                                  argv[4],
1013                                                  argv_lens[4])) {
1014           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1015                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
1016                              channel_entry->channel_name);
1017           goto out;
1018         }
1019
1020         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1021                            SILCTXT_CH_PRIVATE_KEY_ADD, 
1022                            channel_entry->channel_name);
1023       }
1024     }
1025
1026     goto out;
1027   }
1028   
1029   /* Unset command */
1030   if (!strcasecmp(argv[3], "unset")) {
1031     command = 2;
1032
1033     if (type == 1 && client_entry) {
1034       /* Unset private message key */
1035       silc_client_del_private_message_key(silc_client, conn, client_entry);
1036     } else if (type == 2) {
1037       /* Unset channel key(s) */
1038       SilcChannelPrivateKey *keys;
1039       uint32 keys_count;
1040       int number;
1041
1042       if (argc == 4)
1043         silc_client_del_channel_private_keys(silc_client, conn, 
1044                                              channel_entry);
1045
1046       if (argc > 4) {
1047         number = atoi(argv[4]);
1048         keys = silc_client_list_channel_private_keys(silc_client, conn, 
1049                                                      channel_entry,
1050                                                      &keys_count);
1051         if (!keys)
1052           goto out;
1053
1054         if (!number || number > keys_count) {
1055           silc_client_free_channel_private_keys(keys, keys_count);
1056           goto out;
1057         }
1058
1059         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
1060                                             keys[number - 1]);
1061         silc_client_free_channel_private_keys(keys, keys_count);
1062       }
1063
1064       goto out;
1065     }
1066   }
1067
1068   /* List command */
1069   if (!strcasecmp(argv[3], "list")) {
1070     command = 3;
1071
1072     if (type == 1) {
1073       SilcPrivateMessageKeys keys;
1074       uint32 keys_count;
1075       int k, i, len;
1076       char buf[1024];
1077
1078       keys = silc_client_list_private_message_keys(silc_client, conn, 
1079                                                    &keys_count);
1080       if (!keys)
1081         goto out;
1082
1083       /* list the private message key(s) */
1084       if (nickname[0] == '*') {
1085         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1086                            SILCTXT_PRIVATE_KEY_LIST);
1087         for (k = 0; k < keys_count; k++) {
1088           memset(buf, 0, sizeof(buf));
1089           strncat(buf, "  ", 2);
1090           len = strlen(keys[k].client_entry->nickname);
1091           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1092           if (len < 30)
1093             for (i = 0; i < 30 - len; i++)
1094               strcat(buf, " ");
1095           strcat(buf, " ");
1096           
1097           len = strlen(keys[k].cipher);
1098           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1099           if (len < 14)
1100             for (i = 0; i < 14 - len; i++)
1101               strcat(buf, " ");
1102           strcat(buf, " ");
1103
1104           if (keys[k].key)
1105             strcat(buf, "<hidden>");
1106           else
1107             strcat(buf, "*generated*");
1108
1109           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1110         }
1111       } else {
1112         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1113                            SILCTXT_PRIVATE_KEY_LIST_NICK,
1114                            client_entry->nickname);
1115         for (k = 0; k < keys_count; k++) {
1116           if (keys[k].client_entry != client_entry)
1117             continue;
1118
1119           memset(buf, 0, sizeof(buf));
1120           strncat(buf, "  ", 2);
1121           len = strlen(keys[k].client_entry->nickname);
1122           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
1123           if (len < 30)
1124             for (i = 0; i < 30 - len; i++)
1125               strcat(buf, " ");
1126           strcat(buf, " ");
1127           
1128           len = strlen(keys[k].cipher);
1129           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
1130           if (len < 14)
1131             for (i = 0; i < 14 - len; i++)
1132               strcat(buf, " ");
1133           strcat(buf, " ");
1134
1135           if (keys[k].key)
1136             strcat(buf, "<hidden>");
1137           else
1138             strcat(buf, "*generated*");
1139
1140           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1141         }
1142       }
1143
1144       silc_client_free_private_message_keys(keys, keys_count);
1145
1146     } else if (type == 2) {
1147       SilcChannelPrivateKey *keys;
1148       uint32 keys_count;
1149       int k, i, len;
1150       char buf[1024];
1151
1152       keys = silc_client_list_channel_private_keys(silc_client, conn, 
1153                                                    channel_entry,
1154                                                    &keys_count);
1155
1156       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1157                          SILCTXT_CH_PRIVATE_KEY_LIST,
1158                          channel_entry->channel_name);
1159
1160       if (!keys)
1161         goto out;
1162       
1163       for (k = 0; k < keys_count; k++) {
1164         memset(buf, 0, sizeof(buf));
1165         strncat(buf, "  ", 2);
1166
1167         len = strlen(keys[k]->cipher->cipher->name);
1168         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
1169         if (len < 16)
1170           for (i = 0; i < 16 - len; i++)
1171             strcat(buf, " ");
1172         strcat(buf, " ");
1173         
1174         len = strlen(silc_hmac_get_name(keys[k]->hmac));
1175         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
1176         if (len < 16)
1177           for (i = 0; i < 16 - len; i++)
1178             strcat(buf, " ");
1179         strcat(buf, " ");
1180         
1181         strcat(buf, "<hidden>");
1182
1183         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
1184       }
1185       
1186       silc_client_free_channel_private_keys(keys, keys_count);
1187     }
1188
1189     goto out;
1190   }
1191
1192   /* Send command is used to send key agreement */
1193   if (!strcasecmp(argv[3], "agreement")) {
1194     command = 4;
1195
1196     if (argc >= 5)
1197       hostname = argv[4];
1198     if (argc >= 6)
1199       port = atoi(argv[5]);
1200
1201     internal = silc_calloc(1, sizeof(*internal));
1202     internal->type = type;
1203     internal->server = server;
1204     
1205     if (!hostname) {
1206       if (settings_get_bool("use_auto_addr")) {
1207        
1208         hostname = (char *)settings_get_str("auto_public_ip");
1209
1210 /* If the hostname isn't set, treat this case as if auto_public_ip wasn't
1211  * set.
1212  */
1213         if ((hostname) && (*hostname == '\0')) {
1214            hostname = NULL;
1215         }
1216         else {
1217           bindhost = (char *)settings_get_str("auto_bind_ip");
1218             
1219 /* if the bind_ip isn't set, but the public_ip IS, then assume then
1220  * public_ip is the same value as the bind_ip.
1221  */
1222           if ((bindhost) && (*bindhost == '\0')) {
1223             bindhost = hostname;
1224           }
1225            port = settings_get_int("auto_bind_port");
1226         }
1227       }  /* if use_auto_addr */
1228     }
1229   }
1230
1231   /* Start command is used to start key agreement (after receiving the
1232      key_agreement client operation). */
1233   if (!strcasecmp(argv[3], "negotiate")) {
1234     command = 5;
1235
1236     if (argc >= 5)
1237       hostname = argv[4];
1238     if (argc >= 6)
1239       port = atoi(argv[5]);
1240
1241     internal = silc_calloc(1, sizeof(*internal));
1242     internal->type = type;
1243     internal->server = server;
1244   }
1245
1246   if (command == 0) {
1247     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
1248              "Usage: /KEY msg|channel <nickname|channel> "
1249              "set|unset|agreement|negotiate [<arguments>]");
1250     goto out;
1251   }
1252
1253   if (command == 4 && client_entry) {
1254     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1255                        SILCTXT_KEY_AGREEMENT, argv[2]);
1256     internal->responder = TRUE;
1257     silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
1258                                    bindhost, port, 120, keyagr_completion, 
1259                                    internal);
1260     if (!hostname)
1261       silc_free(internal);
1262     goto out;
1263   }
1264
1265   if (command == 5 && client_entry && hostname) {
1266     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1267                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1268     internal->responder = FALSE;
1269     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1270                                       hostname, port, keyagr_completion, 
1271                                       internal);
1272     goto out;
1273   }
1274
1275  out:
1276   silc_free(nickname);
1277 }
1278
1279 /* Lists locally saved client and server public keys. */
1280
1281 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1282                              WI_ITEM_REC *item)
1283 {
1284
1285 }
1286
1287 void silc_channels_init(void)
1288 {
1289   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1290   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1291   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1292
1293   signal_add("silc event join", (SIGNAL_FUNC) event_join);
1294   signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
1295   signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
1296   signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
1297   signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
1298   signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
1299   signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
1300   signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
1301   signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
1302   signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
1303   signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
1304   signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
1305   signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
1306   
1307   command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1308   command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1309   command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1310   command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1311   command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1312   command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1313   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
1314
1315   silc_nicklist_init();
1316 }
1317
1318 void silc_channels_deinit(void)
1319 {
1320   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1321   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1322   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1323
1324   signal_remove("silc event join", (SIGNAL_FUNC) event_join);
1325   signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
1326   signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
1327   signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
1328   signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
1329   signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
1330   signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
1331   signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
1332   signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
1333   signal_remove("silc event channel_change", 
1334                 (SIGNAL_FUNC) event_channel_change);
1335   signal_remove("silc event server_signoff", 
1336                 (SIGNAL_FUNC) event_server_signoff);
1337   signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
1338   signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
1339   
1340   command_unbind("part", (SIGNAL_FUNC) command_part);
1341   command_unbind("me", (SIGNAL_FUNC) command_me);
1342   command_unbind("action", (SIGNAL_FUNC) command_action);
1343   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1344   command_unbind("away", (SIGNAL_FUNC) command_away);
1345   command_unbind("key", (SIGNAL_FUNC) command_key);
1346   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1347
1348   silc_nicklist_deinit();
1349 }