Merged silc_1_0_branch to trunk.
[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 #include "silc-commands.h"
47
48 void sig_mime(SILC_SERVER_REC *server, SILC_CHANNEL_REC *channel,
49                const char *blob, const char *enc, const char *type,
50                const char *nick)
51 {
52
53   if (!(IS_SILC_SERVER(server)))
54     return;
55   
56   printformat_module("fe-common/silc", server, 
57                         channel == NULL ? NULL : channel->name,
58                         MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
59                         nick == NULL ? "[<unknown>]" : nick, type);
60
61 }
62
63 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
64                                       const char *name,
65                                       const char *visible_name,
66                                       int automatic)
67 {
68   SILC_CHANNEL_REC *rec;
69
70   g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
71   g_return_val_if_fail(name != NULL, NULL);
72
73   rec = g_new0(SILC_CHANNEL_REC, 1);
74   rec->chat_type = SILC_PROTOCOL;
75   channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
76                automatic);
77   return rec;
78 }
79
80 static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
81 {
82   if (!IS_SILC_CHANNEL(channel))
83     return;
84   if (channel->server && channel->server->disconnected)
85     return;
86
87   if (channel->server != NULL && !channel->left && !channel->kicked) {
88     /* destroying channel record without actually
89        having left the channel yet */
90     silc_command_exec(channel->server, "LEAVE", channel->name);
91   }
92 }
93
94 static void silc_channels_join(SILC_SERVER_REC *server,
95                                const char *channels, int automatic)
96 {
97   char **list, **tmp;
98   SILC_CHANNEL_REC *chanrec;
99
100   list = g_strsplit(channels, ",", -1);
101   for (tmp = list; *tmp != NULL; tmp++) {
102     chanrec = silc_channel_find(server, *tmp);
103     if (chanrec)
104       continue;
105
106     silc_command_exec(server, "JOIN", *tmp);
107   }
108
109   g_strfreev(list);
110 }
111
112 static void sig_connected(SILC_SERVER_REC *server)
113 {
114   if (IS_SILC_SERVER(server))
115     server->channels_join = (void *) silc_channels_join;
116 }
117
118 /* "server quit" signal from the core to indicate that QUIT command
119    was called. */
120
121 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
122 {
123   if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
124     silc_command_exec(server, "QUIT", msg);
125 }
126
127 static void sig_gui_quit(SILC_SERVER_REC *server, const char *msg)
128 {
129   silc_client_stop(silc_client);
130 }
131
132 /* Find Irssi channel entry by SILC channel entry */
133
134 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
135                                           SilcChannelEntry entry)
136 {
137   GSList *tmp;
138
139   g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
140
141   for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
142     SILC_CHANNEL_REC *rec = tmp->data;
143
144     if (rec->entry == entry)
145       return rec;
146   }
147
148   return NULL;
149 }
150
151 /* PART (LEAVE) command. */
152
153 static void command_part(const char *data, SILC_SERVER_REC *server,
154                          WI_ITEM_REC *item)
155 {
156   SILC_CHANNEL_REC *chanrec;
157   char userhost[256];
158   
159   CMD_SILC_SERVER(server);
160
161   if (!IS_SILC_SERVER(server) || !server->connected)
162     cmd_return_error(CMDERR_NOT_CONNECTED);
163
164   if (!strcmp(data, "*") || *data == '\0') {
165     if (!IS_SILC_CHANNEL(item))
166       cmd_return_error(CMDERR_NOT_JOINED);
167     data = item->visible_name;
168   }
169
170   chanrec = silc_channel_find(server, data);
171   if (chanrec == NULL) 
172     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
173
174   memset(userhost, 0, sizeof(userhost));
175   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
176            server->conn->local_entry->username, 
177            server->conn->local_entry->hostname);
178   signal_emit("message part", 5, server, chanrec->name,
179               server->nick, userhost, "");
180   
181   chanrec->left = TRUE;
182   silc_command_exec(server, "LEAVE", chanrec->name);
183   signal_stop();
184
185   channel_destroy(CHANNEL(chanrec));
186 }
187
188 /* ME local command. */
189
190 static void command_me(const char *data, SILC_SERVER_REC *server,
191                        WI_ITEM_REC *item)
192 {
193   SILC_CHANNEL_REC *chanrec;
194   char *tmpcmd = "ME", *tmp;
195   SilcUInt32 argc = 0;
196   unsigned char *message = NULL;
197   unsigned char **argv;
198   SilcUInt32 *argv_lens, *argv_types;
199   int i;
200  
201   CMD_SILC_SERVER(server);
202
203   if (!IS_SILC_SERVER(server) || !server->connected)
204     cmd_return_error(CMDERR_NOT_CONNECTED);
205
206   if (!IS_SILC_CHANNEL(item))
207     cmd_return_error(CMDERR_NOT_JOINED);
208
209   /* Now parse all arguments */
210   tmp = g_strconcat(tmpcmd, " ", data, NULL);
211   silc_parse_command_line(tmp, &argv, &argv_lens,
212                           &argv_types, &argc, 2);
213   g_free(tmp);
214
215   if (argc < 2)
216     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
217
218   chanrec = silc_channel_find(server, item->visible_name);
219   if (chanrec == NULL) 
220     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
221
222   if (!silc_term_utf8()) {
223     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
224                                     SILC_STRING_LANGUAGE);
225     message = silc_calloc(len + 1, sizeof(*message));
226     g_return_if_fail(message != NULL);
227     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
228                      message, len);
229   }
230
231   /* Send the action message */
232   silc_client_send_channel_message(silc_client, server->conn, 
233                                    chanrec->entry, NULL,
234                                    SILC_MESSAGE_FLAG_ACTION |
235                                    SILC_MESSAGE_FLAG_UTF8,
236                                    message ? message : argv[1],
237                                    message ? strlen(message) : argv_lens[1],
238                                    TRUE);
239
240   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
241                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
242                      server->conn->local_entry->nickname, argv[1]);
243
244   for (i = 0; i < argc; i++)
245     silc_free(argv[i]);
246   silc_free(argv_lens);
247   silc_free(argv_types);
248   silc_free(message);
249 }
250
251 /* ACTION local command. Same as ME but takes the channel as mandatory
252    argument. */
253
254 static void command_action(const char *data, SILC_SERVER_REC *server,
255                            WI_ITEM_REC *item)
256 {
257   SILC_CHANNEL_REC *chanrec;
258   char *tmpcmd = "ME", *tmp;
259   SilcUInt32 argc = 0;
260   unsigned char *message = NULL;
261   unsigned char **argv;
262   SilcUInt32 *argv_lens, *argv_types;
263   int i;
264  
265   CMD_SILC_SERVER(server);
266   if (!IS_SILC_SERVER(server) || !server->connected)
267     cmd_return_error(CMDERR_NOT_CONNECTED);
268
269   if (!IS_SILC_CHANNEL(item))
270     cmd_return_error(CMDERR_NOT_JOINED);
271
272   /* Now parse all arguments */
273   tmp = g_strconcat(tmpcmd, " ", data, NULL);
274   silc_parse_command_line(tmp, &argv, &argv_lens,
275                           &argv_types, &argc, 3);
276   g_free(tmp);
277
278   if (argc < 3)
279     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
280
281   chanrec = silc_channel_find(server, argv[1]);
282   if (chanrec == NULL) 
283     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
284
285   if (!silc_term_utf8()) {
286     int len = silc_utf8_encoded_len(argv[2], argv_lens[2],
287                                     SILC_STRING_LANGUAGE);
288     message = silc_calloc(len + 1, sizeof(*message));
289     g_return_if_fail(message != NULL);
290     silc_utf8_encode(argv[2], argv_lens[2], SILC_STRING_LANGUAGE,
291                      message, len);
292   }
293
294   /* Send the action message */
295   silc_client_send_channel_message(silc_client, server->conn, 
296                                    chanrec->entry, NULL,
297                                    SILC_MESSAGE_FLAG_ACTION |
298                                    SILC_MESSAGE_FLAG_UTF8,
299                                    message ? message : argv[2],
300                                    message ? strlen(message) : argv_lens[2],
301                                    TRUE);
302
303   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
304                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
305                      server->conn->local_entry->nickname, argv[2]);
306
307   for (i = 0; i < argc; i++)
308     silc_free(argv[i]);
309   silc_free(argv_lens);
310   silc_free(argv_types);
311   silc_free(message);
312 }
313
314 /* NOTICE local command. */
315
316 static void command_notice(const char *data, SILC_SERVER_REC *server,
317                            WI_ITEM_REC *item)
318 {
319   SILC_CHANNEL_REC *chanrec;
320   char *tmpcmd = "ME", *tmp;
321   SilcUInt32 argc = 0;
322   unsigned char *message = NULL;
323   unsigned char **argv;
324   SilcUInt32 *argv_lens, *argv_types;
325   int i;
326  
327   CMD_SILC_SERVER(server);
328   if (!IS_SILC_SERVER(server) || !server->connected)
329     cmd_return_error(CMDERR_NOT_CONNECTED);
330
331   if (!IS_SILC_CHANNEL(item))
332     cmd_return_error(CMDERR_NOT_JOINED);
333
334   /* Now parse all arguments */
335   tmp = g_strconcat(tmpcmd, " ", data, NULL);
336   silc_parse_command_line(tmp, &argv, &argv_lens,
337                           &argv_types, &argc, 2);
338   g_free(tmp);
339
340   if (argc < 2)
341     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
342
343   chanrec = silc_channel_find(server, item->visible_name);
344   if (chanrec == NULL) 
345     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
346
347   if (!silc_term_utf8()) {
348     int len = silc_utf8_encoded_len(argv[1], argv_lens[1],
349                                     SILC_STRING_LANGUAGE);
350     message = silc_calloc(len + 1, sizeof(*message));
351     g_return_if_fail(message != NULL);
352     silc_utf8_encode(argv[1], argv_lens[1], SILC_STRING_LANGUAGE,
353                      message, len);
354   }
355
356   /* Send the action message */
357   silc_client_send_channel_message(silc_client, server->conn, 
358                                    chanrec->entry, NULL,
359                                    SILC_MESSAGE_FLAG_NOTICE |
360                                    SILC_MESSAGE_FLAG_UTF8,
361                                    message ? message : argv[1],
362                                    message ? strlen(message) : argv_lens[1],
363                                    TRUE);
364
365   printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
366                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
367                      server->conn->local_entry->nickname, argv[1]);
368
369   for (i = 0; i < argc; i++)
370     silc_free(argv[i]);
371   silc_free(argv_lens);
372   silc_free(argv_types);
373   silc_free(message);
374 }
375
376 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
377    flag. */
378
379 bool silc_set_away(const char *reason, SILC_SERVER_REC *server)
380 {
381   bool set;
382   
383   if (!IS_SILC_SERVER(server) || !server->connected)
384     return FALSE;
385   
386   if (*reason == '\0') {
387     /* Remove any possible away message */
388     silc_client_set_away_message(silc_client, server->conn, NULL);
389     set = FALSE;
390
391     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
392                        SILCTXT_UNSET_AWAY);
393   } else {
394     /* Set the away message */
395     silc_client_set_away_message(silc_client, server->conn, (char *)reason);
396     set = TRUE;
397
398     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
399                        SILCTXT_SET_AWAY, reason);
400   }
401
402   server->usermode_away = set;
403   g_free_and_null(server->away_reason);
404   if (set)
405     server->away_reason = g_strdup((char *)reason);
406
407   signal_emit("away mode changed", 1, server);
408
409   return set;
410 }
411
412 static void command_away(const char *data, SILC_SERVER_REC *server,
413                          WI_ITEM_REC *item)
414 {
415   CMD_SILC_SERVER(server);
416
417   if (!IS_SILC_SERVER(server) || !server->connected)
418     cmd_return_error(CMDERR_NOT_CONNECTED);
419
420   g_free_and_null(server->away_reason);
421   if ((data) && (*data != '\0'))
422     server->away_reason = g_strdup(data);
423   
424   silc_command_exec(server, "UMODE", 
425                     (server->away_reason != NULL) ? "+g" : "-g");
426 }
427
428 typedef struct {
429   int type;                     /* 1 = msg, 2 = channel */
430   bool responder;
431   SILC_SERVER_REC *server;
432 } *KeyInternal;
433
434 /* Key agreement callback that is called after the key agreement protocol
435    has been performed. This is called also if error occured during the
436    key agreement protocol. The `key' is the allocated key material and
437    the caller is responsible of freeing it. The `key' is NULL if error
438    has occured. The application can freely use the `key' to whatever
439    purpose it needs. See lib/silcske/silcske.h for the definition of
440    the SilcSKEKeyMaterial structure. */
441
442 static void keyagr_completion(SilcClient client,
443                               SilcClientConnection conn,
444                               SilcClientEntry client_entry,
445                               SilcKeyAgreementStatus status,
446                               SilcSKEKeyMaterial *key,
447                               void *context)
448 {
449   KeyInternal i = (KeyInternal)context;
450
451   switch(status) {
452   case SILC_KEY_AGREEMENT_OK:
453     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
454                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
455
456     if (i->type == 1) {
457       /* Set the private key for this client */
458       silc_client_del_private_message_key(client, conn, client_entry);
459       silc_client_add_private_message_key_ske(client, conn, client_entry,
460                                               NULL, NULL, key, i->responder);
461       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
462                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
463                          client_entry->nickname);
464       silc_ske_free_key_material(key);
465     }
466     
467     break;
468     
469   case SILC_KEY_AGREEMENT_ERROR:
470     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
471                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
472     break;
473     
474   case SILC_KEY_AGREEMENT_FAILURE:
475     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
476                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
477     break;
478     
479   case SILC_KEY_AGREEMENT_TIMEOUT:
480     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
481                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
482     break;
483     
484   case SILC_KEY_AGREEMENT_ABORTED:
485     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
486                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
487     break;
488
489   case SILC_KEY_AGREEMENT_ALREADY_STARTED:
490     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
491                        SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
492                        client_entry->nickname);
493     break;
494     
495   case SILC_KEY_AGREEMENT_SELF_DENIED:
496     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
497                        SILCTXT_KEY_AGREEMENT_SELF_DENIED);
498     break;
499     
500   default:
501     break;
502   } 
503
504   if (i)
505     silc_free(i);
506 }
507
508 /* Local command KEY. This command is used to set and unset private
509    keys for channels, set and unset private keys for private messages
510    with remote clients and to send key agreement requests and
511    negotiate the key agreement protocol with remote client.  The
512    key agreement is supported only to negotiate private message keys,
513    it currently cannot be used to negotiate private keys for channels,
514    as it is not convenient for that purpose. */
515
516 typedef struct {
517   SILC_SERVER_REC *server;
518   char *data;
519   char *nick;
520   WI_ITEM_REC *item;
521 } *KeyGetClients;
522
523 /* Callback to be called after client information is resolved from the
524    server. */
525
526 static void silc_client_command_key_get_clients(SilcClient client,
527                                                 SilcClientConnection conn,
528                                                 SilcClientEntry *clients,
529                                                 SilcUInt32 clients_count,
530                                                 void *context)
531 {
532   KeyGetClients internal = (KeyGetClients)context;
533
534   if (!clients) {
535     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
536               internal->nick);
537     silc_free(internal->data);
538     silc_free(internal->nick);
539     silc_free(internal);
540     return;
541   }
542
543   signal_emit("command key", 3, internal->data, internal->server,
544               internal->item);
545
546   silc_free(internal->data);
547   silc_free(internal->nick);
548   silc_free(internal);
549 }
550
551 static void command_key(const char *data, SILC_SERVER_REC *server,
552                         WI_ITEM_REC *item)
553 {
554   SilcClientConnection conn;
555   SilcClientEntry *entrys, client_entry = NULL;
556   SilcUInt32 entry_count;
557   SILC_CHANNEL_REC *chanrec = NULL;
558   SilcChannelEntry channel_entry = NULL;
559   char *nickname = NULL, *tmp;
560   int command = 0, port = 0, type = 0;
561   char *hostname = NULL;
562   KeyInternal internal = NULL;
563   SilcUInt32 argc = 0;
564   unsigned char **argv;
565   SilcUInt32 *argv_lens, *argv_types;
566   char *bindhost = NULL;
567  
568   CMD_SILC_SERVER(server);
569
570   if (!server || !IS_SILC_SERVER(server) || !server->connected)
571     cmd_return_error(CMDERR_NOT_CONNECTED);
572
573   conn = server->conn;
574
575   /* Now parse all arguments */
576   tmp = g_strconcat("KEY", " ", data, NULL);
577   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
578   g_free(tmp);
579
580   if (argc < 4)
581     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
582
583   /* Get type */
584   if (!strcasecmp(argv[1], "msg"))
585     type = 1;
586   if (!strcasecmp(argv[1], "channel"))
587     type = 2;
588
589   if (type == 0)
590     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
591
592   if (type == 1) {
593     if (argv[2][0] == '*') {
594       nickname = strdup("*");
595     } else {
596       /* Parse the typed nickname. */
597       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
598         printformat_module("fe-common/silc", server, NULL,
599                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
600         return;
601       }
602       
603       /* Find client entry */
604       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
605                                              argv[2], &entry_count);
606       if (!entrys) {
607         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
608         inter->server = server;
609         inter->data = strdup(data);
610         inter->nick = strdup(nickname);
611         inter->item = item;
612         silc_client_get_clients(silc_client, conn, nickname, argv[2],
613                                 silc_client_command_key_get_clients, inter);
614         goto out;
615       }
616       client_entry = entrys[0];
617       silc_free(entrys);
618     }
619   }
620
621   if (type == 2) {
622     /* Get channel entry */
623     char *name;
624
625     if (argv[2][0] == '*') {
626       if (!conn->current_channel) {
627         silc_free(nickname);
628         cmd_return_error(CMDERR_NOT_JOINED);
629       }
630       name = conn->current_channel->channel_name;
631     } else {
632       name = argv[2];
633     }
634
635     chanrec = silc_channel_find(server, name);
636     if (chanrec == NULL) {
637       silc_free(nickname);
638       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
639     }
640     channel_entry = chanrec->entry;
641   }
642
643   /* Set command */
644   if (!strcasecmp(argv[3], "set")) {
645     command = 1;
646
647     if (argc >= 5) {
648       char *cipher = NULL, *hmac = NULL;
649
650       if (type == 1 && client_entry) {
651         /* Set private message key */
652         bool responder = FALSE;
653         
654         silc_client_del_private_message_key(silc_client, conn, client_entry);
655
656         if (argc >= 6) {
657           if (!strcasecmp(argv[5], "-responder"))
658             responder = TRUE;
659           else
660             cipher = argv[5];
661         }
662         if (argc >= 7) {
663           if (!strcasecmp(argv[6], "-responder"))
664             responder = TRUE;
665           else
666             hmac = argv[6];
667         }
668         if (argc >= 8) {
669           if (!strcasecmp(argv[7], "-responder"))
670             responder = TRUE;
671         }
672
673         silc_client_add_private_message_key(silc_client, conn, client_entry,
674                                             cipher, hmac,
675                                             argv[4], argv_lens[4],
676                                             (argv[4][0] == '*' ?
677                                              TRUE : FALSE), responder);
678
679         /* Send the key to the remote client so that it starts using it
680            too. */
681         /* XXX for now we don't do this.  This feature is pretty stupid
682            and should perhaps be removed altogether from SILC.
683         silc_client_send_private_message_key(silc_client, conn, 
684                                              client_entry, TRUE);
685         */
686       } else if (type == 2) {
687         /* Set private channel key */
688         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
689           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
690                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
691                              channel_entry->channel_name);
692           goto out;
693         }
694
695         if (argc >= 6)
696           cipher = argv[5];
697         if (argc >= 7)
698           hmac = argv[6];
699
700         if (!silc_client_add_channel_private_key(silc_client, conn, 
701                                                  channel_entry, NULL,
702                                                  cipher, hmac,
703                                                  argv[4],
704                                                  argv_lens[4])) {
705           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
706                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
707                              channel_entry->channel_name);
708           goto out;
709         }
710
711         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
712                            SILCTXT_CH_PRIVATE_KEY_ADD, 
713                            channel_entry->channel_name);
714       }
715     }
716
717     goto out;
718   }
719   
720   /* Unset command */
721   if (!strcasecmp(argv[3], "unset")) {
722     command = 2;
723
724     if (type == 1 && client_entry) {
725       /* Unset private message key */
726       silc_client_del_private_message_key(silc_client, conn, client_entry);
727     } else if (type == 2) {
728       /* Unset channel key(s) */
729       SilcChannelPrivateKey *keys;
730       SilcUInt32 keys_count;
731       int number;
732
733       if (argc == 4)
734         silc_client_del_channel_private_keys(silc_client, conn, 
735                                              channel_entry);
736
737       if (argc > 4) {
738         number = atoi(argv[4]);
739         keys = silc_client_list_channel_private_keys(silc_client, conn, 
740                                                      channel_entry,
741                                                      &keys_count);
742         if (!keys)
743           goto out;
744
745         if (!number || number > keys_count) {
746           silc_client_free_channel_private_keys(keys, keys_count);
747           goto out;
748         }
749
750         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
751                                             keys[number - 1]);
752         silc_client_free_channel_private_keys(keys, keys_count);
753       }
754
755       goto out;
756     }
757   }
758
759   /* List command */
760   if (!strcasecmp(argv[3], "list")) {
761     command = 3;
762
763     if (type == 1) {
764       SilcPrivateMessageKeys keys;
765       SilcUInt32 keys_count;
766       int k, i, len;
767       char buf[1024];
768
769       keys = silc_client_list_private_message_keys(silc_client, conn, 
770                                                    &keys_count);
771       if (!keys)
772         goto out;
773
774       /* list the private message key(s) */
775       if (nickname[0] == '*') {
776         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
777                            SILCTXT_PRIVATE_KEY_LIST);
778         for (k = 0; k < keys_count; k++) {
779           memset(buf, 0, sizeof(buf));
780           strncat(buf, "  ", 2);
781           len = strlen(keys[k].client_entry->nickname);
782           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
783           if (len < 30)
784             for (i = 0; i < 30 - len; i++)
785               strcat(buf, " ");
786           strcat(buf, " ");
787           
788           len = strlen(keys[k].cipher);
789           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
790           if (len < 14)
791             for (i = 0; i < 14 - len; i++)
792               strcat(buf, " ");
793           strcat(buf, " ");
794
795           if (keys[k].key)
796             strcat(buf, "<hidden>");
797           else
798             strcat(buf, "*generated*");
799
800           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
801         }
802       } else {
803         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
804                            SILCTXT_PRIVATE_KEY_LIST_NICK,
805                            client_entry->nickname);
806         for (k = 0; k < keys_count; k++) {
807           if (keys[k].client_entry != client_entry)
808             continue;
809
810           memset(buf, 0, sizeof(buf));
811           strncat(buf, "  ", 2);
812           len = strlen(keys[k].client_entry->nickname);
813           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
814           if (len < 30)
815             for (i = 0; i < 30 - len; i++)
816               strcat(buf, " ");
817           strcat(buf, " ");
818           
819           len = strlen(keys[k].cipher);
820           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
821           if (len < 14)
822             for (i = 0; i < 14 - len; i++)
823               strcat(buf, " ");
824           strcat(buf, " ");
825
826           if (keys[k].key)
827             strcat(buf, "<hidden>");
828           else
829             strcat(buf, "*generated*");
830
831           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
832         }
833       }
834
835       silc_client_free_private_message_keys(keys, keys_count);
836
837     } else if (type == 2) {
838       SilcChannelPrivateKey *keys;
839       SilcUInt32 keys_count;
840       int k, i, len;
841       char buf[1024];
842
843       keys = silc_client_list_channel_private_keys(silc_client, conn, 
844                                                    channel_entry,
845                                                    &keys_count);
846
847       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
848                          SILCTXT_CH_PRIVATE_KEY_LIST,
849                          channel_entry->channel_name);
850
851       if (!keys)
852         goto out;
853       
854       for (k = 0; k < keys_count; k++) {
855         memset(buf, 0, sizeof(buf));
856         strncat(buf, "  ", 2);
857
858         len = strlen(silc_cipher_get_name(keys[k]->cipher));
859         strncat(buf, silc_cipher_get_name(keys[k]->cipher),
860                 len > 16 ? 16 : len);
861         if (len < 16)
862           for (i = 0; i < 16 - len; i++)
863             strcat(buf, " ");
864         strcat(buf, " ");
865         
866         len = strlen(silc_hmac_get_name(keys[k]->hmac));
867         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
868         if (len < 16)
869           for (i = 0; i < 16 - len; i++)
870             strcat(buf, " ");
871         strcat(buf, " ");
872         
873         strcat(buf, "<hidden>");
874
875         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
876       }
877       
878       silc_client_free_channel_private_keys(keys, keys_count);
879     }
880
881     goto out;
882   }
883
884   /* Send command is used to send key agreement */
885   if (!strcasecmp(argv[3], "agreement")) {
886     command = 4;
887
888     if (argc >= 5)
889       hostname = argv[4];
890     if (argc >= 6)
891       port = atoi(argv[5]);
892
893     internal = silc_calloc(1, sizeof(*internal));
894     internal->type = type;
895     internal->server = server;
896     
897     if (!hostname) {
898       if (settings_get_bool("use_auto_addr")) {
899        
900         hostname = (char *)settings_get_str("auto_public_ip");
901
902         /* If the hostname isn't set, treat this case as if auto_public_ip 
903            wasn't set. */
904         if ((hostname) && (*hostname == '\0')) {
905            hostname = NULL;
906         } else {
907           bindhost = (char *)settings_get_str("auto_bind_ip");
908             
909           /* if the bind_ip isn't set, but the public_ip IS, then assume then
910              public_ip is the same value as the bind_ip. */
911           if ((bindhost) && (*bindhost == '\0'))
912             bindhost = hostname;
913           port = settings_get_int("auto_bind_port");
914         }
915       }  /* if use_auto_addr */
916     }
917   }
918
919   /* Start command is used to start key agreement (after receiving the
920      key_agreement client operation). */
921   if (!strcasecmp(argv[3], "negotiate")) {
922     command = 5;
923
924     if (argc >= 5)
925       hostname = argv[4];
926     if (argc >= 6)
927       port = atoi(argv[5]);
928
929     internal = silc_calloc(1, sizeof(*internal));
930     internal->type = type;
931     internal->server = server;
932   }
933
934   /* Change current channel private key */
935   if (!strcasecmp(argv[3], "change")) {
936     command = 6;
937     if (type == 2) {
938       /* Unset channel key(s) */
939       SilcChannelPrivateKey *keys;
940       SilcUInt32 keys_count;
941       int number;
942
943       keys = silc_client_list_channel_private_keys(silc_client, conn, 
944                                                    channel_entry,
945                                                    &keys_count);
946       if (!keys)
947         goto out;
948
949       if (argc == 4) {
950         chanrec->cur_key++;
951         if (chanrec->cur_key >= keys_count)
952           chanrec->cur_key = 0;
953       }
954
955       if (argc > 4) {
956         number = atoi(argv[4]);
957         if (!number || number > keys_count)
958           chanrec->cur_key = 0;
959         else
960           chanrec->cur_key = number - 1;
961       }
962
963       /* Set the current channel private key */
964       silc_client_current_channel_private_key(silc_client, conn, 
965                                               channel_entry, 
966                                               keys[chanrec->cur_key]);
967       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
968                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
969                          channel_entry->channel_name);
970
971       silc_client_free_channel_private_keys(keys, keys_count);
972       goto out;
973     }
974   }
975
976   if (command == 0) {
977     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
978              "Usage: /KEY msg|channel <nickname|channel> "
979              "set|unset|agreement|negotiate [<arguments>]");
980     goto out;
981   }
982
983   if (command == 4 && client_entry) {
984     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
985                        SILCTXT_KEY_AGREEMENT, argv[2]);
986     internal->responder = TRUE;
987     silc_client_send_key_agreement(
988                            silc_client, conn, client_entry, hostname, 
989                            bindhost, port, 
990                            settings_get_int("key_exchange_timeout_secs"), 
991                            keyagr_completion, internal);
992     if (!hostname)
993       silc_free(internal);
994     goto out;
995   }
996
997   if (command == 5 && client_entry && hostname) {
998     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
999                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1000     internal->responder = FALSE;
1001     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1002                                       hostname, port, keyagr_completion, 
1003                                       internal);
1004     goto out;
1005   }
1006
1007  out:
1008   silc_free(nickname);
1009 }
1010
1011 /* Lists locally saved client and server public keys. */
1012
1013 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1014                              WI_ITEM_REC *item)
1015 {
1016
1017 }
1018
1019 void silc_channels_init(void)
1020 {
1021   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1022   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1023   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1024   signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1025   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1026
1027   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1028   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1029   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1030   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1031   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1032   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1033 /*  command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys); */
1034
1035   silc_nicklist_init();
1036 }
1037
1038 void silc_channels_deinit(void)
1039 {
1040   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1041   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1042   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1043   signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1044   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1045
1046   command_unbind("part", (SIGNAL_FUNC) command_part);
1047   command_unbind("me", (SIGNAL_FUNC) command_me);
1048   command_unbind("action", (SIGNAL_FUNC) command_action);
1049   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1050   command_unbind("away", (SIGNAL_FUNC) command_away);
1051   command_unbind("key", (SIGNAL_FUNC) command_key);
1052 /*  command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys); */
1053
1054   silc_nicklist_deinit();
1055 }