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