Tue Jan 04 02:43:44 CET 2004 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
204 /* ACTION local command. */
205
206 static void command_action(const char *data, SILC_SERVER_REC *server,
207                            WI_ITEM_REC *item)
208 {
209   GHashTable *optlist;
210   char *target, *msg;
211   char *message = NULL;
212   int target_type;
213   void *free_arg;
214
215   CMD_SILC_SERVER(server);
216   if (!IS_SILC_SERVER(server) || !server->connected)
217     cmd_return_error(CMDERR_NOT_CONNECTED);
218
219   if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
220     cmd_return_error(CMDERR_NOT_JOINED);
221
222   /* Now parse all arguments */
223   if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | 
224                       PARAM_FLAG_GETREST, 
225                       "action", &optlist, &target, &msg))
226     return;
227
228   if (*target == '\0' || *msg == '\0')
229     cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
230
231   if (strcmp(target, "*") == 0) {
232     /* send to active channel/query */
233     if (item == NULL)
234       cmd_param_error(CMDERR_NOT_JOINED);
235
236     target_type = IS_SILC_CHANNEL(item) ? 
237             SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
238     target = (char *)window_item_get_target(item);
239   } else if (g_hash_table_lookup(optlist, "channel") != NULL)
240     target_type = SEND_TARGET_CHANNEL;
241   else {
242     target_type = SEND_TARGET_NICK;
243   }
244
245   if (!silc_term_utf8()) {
246     int len = silc_utf8_encoded_len(msg, strlen(msg),
247                                     SILC_STRING_LANGUAGE);
248     message = silc_calloc(len + 1, sizeof(*message));
249     g_return_if_fail(message != NULL);
250     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LANGUAGE,
251                      message, len);
252   }
253
254   if (target != NULL) {
255     if (target_type == SEND_TARGET_CHANNEL) {
256       if (silc_send_channel(server, target, (message != NULL ? message : msg),
257                             SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
258                             (g_hash_table_lookup(optlist, "sign") != NULL ?
259                              SILC_MESSAGE_FLAG_SIGNED : 0))) {
260         if (g_hash_table_lookup(optlist, "sign"))
261           signal_emit("message silc signed_own_action", 3, server, msg, target);
262         else
263           signal_emit("message silc own_action", 3, server, msg, target);
264       }
265     } else {
266       if (silc_send_msg(server, target, (message != NULL ? message : msg),
267                         (message != NULL ? strlen(message) : strlen(msg)),
268                         SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
269                         (g_hash_table_lookup(optlist, "sign") != NULL ?
270                          SILC_MESSAGE_FLAG_SIGNED : 0))) {
271         if (g_hash_table_lookup(optlist, "sign"))
272           signal_emit("message silc signed_own_private_action", 3, 
273                           server, msg, target);
274         else
275           signal_emit("message silc own_private_action", 3,
276                           server, msg, target);
277       }
278     }
279   }
280
281   cmd_params_free(free_arg);
282   silc_free(message);
283 }
284
285 /* ME local command. */
286
287 static void command_me(const char *data, SILC_SERVER_REC *server,
288                        WI_ITEM_REC *item)
289 {
290   char *tmpcmd;
291
292   CMD_SILC_SERVER(server);
293   if (!IS_SILC_SERVER(server) || !server->connected)
294     cmd_return_error(CMDERR_NOT_CONNECTED);
295
296   if (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item))
297     cmd_return_error(CMDERR_NOT_JOINED);
298
299   if (IS_SILC_CHANNEL(item)) 
300     tmpcmd = g_strdup_printf("-channel %s %s", item->visible_name, data);
301   else
302     tmpcmd = g_strdup_printf("%s %s", item->visible_name, data);
303
304   command_action(tmpcmd, server, item);
305   g_free(tmpcmd);
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   GHashTable *optlist;
314   char *target, *msg;
315   char *message = NULL;
316   int target_type;
317   void *free_arg;
318
319   CMD_SILC_SERVER(server);
320   if (!IS_SILC_SERVER(server) || !server->connected)
321     cmd_return_error(CMDERR_NOT_CONNECTED);
322
323   if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
324     cmd_return_error(CMDERR_NOT_JOINED);
325
326   /* Now parse all arguments */
327   if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | 
328                       PARAM_FLAG_GETREST, 
329                       "notice", &optlist, &target, &msg))
330     return;
331
332   if (*target == '\0' || *msg == '\0')
333     cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
334
335   if (strcmp(target, "*") == 0) {
336     /* send to active channel/query */
337     if (item == NULL)
338       cmd_param_error(CMDERR_NOT_JOINED);
339
340     target_type = IS_SILC_CHANNEL(item) ? 
341             SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
342     target = (char *)window_item_get_target(item);
343   } else if (g_hash_table_lookup(optlist, "channel") != NULL)
344     target_type = SEND_TARGET_CHANNEL;
345   else {
346     target_type = SEND_TARGET_NICK;
347   }
348
349   if (!silc_term_utf8()) {
350     int len = silc_utf8_encoded_len(msg, strlen(msg),
351                                     SILC_STRING_LANGUAGE);
352     message = silc_calloc(len + 1, sizeof(*message));
353     g_return_if_fail(message != NULL);
354     silc_utf8_encode(msg, strlen(msg), SILC_STRING_LANGUAGE,
355                      message, len);
356   }
357
358   if (target != NULL) {
359     if (target_type == SEND_TARGET_CHANNEL) {
360       if (silc_send_channel(server, target, (message != NULL ? message : msg),
361                             SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
362                             (g_hash_table_lookup(optlist, "sign") != NULL ?
363                              SILC_MESSAGE_FLAG_SIGNED : 0))) {
364         if (g_hash_table_lookup(optlist, "sign"))
365           signal_emit("message silc signed_own_notice", 3, server, msg, target);
366         else
367           signal_emit("message silc own_notice", 3, server, msg, target);
368       }
369     } else {
370       if (silc_send_msg(server, target, (message != NULL ? message : msg),
371                         (message != NULL ? strlen(message) : strlen(msg)),
372                         SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
373                         (g_hash_table_lookup(optlist, "sign") != NULL ?
374                          SILC_MESSAGE_FLAG_SIGNED : 0))) {
375         if (g_hash_table_lookup(optlist, "sign"))
376           signal_emit("message silc signed_own_private_notice", 3, 
377                           server, msg, target);
378         else
379           signal_emit("message silc own_private_notice", 3,
380                           server, msg, target);
381       }
382     }
383   }
384
385   cmd_params_free(free_arg);
386   silc_free(message);
387 }
388
389 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
390    flag. */
391
392 bool silc_set_away(const char *reason, SILC_SERVER_REC *server)
393 {
394   bool set;
395   
396   if (!IS_SILC_SERVER(server) || !server->connected)
397     return FALSE;
398   
399   if (*reason == '\0') {
400     /* Remove any possible away message */
401     silc_client_set_away_message(silc_client, server->conn, NULL);
402     set = FALSE;
403
404     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
405                        SILCTXT_UNSET_AWAY);
406   } else {
407     /* Set the away message */
408     silc_client_set_away_message(silc_client, server->conn, (char *)reason);
409     set = TRUE;
410
411     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
412                        SILCTXT_SET_AWAY, reason);
413   }
414
415   server->usermode_away = set;
416   g_free_and_null(server->away_reason);
417   if (set)
418     server->away_reason = g_strdup((char *)reason);
419
420   signal_emit("away mode changed", 1, server);
421
422   return set;
423 }
424
425 static void command_away(const char *data, SILC_SERVER_REC *server,
426                          WI_ITEM_REC *item)
427 {
428   CMD_SILC_SERVER(server);
429
430   if (!IS_SILC_SERVER(server) || !server->connected)
431     cmd_return_error(CMDERR_NOT_CONNECTED);
432
433   g_free_and_null(server->away_reason);
434   if ((data) && (*data != '\0'))
435     server->away_reason = g_strdup(data);
436   
437   silc_command_exec(server, "UMODE", 
438                     (server->away_reason != NULL) ? "+g" : "-g");
439 }
440
441 typedef struct {
442   int type;                     /* 1 = msg, 2 = channel */
443   bool responder;
444   SILC_SERVER_REC *server;
445 } *KeyInternal;
446
447 /* Key agreement callback that is called after the key agreement protocol
448    has been performed. This is called also if error occured during the
449    key agreement protocol. The `key' is the allocated key material and
450    the caller is responsible of freeing it. The `key' is NULL if error
451    has occured. The application can freely use the `key' to whatever
452    purpose it needs. See lib/silcske/silcske.h for the definition of
453    the SilcSKEKeyMaterial structure. */
454
455 static void keyagr_completion(SilcClient client,
456                               SilcClientConnection conn,
457                               SilcClientEntry client_entry,
458                               SilcKeyAgreementStatus status,
459                               SilcSKEKeyMaterial *key,
460                               void *context)
461 {
462   KeyInternal i = (KeyInternal)context;
463
464   switch(status) {
465   case SILC_KEY_AGREEMENT_OK:
466     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
467                        SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
468
469     if (i->type == 1) {
470       /* Set the private key for this client */
471       silc_client_del_private_message_key(client, conn, client_entry);
472       silc_client_add_private_message_key_ske(client, conn, client_entry,
473                                               NULL, NULL, key, i->responder);
474       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
475                          SILCTXT_KEY_AGREEMENT_PRIVMSG, 
476                          client_entry->nickname);
477       silc_ske_free_key_material(key);
478     }
479     
480     break;
481     
482   case SILC_KEY_AGREEMENT_ERROR:
483     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
484                        SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
485     break;
486     
487   case SILC_KEY_AGREEMENT_FAILURE:
488     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
489                        SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
490     break;
491     
492   case SILC_KEY_AGREEMENT_TIMEOUT:
493     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
494                        SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
495     break;
496     
497   case SILC_KEY_AGREEMENT_ABORTED:
498     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
499                        SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
500     break;
501
502   case SILC_KEY_AGREEMENT_ALREADY_STARTED:
503     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
504                        SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
505                        client_entry->nickname);
506     break;
507     
508   case SILC_KEY_AGREEMENT_SELF_DENIED:
509     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
510                        SILCTXT_KEY_AGREEMENT_SELF_DENIED);
511     break;
512     
513   default:
514     break;
515   } 
516
517   if (i)
518     silc_free(i);
519 }
520
521 /* Local command KEY. This command is used to set and unset private
522    keys for channels, set and unset private keys for private messages
523    with remote clients and to send key agreement requests and
524    negotiate the key agreement protocol with remote client.  The
525    key agreement is supported only to negotiate private message keys,
526    it currently cannot be used to negotiate private keys for channels,
527    as it is not convenient for that purpose. */
528
529 typedef struct {
530   SILC_SERVER_REC *server;
531   char *data;
532   char *nick;
533   WI_ITEM_REC *item;
534 } *KeyGetClients;
535
536 /* Callback to be called after client information is resolved from the
537    server. */
538
539 static void silc_client_command_key_get_clients(SilcClient client,
540                                                 SilcClientConnection conn,
541                                                 SilcClientEntry *clients,
542                                                 SilcUInt32 clients_count,
543                                                 void *context)
544 {
545   KeyGetClients internal = (KeyGetClients)context;
546
547   if (!clients) {
548     printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
549               internal->nick);
550     silc_free(internal->data);
551     silc_free(internal->nick);
552     silc_free(internal);
553     return;
554   }
555
556   signal_emit("command key", 3, internal->data, internal->server,
557               internal->item);
558
559   silc_free(internal->data);
560   silc_free(internal->nick);
561   silc_free(internal);
562 }
563
564 static void command_key(const char *data, SILC_SERVER_REC *server,
565                         WI_ITEM_REC *item)
566 {
567   SilcClientConnection conn;
568   SilcClientEntry *entrys, client_entry = NULL;
569   SilcUInt32 entry_count;
570   SILC_CHANNEL_REC *chanrec = NULL;
571   SilcChannelEntry channel_entry = NULL;
572   char *nickname = NULL, *tmp;
573   int command = 0, port = 0, type = 0;
574   char *hostname = NULL;
575   KeyInternal internal = NULL;
576   SilcUInt32 argc = 0;
577   unsigned char **argv;
578   SilcUInt32 *argv_lens, *argv_types;
579   char *bindhost = NULL;
580  
581   CMD_SILC_SERVER(server);
582
583   if (!server || !IS_SILC_SERVER(server) || !server->connected)
584     cmd_return_error(CMDERR_NOT_CONNECTED);
585
586   conn = server->conn;
587
588   /* Now parse all arguments */
589   tmp = g_strconcat("KEY", " ", data, NULL);
590   silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
591   g_free(tmp);
592
593   if (argc < 4)
594     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
595
596   /* Get type */
597   if (!strcasecmp(argv[1], "msg"))
598     type = 1;
599   if (!strcasecmp(argv[1], "channel"))
600     type = 2;
601
602   if (type == 0)
603     cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
604
605   if (type == 1) {
606     if (argv[2][0] == '*') {
607       nickname = strdup("*");
608     } else {
609       /* Parse the typed nickname. */
610       if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
611         printformat_module("fe-common/silc", server, NULL,
612                            MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
613         return;
614       }
615       
616       /* Find client entry */
617       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
618                                              argv[2], &entry_count);
619       if (!entrys) {
620         KeyGetClients inter = silc_calloc(1, sizeof(*inter));
621         inter->server = server;
622         inter->data = strdup(data);
623         inter->nick = strdup(nickname);
624         inter->item = item;
625         silc_client_get_clients(silc_client, conn, nickname, argv[2],
626                                 silc_client_command_key_get_clients, inter);
627         goto out;
628       }
629       client_entry = entrys[0];
630       silc_free(entrys);
631     }
632   }
633
634   if (type == 2) {
635     /* Get channel entry */
636     char *name;
637
638     if (argv[2][0] == '*') {
639       if (!conn->current_channel) {
640         silc_free(nickname);
641         cmd_return_error(CMDERR_NOT_JOINED);
642       }
643       name = conn->current_channel->channel_name;
644     } else {
645       name = argv[2];
646     }
647
648     chanrec = silc_channel_find(server, name);
649     if (chanrec == NULL) {
650       silc_free(nickname);
651       cmd_return_error(CMDERR_CHAN_NOT_FOUND);
652     }
653     channel_entry = chanrec->entry;
654   }
655
656   /* Set command */
657   if (!strcasecmp(argv[3], "set")) {
658     command = 1;
659
660     if (argc >= 5) {
661       char *cipher = NULL, *hmac = NULL;
662
663       if (type == 1 && client_entry) {
664         /* Set private message key */
665         bool responder = FALSE;
666         
667         silc_client_del_private_message_key(silc_client, conn, client_entry);
668
669         if (argc >= 6) {
670           if (!strcasecmp(argv[5], "-responder"))
671             responder = TRUE;
672           else
673             cipher = argv[5];
674         }
675         if (argc >= 7) {
676           if (!strcasecmp(argv[6], "-responder"))
677             responder = TRUE;
678           else
679             hmac = argv[6];
680         }
681         if (argc >= 8) {
682           if (!strcasecmp(argv[7], "-responder"))
683             responder = TRUE;
684         }
685
686         silc_client_add_private_message_key(silc_client, conn, client_entry,
687                                             cipher, hmac,
688                                             argv[4], argv_lens[4],
689                                             (argv[4][0] == '*' ?
690                                              TRUE : FALSE), responder);
691
692         /* Send the key to the remote client so that it starts using it
693            too. */
694         /* XXX for now we don't do this.  This feature is pretty stupid
695            and should perhaps be removed altogether from SILC.
696         silc_client_send_private_message_key(silc_client, conn, 
697                                              client_entry, TRUE);
698         */
699       } else if (type == 2) {
700         /* Set private channel key */
701         if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
702           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
703                              SILCTXT_CH_PRIVATE_KEY_NOMODE, 
704                              channel_entry->channel_name);
705           goto out;
706         }
707
708         if (argc >= 6)
709           cipher = argv[5];
710         if (argc >= 7)
711           hmac = argv[6];
712
713         if (!silc_client_add_channel_private_key(silc_client, conn, 
714                                                  channel_entry, NULL,
715                                                  cipher, hmac,
716                                                  argv[4],
717                                                  argv_lens[4])) {
718           printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
719                              SILCTXT_CH_PRIVATE_KEY_ERROR, 
720                              channel_entry->channel_name);
721           goto out;
722         }
723
724         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
725                            SILCTXT_CH_PRIVATE_KEY_ADD, 
726                            channel_entry->channel_name);
727       }
728     }
729
730     goto out;
731   }
732   
733   /* Unset command */
734   if (!strcasecmp(argv[3], "unset")) {
735     command = 2;
736
737     if (type == 1 && client_entry) {
738       /* Unset private message key */
739       silc_client_del_private_message_key(silc_client, conn, client_entry);
740     } else if (type == 2) {
741       /* Unset channel key(s) */
742       SilcChannelPrivateKey *keys;
743       SilcUInt32 keys_count;
744       int number;
745
746       if (argc == 4)
747         silc_client_del_channel_private_keys(silc_client, conn, 
748                                              channel_entry);
749
750       if (argc > 4) {
751         number = atoi(argv[4]);
752         keys = silc_client_list_channel_private_keys(silc_client, conn, 
753                                                      channel_entry,
754                                                      &keys_count);
755         if (!keys)
756           goto out;
757
758         if (!number || number > keys_count) {
759           silc_client_free_channel_private_keys(keys, keys_count);
760           goto out;
761         }
762
763         silc_client_del_channel_private_key(silc_client, conn, channel_entry,
764                                             keys[number - 1]);
765         silc_client_free_channel_private_keys(keys, keys_count);
766       }
767
768       goto out;
769     }
770   }
771
772   /* List command */
773   if (!strcasecmp(argv[3], "list")) {
774     command = 3;
775
776     if (type == 1) {
777       SilcPrivateMessageKeys keys;
778       SilcUInt32 keys_count;
779       int k, i, len;
780       char buf[1024];
781
782       keys = silc_client_list_private_message_keys(silc_client, conn, 
783                                                    &keys_count);
784       if (!keys)
785         goto out;
786
787       /* list the private message key(s) */
788       if (nickname[0] == '*') {
789         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
790                            SILCTXT_PRIVATE_KEY_LIST);
791         for (k = 0; k < keys_count; k++) {
792           memset(buf, 0, sizeof(buf));
793           strncat(buf, "  ", 2);
794           len = strlen(keys[k].client_entry->nickname);
795           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
796           if (len < 30)
797             for (i = 0; i < 30 - len; i++)
798               strcat(buf, " ");
799           strcat(buf, " ");
800           
801           len = strlen(keys[k].cipher);
802           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
803           if (len < 14)
804             for (i = 0; i < 14 - len; i++)
805               strcat(buf, " ");
806           strcat(buf, " ");
807
808           if (keys[k].key)
809             strcat(buf, "<hidden>");
810           else
811             strcat(buf, "*generated*");
812
813           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
814         }
815       } else {
816         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
817                            SILCTXT_PRIVATE_KEY_LIST_NICK,
818                            client_entry->nickname);
819         for (k = 0; k < keys_count; k++) {
820           if (keys[k].client_entry != client_entry)
821             continue;
822
823           memset(buf, 0, sizeof(buf));
824           strncat(buf, "  ", 2);
825           len = strlen(keys[k].client_entry->nickname);
826           strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
827           if (len < 30)
828             for (i = 0; i < 30 - len; i++)
829               strcat(buf, " ");
830           strcat(buf, " ");
831           
832           len = strlen(keys[k].cipher);
833           strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
834           if (len < 14)
835             for (i = 0; i < 14 - len; i++)
836               strcat(buf, " ");
837           strcat(buf, " ");
838
839           if (keys[k].key)
840             strcat(buf, "<hidden>");
841           else
842             strcat(buf, "*generated*");
843
844           silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
845         }
846       }
847
848       silc_client_free_private_message_keys(keys, keys_count);
849
850     } else if (type == 2) {
851       SilcChannelPrivateKey *keys;
852       SilcUInt32 keys_count;
853       int k, i, len;
854       char buf[1024];
855
856       keys = silc_client_list_channel_private_keys(silc_client, conn, 
857                                                    channel_entry,
858                                                    &keys_count);
859
860       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
861                          SILCTXT_CH_PRIVATE_KEY_LIST,
862                          channel_entry->channel_name);
863
864       if (!keys)
865         goto out;
866       
867       for (k = 0; k < keys_count; k++) {
868         memset(buf, 0, sizeof(buf));
869         strncat(buf, "  ", 2);
870
871         len = strlen(silc_cipher_get_name(keys[k]->cipher));
872         strncat(buf, silc_cipher_get_name(keys[k]->cipher),
873                 len > 16 ? 16 : len);
874         if (len < 16)
875           for (i = 0; i < 16 - len; i++)
876             strcat(buf, " ");
877         strcat(buf, " ");
878         
879         len = strlen(silc_hmac_get_name(keys[k]->hmac));
880         strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
881         if (len < 16)
882           for (i = 0; i < 16 - len; i++)
883             strcat(buf, " ");
884         strcat(buf, " ");
885         
886         strcat(buf, "<hidden>");
887
888         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
889       }
890       
891       silc_client_free_channel_private_keys(keys, keys_count);
892     }
893
894     goto out;
895   }
896
897   /* Send command is used to send key agreement */
898   if (!strcasecmp(argv[3], "agreement")) {
899     command = 4;
900
901     if (argc >= 5)
902       hostname = argv[4];
903     if (argc >= 6)
904       port = atoi(argv[5]);
905
906     internal = silc_calloc(1, sizeof(*internal));
907     internal->type = type;
908     internal->server = server;
909     
910     if (!hostname) {
911       if (settings_get_bool("use_auto_addr")) {
912        
913         hostname = (char *)settings_get_str("auto_public_ip");
914
915         /* If the hostname isn't set, treat this case as if auto_public_ip 
916            wasn't set. */
917         if ((hostname) && (*hostname == '\0')) {
918            hostname = NULL;
919         } else {
920           bindhost = (char *)settings_get_str("auto_bind_ip");
921             
922           /* if the bind_ip isn't set, but the public_ip IS, then assume then
923              public_ip is the same value as the bind_ip. */
924           if ((bindhost) && (*bindhost == '\0'))
925             bindhost = hostname;
926           port = settings_get_int("auto_bind_port");
927         }
928       }  /* if use_auto_addr */
929     }
930   }
931
932   /* Start command is used to start key agreement (after receiving the
933      key_agreement client operation). */
934   if (!strcasecmp(argv[3], "negotiate")) {
935     command = 5;
936
937     if (argc >= 5)
938       hostname = argv[4];
939     if (argc >= 6)
940       port = atoi(argv[5]);
941
942     internal = silc_calloc(1, sizeof(*internal));
943     internal->type = type;
944     internal->server = server;
945   }
946
947   /* Change current channel private key */
948   if (!strcasecmp(argv[3], "change")) {
949     command = 6;
950     if (type == 2) {
951       /* Unset channel key(s) */
952       SilcChannelPrivateKey *keys;
953       SilcUInt32 keys_count;
954       int number;
955
956       keys = silc_client_list_channel_private_keys(silc_client, conn, 
957                                                    channel_entry,
958                                                    &keys_count);
959       if (!keys)
960         goto out;
961
962       if (argc == 4) {
963         chanrec->cur_key++;
964         if (chanrec->cur_key >= keys_count)
965           chanrec->cur_key = 0;
966       }
967
968       if (argc > 4) {
969         number = atoi(argv[4]);
970         if (!number || number > keys_count)
971           chanrec->cur_key = 0;
972         else
973           chanrec->cur_key = number - 1;
974       }
975
976       /* Set the current channel private key */
977       silc_client_current_channel_private_key(silc_client, conn, 
978                                               channel_entry, 
979                                               keys[chanrec->cur_key]);
980       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
981                          SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
982                          channel_entry->channel_name);
983
984       silc_client_free_channel_private_keys(keys, keys_count);
985       goto out;
986     }
987   }
988
989   if (command == 0) {
990     silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
991              "Usage: /KEY msg|channel <nickname|channel> "
992              "set|unset|agreement|negotiate [<arguments>]");
993     goto out;
994   }
995
996   if (command == 4 && client_entry) {
997     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
998                        SILCTXT_KEY_AGREEMENT, argv[2]);
999     internal->responder = TRUE;
1000     silc_client_send_key_agreement(
1001                            silc_client, conn, client_entry, hostname, 
1002                            bindhost, port, 
1003                            settings_get_int("key_exchange_timeout_secs"), 
1004                            keyagr_completion, internal);
1005     if (!hostname)
1006       silc_free(internal);
1007     goto out;
1008   }
1009
1010   if (command == 5 && client_entry && hostname) {
1011     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
1012                        SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
1013     internal->responder = FALSE;
1014     silc_client_perform_key_agreement(silc_client, conn, client_entry, 
1015                                       hostname, port, keyagr_completion, 
1016                                       internal);
1017     goto out;
1018   }
1019
1020  out:
1021   silc_free(nickname);
1022 }
1023
1024 void silc_list_key(const char *pub_filename, int verbose)
1025 {
1026   SilcPublicKey public_key;
1027   SilcPublicKeyIdentifier ident;
1028   char *fingerprint, *babbleprint;
1029   unsigned char *pk;
1030   SilcUInt32 pk_len;
1031   SilcPKCS pkcs;
1032   SilcUInt32 key_len = 0;
1033   int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
1034
1035   if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1036                                 SILC_PKCS_FILE_PEM) == FALSE)
1037     if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
1038                                   SILC_PKCS_FILE_BIN) == FALSE) {
1039       printformat_module("fe-common/silc", NULL, NULL,
1040                          MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
1041                          pub_filename);
1042       return;
1043     }
1044
1045   ident = silc_pkcs_decode_identifier(public_key->identifier);
1046      
1047   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
1048   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1049   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1050   
1051   if (silc_pkcs_alloc(public_key->name, &pkcs)) {
1052     key_len = silc_pkcs_public_key_set(pkcs, public_key);
1053     silc_pkcs_free(pkcs);
1054   }
1055
1056   printformat_module("fe-common/silc", NULL, NULL,
1057                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
1058                      pub_filename);
1059
1060   if (verbose)
1061     printformat_module("fe-common/silc", NULL, NULL,
1062                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
1063                        public_key->name);
1064   if (key_len && verbose)
1065     printformat_module("fe-common/silc", NULL, NULL,
1066                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
1067                         (unsigned int)key_len);
1068   if (ident->realname && (!is_server_key || verbose))
1069     printformat_module("fe-common/silc", NULL, NULL,
1070                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
1071                        ident->realname);
1072   if (ident->username && verbose)
1073     printformat_module("fe-common/silc", NULL, NULL,
1074                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
1075                        ident->username);
1076   if (ident->host && (is_server_key || verbose))
1077     printformat_module("fe-common/silc", NULL, NULL,
1078                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
1079                        ident->host);
1080   if (ident->email && verbose)
1081     printformat_module("fe-common/silc", NULL, NULL,
1082                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
1083                        ident->email);
1084   if (ident->org && verbose)
1085     printformat_module("fe-common/silc", NULL, NULL,
1086                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
1087                        ident->org);
1088   if (ident->country && verbose)
1089     printformat_module("fe-common/silc", NULL, NULL,
1090                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
1091                        ident->country);
1092
1093   if (verbose) {
1094     printformat_module("fe-common/silc", NULL, NULL,
1095                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
1096                        fingerprint);
1097     printformat_module("fe-common/silc", NULL, NULL,
1098                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
1099                        babbleprint);
1100   }
1101
1102   silc_free(fingerprint);
1103   silc_free(babbleprint);
1104   silc_free(pk);
1105   silc_pkcs_public_key_free(public_key);
1106   silc_pkcs_free_identifier(ident);
1107
1108 }
1109
1110
1111 void silc_list_keys_in_dir(const char *dirname, const char *where)
1112 {
1113   DIR *dir;
1114   struct dirent *entry;
1115
1116   dir = opendir(dirname);
1117
1118   if (dir == NULL)
1119           cmd_return_error(CMDERR_ERRNO);
1120
1121   printformat_module("fe-common/silc", NULL, NULL,
1122                      MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
1123                      where);
1124
1125   rewinddir(dir);
1126
1127   while ((entry = readdir(dir)) != NULL) {
1128     /* try to open everything that isn't a directory */
1129     struct stat buf;
1130     char filename[256];
1131
1132     snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
1133     if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
1134       silc_list_key(filename, FALSE);
1135   }
1136
1137   closedir(dir);
1138 }
1139
1140 void silc_list_file(const char *filename)
1141 {
1142
1143   char path[256];
1144   struct stat buf;
1145
1146   snprintf(path, sizeof(path) - 1, "%s", filename);
1147   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1148     goto list_key;
1149
1150   snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
1151   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1152     goto list_key;
1153
1154   snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
1155            filename);
1156   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1157     goto list_key;
1158   
1159   snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
1160            filename);
1161   if (!stat(path, &buf) && S_ISREG(buf.st_mode))
1162     goto list_key;
1163
1164   return;
1165
1166 list_key:
1167
1168   silc_list_key(path, TRUE);
1169
1170 }
1171
1172 /* Lists locally saved client and server public keys. */
1173 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
1174                              WI_ITEM_REC *item)
1175 {
1176   GHashTable *optlist;
1177   char *filename;
1178   void *free_arg;
1179   char dirname[256];
1180
1181   if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
1182                       PARAM_FLAG_GETREST, "listkeys", &optlist,
1183                       &filename))
1184     return;
1185
1186   if (*filename != '\0') {
1187
1188     silc_list_file(filename);
1189
1190   } else {
1191     int clients, servers;
1192
1193     clients = (g_hash_table_lookup(optlist, "clients") != NULL);
1194     servers = (g_hash_table_lookup(optlist, "servers") != NULL);
1195
1196     if (!(clients || servers))
1197       clients = servers = 1;
1198
1199     if (servers) {
1200       snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
1201       silc_list_keys_in_dir(dirname, "server");
1202     }
1203
1204     if (clients) {
1205       snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
1206       silc_list_keys_in_dir(dirname, "client");
1207     }
1208   }
1209   cmd_params_free(free_arg);
1210 }
1211
1212 void silc_channels_init(void)
1213 {
1214   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1215   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
1216   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
1217   signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1218   signal_add("mime", (SIGNAL_FUNC) sig_mime);
1219
1220   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
1221   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
1222   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
1223   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
1224   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
1225   command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
1226   command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys); 
1227
1228   command_set_options("listkeys", "clients servers");
1229   command_set_options("action", "sign channel");
1230   command_set_options("notice", "sign channel");
1231
1232   silc_nicklist_init();
1233 }
1234
1235 void silc_channels_deinit(void)
1236 {
1237   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
1238   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1239   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1240   signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
1241   signal_remove("mime", (SIGNAL_FUNC) sig_mime);
1242
1243   command_unbind("part", (SIGNAL_FUNC) command_part);
1244   command_unbind("me", (SIGNAL_FUNC) command_me);
1245   command_unbind("action", (SIGNAL_FUNC) command_action);
1246   command_unbind("notice", (SIGNAL_FUNC) command_notice);
1247   command_unbind("away", (SIGNAL_FUNC) command_away);
1248   command_unbind("key", (SIGNAL_FUNC) command_key);
1249   command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
1250
1251   silc_nicklist_deinit();
1252 }