Fixed -C option to work on command line. Bug #16.
[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         silc_client_send_private_message_key(silc_client, conn, 
650                                              client_entry, TRUE);
651       } else if (type == 2) {
652         /* Set private channel key */
653         char *cipher = NULL, *hmac = NULL;
654
655         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
656           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
657                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
658                              channel_entry->channel_name);
659           goto out;
660         }
661
662         if (argc >= 6)
663           cipher = argv[5];
664         if (argc >= 7)
665           hmac = argv[6];
666
667         if (!silc_client_add_channel_private_key(silc_client, conn, 
668                                                  channel_entry, NULL,
669                                                  cipher, hmac,
670                                                  argv[4],
671                                                  argv_lens[4])) {
672           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
673                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
674                              channel_entry->channel_name);
675           goto out;
676         }
677
678         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
679                            SILCTXT_CH_PRIVATE_KEY_ADD, 
680                            channel_entry->channel_name);
681       }
682     }
683
684     goto out;
685   }
686   
687   /* Unset command */
688   if (!strcasecmp(argv[3], "unset")) {
689     command = 2;
690
691     if (type == 1 && client_entry) {
692       /* Unset private message key */
693       silc_client_del_private_message_key(silc_client, conn, client_entry);
694     } else if (type == 2) {
695       /* Unset channel key(s) */
696       SilcChannelPrivateKey *keys;
697       SilcUInt32 keys_count;
698       int number;
699
700       if (argc == 4)
701         silc_client_del_channel_private_keys(silc_client, conn, 
702                                              channel_entry);
703
704       if (argc > 4) {
705         number = atoi(argv[4]);
706         keys = silc_client_list_channel_private_keys(silc_client, conn, 
707                                                      channel_entry,
708                                                      &keys_count);
709         if (!keys)
710           goto out;
711
712         if (!number || number > keys_count) {
713           silc_client_free_channel_private_keys(keys, keys_count);
714           goto out;
715         }
716
717         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
718                                             keys[number - 1]);
719         silc_client_free_channel_private_keys(keys, keys_count);
720       }
721
722       goto out;
723     }
724   }
725
726   /* List command */
727   if (!strcasecmp(argv[3], "list")) {
728     command = 3;
729
730     if (type == 1) {
731       SilcPrivateMessageKeys keys;
732       SilcUInt32 keys_count;
733       int k, i, len;
734       char buf[1024];
735
736       keys = silc_client_list_private_message_keys(silc_client, conn, 
737                                                    &keys_count);
738       if (!keys)
739         goto out;
740
741       /* list the private message key(s) */
742       if (nickname[0] == '*') {
743         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
744                            SILCTXT_PRIVATE_KEY_LIST);
745         for (k = 0; k < keys_count; k++) {
746           memset(buf, 0, sizeof(buf));
747           strncat(buf, "  ", 2);
748           len = strlen(keys[k].client_entry->nickname);
749           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
750           if (len < 30)
751             for (i = 0; i < 30 - len; i++)
752               strcat(buf, " ");
753           strcat(buf, " ");
754           
755           len = strlen(keys[k].cipher);
756           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
757           if (len < 14)
758             for (i = 0; i < 14 - len; i++)
759               strcat(buf, " ");
760           strcat(buf, " ");
761
762           if (keys[k].key)
763             strcat(buf, "<hidden>");
764           else
765             strcat(buf, "*generated*");
766
767           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
768         }
769       } else {
770         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
771                            SILCTXT_PRIVATE_KEY_LIST_NICK,
772                            client_entry->nickname);
773         for (k = 0; k < keys_count; k++) {
774           if (keys[k].client_entry != client_entry)
775             continue;
776
777           memset(buf, 0, sizeof(buf));
778           strncat(buf, "  ", 2);
779           len = strlen(keys[k].client_entry->nickname);
780           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
781           if (len < 30)
782             for (i = 0; i < 30 - len; i++)
783               strcat(buf, " ");
784           strcat(buf, " ");
785           
786           len = strlen(keys[k].cipher);
787           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
788           if (len < 14)
789             for (i = 0; i < 14 - len; i++)
790               strcat(buf, " ");
791           strcat(buf, " ");
792
793           if (keys[k].key)
794             strcat(buf, "<hidden>");
795           else
796             strcat(buf, "*generated*");
797
798           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
799         }
800       }
801
802       silc_client_free_private_message_keys(keys, keys_count);
803
804     } else if (type == 2) {
805       SilcChannelPrivateKey *keys;
806       SilcUInt32 keys_count;
807       int k, i, len;
808       char buf[1024];
809
810       keys = silc_client_list_channel_private_keys(silc_client, conn, 
811                                                    channel_entry,
812                                                    &keys_count);
813
814       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
815                          SILCTXT_CH_PRIVATE_KEY_LIST,
816                          channel_entry->channel_name);
817
818       if (!keys)
819         goto out;
820       
821       for (k = 0; k < keys_count; k++) {
822         memset(buf, 0, sizeof(buf));
823         strncat(buf, "  ", 2);
824
825         len = strlen(keys[k]->cipher->cipher->name);
826         strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
827         if (len < 16)
828           for (i = 0; i < 16 - len; i++)
829             strcat(buf, " ");
830         strcat(buf, " ");
831         
832         len = strlen(silc_hmac_get_name(keys[k]->hmac));
833         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
834         if (len < 16)
835           for (i = 0; i < 16 - len; i++)
836             strcat(buf, " ");
837         strcat(buf, " ");
838         
839         strcat(buf, "<hidden>");
840
841         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
842       }
843       
844       silc_client_free_channel_private_keys(keys, keys_count);
845     }
846
847     goto out;
848   }
849
850   /* Send command is used to send key agreement */
851   if (!strcasecmp(argv[3], "agreement")) {
852     command = 4;
853
854     if (argc >= 5)
855       hostname = argv[4];
856     if (argc >= 6)
857       port = atoi(argv[5]);
858
859     internal = silc_calloc(1, sizeof(*internal));
860     internal->type = type;
861     internal->server = server;
862     
863     if (!hostname) {
864       if (settings_get_bool("use_auto_addr")) {
865        
866         hostname = (char *)settings_get_str("auto_public_ip");
867
868         /* If the hostname isn't set, treat this case as if auto_public_ip 
869            wasn't set. */
870         if ((hostname) && (*hostname == '\0')) {
871            hostname = NULL;
872         } else {
873           bindhost = (char *)settings_get_str("auto_bind_ip");
874             
875           /* if the bind_ip isn't set, but the public_ip IS, then assume then
876              public_ip is the same value as the bind_ip. */
877           if ((bindhost) && (*bindhost == '\0'))
878             bindhost = hostname;
879           port = settings_get_int("auto_bind_port");
880         }
881       }  /* if use_auto_addr */
882     }
883   }
884
885   /* Start command is used to start key agreement (after receiving the
886      key_agreement client operation). */
887   if (!strcasecmp(argv[3], "negotiate")) {
888     command = 5;
889
890     if (argc >= 5)
891       hostname = argv[4];
892     if (argc >= 6)
893       port = atoi(argv[5]);
894
895     internal = silc_calloc(1, sizeof(*internal));
896     internal->type = type;
897     internal->server = server;
898   }
899
900   /* Change current channel private key */
901   if (!strcasecmp(argv[3], "change")) {
902     command = 6;
903     if (type == 2) {
904       /* Unset channel key(s) */
905       SilcChannelPrivateKey *keys;
906       SilcUInt32 keys_count;
907       int number;
908
909       keys = silc_client_list_channel_private_keys(silc_client, conn, 
910                                                    channel_entry,
911                                                    &keys_count);
912       if (!keys)
913         goto out;
914
915       if (argc == 4) {
916         chanrec->cur_key++;
917         if (chanrec->cur_key >= keys_count)
918           chanrec->cur_key = 0;
919       }
920
921       if (argc > 4) {
922         number = atoi(argv[4]);
923         if (!number || number > keys_count)
924           chanrec->cur_key = 0;
925         else
926           chanrec->cur_key = number - 1;
927       }
928
929       /* Set the current channel private key */
930       silc_client_current_channel_private_key(silc_client, conn, 
931                                               channel_entry, 
932                                               keys[chanrec->cur_key]);
933       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
934                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
935                          channel_entry->channel_name);
936
937       silc_client_free_channel_private_keys(keys, keys_count);
938       goto out;
939     }
940   }
941
942   if (command == 0) {
943     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
944              "Usage: /KEY msg|channel <nickname|channel> "
945              "set|unset|agreement|negotiate [<arguments>]");
946     goto out;
947   }
948
949   if (command == 4 && client_entry) {
950     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
951                        SILCTXT_KEY_AGREEMENT, argv[2]);
952     internal->responder = TRUE;
953     silc_client_send_key_agreement(
954                            silc_client, conn, client_entry, hostname, 
955                            bindhost, port, 
956                            settings_get_int("key_exchange_timeout_secs"), 
957                            keyagr_completion, internal);
958     if (!hostname)
959       silc_free(internal);
960     goto out;
961   }
962
963   if (command == 5 && client_entry && hostname) {
964     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
965                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
966     internal->responder = FALSE;
967     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
968                                       hostname, port, keyagr_completion, 
969                                       internal);
970     goto out;
971   }
972
973  out:
974   silc_free(nickname);
975 }
976
977 /* Lists locally saved client and server public keys. */
978
979 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
980                              WI_ITEM_REC *item)
981 {
982
983 }
984
985 void silc_channels_init(void)
986 {
987   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
988   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
989   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
990   signal_add("mime", (SIGNAL_FUNC) sig_mime);
991
992   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
993   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
994   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
995   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
996   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
997   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
998   command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
999
1000   silc_nicklist_init();
1001 }
1002
1003 void silc_channels_deinit(void)
1004 {
1005   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1006   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1007   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1008   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1009
1010   command_unbind("part", (SIGNAL_FUNC) command_part);
1011   command_unbind("me", (SIGNAL_FUNC) command_me);
1012   command_unbind("action", (SIGNAL_FUNC) command_action);
1013   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1014   command_unbind("away", (SIGNAL_FUNC) command_away);
1015   command_unbind("key", (SIGNAL_FUNC) command_key);
1016   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1017
1018   silc_nicklist_deinit();
1019 }