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