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