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