Porting to new Toolkit API.
[crypto.git] / apps / irssi / src / silc / core / silc-channels.c
index 9c66e1f17796e4fbce080bb0d92a4de8f051eb63..1bd719518c0851eec3fcadaf7f82688c04cf72a8 100644 (file)
@@ -1,19 +1,19 @@
 /*
   silc-channels.c : irssi
 
-  Copyright (C) 2000 - 2001 Timo Sirainen
-                            Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Copyright (C) 2000 - 2001, 2004, 2006 Timo Sirainen
+                Pekka Riikonen <priikone@silcnet.org>
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
-  
+
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
@@ -38,6 +38,7 @@
 #include "silc-channels.h"
 #include "silc-queries.h"
 #include "silc-nicklist.h"
+#include "silc-cmdqueue.h"
 #include "window-item-def.h"
 
 #include "fe-common/core/printtext.h"
 
 #include "silc-commands.h"
 
+void sig_mime(SILC_SERVER_REC *server, SILC_CHANNEL_REC *channel,
+             const char *blob, const char *nick, int verified)
+{
+  unsigned char *message;
+  SilcUInt32 message_len;
+  SilcMime mime;
+
+  if (!(IS_SILC_SERVER(server)))
+    return;
+
+  message = silc_unescape_data(blob, &message_len);
+
+  mime = silc_mime_decode(NULL, message, message_len);
+  if (!mime) {
+    silc_free(message);
+    return;
+  }
+
+  printformat_module("fe-common/silc", server,
+                    channel == NULL ? NULL : channel->name,
+                    MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
+                    nick == NULL ? "[<unknown>]" : nick,
+                    silc_mime_get_field(mime, "Content-Type"));
+
+  silc_free(message);
+  silc_mime_free(mime);
+}
+
 SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
-                                     const char *name, int automatic)
+                                     const char *name,
+                                     const char *visible_name,
+                                     int automatic)
 {
   SILC_CHANNEL_REC *rec;
 
@@ -55,10 +86,8 @@ SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
 
   rec = g_new0(SILC_CHANNEL_REC, 1);
   rec->chat_type = SILC_PROTOCOL;
-  rec->name = g_strdup(name);
-  rec->server = server;
-
-  channel_init((CHANNEL_REC *) rec, automatic);
+  channel_init((CHANNEL_REC *)rec, (SERVER_REC *)server, name, name,
+              automatic);
   return rec;
 }
 
@@ -73,6 +102,8 @@ static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
     /* destroying channel record without actually
        having left the channel yet */
     silc_command_exec(channel->server, "LEAVE", channel->name);
+    /* enable queueing because we destroy the channel immedially */
+    silc_queue_enable(channel->server->conn);
   }
 }
 
@@ -105,10 +136,15 @@ static void sig_connected(SILC_SERVER_REC *server)
 
 static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
 {
-  if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
+  if (IS_SILC_SERVER(server) && server->conn)
     silc_command_exec(server, "QUIT", msg);
 }
 
+static void sig_gui_quit(SILC_SERVER_REC *server, const char *msg)
+{
+  silc_client_stop(silc_client);
+}
+
 /* Find Irssi channel entry by SILC channel entry */
 
 SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
@@ -135,7 +171,7 @@ static void command_part(const char *data, SILC_SERVER_REC *server,
 {
   SILC_CHANNEL_REC *chanrec;
   char userhost[256];
-  
+
   CMD_SILC_SERVER(server);
 
   if (!IS_SILC_SERVER(server) || !server->connected)
@@ -144,123 +180,132 @@ static void command_part(const char *data, SILC_SERVER_REC *server,
   if (!strcmp(data, "*") || *data == '\0') {
     if (!IS_SILC_CHANNEL(item))
       cmd_return_error(CMDERR_NOT_JOINED);
-    data = item->name;
+    data = item->visible_name;
   }
 
   chanrec = silc_channel_find(server, data);
-  if (chanrec == NULL) 
+  if (chanrec == NULL)
     cmd_return_error(CMDERR_CHAN_NOT_FOUND);
 
   memset(userhost, 0, sizeof(userhost));
   snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
-          server->conn->local_entry->username, 
+          server->conn->local_entry->username,
           server->conn->local_entry->hostname);
   signal_emit("message part", 5, server, chanrec->name,
              server->nick, userhost, "");
-  
+
   chanrec->left = TRUE;
   silc_command_exec(server, "LEAVE", chanrec->name);
+  /* enable queueing because we destroy the channel immedially */
+  silc_queue_enable(server->conn);
   signal_stop();
-  
+
   channel_destroy(CHANNEL(chanrec));
 }
 
-/* ME local command. */
 
-static void command_me(const char *data, SILC_SERVER_REC *server,
-                      WI_ITEM_REC *item)
+/* ACTION local command. */
+
+static void command_action(const char *data, SILC_SERVER_REC *server,
+                          WI_ITEM_REC *item)
 {
-  SILC_CHANNEL_REC *chanrec;
-  char *tmpcmd = "ME", *tmp;
-  SilcUInt32 argc = 0;
-  unsigned char **argv;
-  SilcUInt32 *argv_lens, *argv_types;
-  int i;
-  CMD_SILC_SERVER(server);
+  GHashTable *optlist;
+  char *target, *msg;
+  char *message = NULL;
+  int target_type;
+  void *free_arg;
 
+  CMD_SILC_SERVER(server);
   if (!IS_SILC_SERVER(server) || !server->connected)
     cmd_return_error(CMDERR_NOT_CONNECTED);
 
-  if (!IS_SILC_CHANNEL(item))
+  if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
     cmd_return_error(CMDERR_NOT_JOINED);
 
   /* Now parse all arguments */
-  tmp = g_strconcat(tmpcmd, " ", data, NULL);
-  silc_parse_command_line(tmp, &argv, &argv_lens,
-                         &argv_types, &argc, 2);
-  g_free(tmp);
-
-  if (argc < 2)
-    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+  if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
+                     PARAM_FLAG_GETREST,
+                     "action", &optlist, &target, &msg))
+    return;
 
-  chanrec = silc_channel_find(server, item->name);
-  if (chanrec == NULL) 
-    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+  if (*target == '\0' || *msg == '\0')
+    cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  if (strcmp(target, "*") == 0) {
+    /* send to active channel/query */
+    if (item == NULL)
+      cmd_param_error(CMDERR_NOT_JOINED);
+
+    target_type = IS_SILC_CHANNEL(item) ?
+           SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+    target = (char *)window_item_get_target(item);
+  } else if (g_hash_table_lookup(optlist, "channel") != NULL)
+    target_type = SEND_TARGET_CHANNEL;
+  else {
+    target_type = SEND_TARGET_NICK;
+  }
 
-  /* Send the action message */
-  silc_client_send_channel_message(silc_client, server->conn, 
-                                  chanrec->entry, NULL,
-                                  SILC_MESSAGE_FLAG_ACTION, 
-                                  argv[1], argv_lens[1], TRUE);
+  if (!silc_term_utf8()) {
+    int len = silc_utf8_encoded_len(msg, strlen(msg),
+                                   SILC_STRING_LOCALE);
+    message = silc_calloc(len + 1, sizeof(*message));
+    g_return_if_fail(message != NULL);
+    silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE,
+                    message, len);
+  }
 
-  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
-                    MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
-                     server->conn->local_entry->nickname, argv[1]);
+  if (target != NULL) {
+    if (target_type == SEND_TARGET_CHANNEL) {
+      if (silc_send_channel(server, target, (message != NULL ? message : msg),
+                           SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
+                           (g_hash_table_lookup(optlist, "sign") != NULL ?
+                            SILC_MESSAGE_FLAG_SIGNED : 0))) {
+       if (g_hash_table_lookup(optlist, "sign"))
+          signal_emit("message silc signed_own_action", 3, server, msg, target);
+       else
+          signal_emit("message silc own_action", 3, server, msg, target);
+      }
+    } else {
+      if (silc_send_msg(server, target, (message != NULL ? message : msg),
+                       (message != NULL ? strlen(message) : strlen(msg)),
+                       SILC_MESSAGE_FLAG_ACTION | SILC_MESSAGE_FLAG_UTF8 |
+                       (g_hash_table_lookup(optlist, "sign") != NULL ?
+                        SILC_MESSAGE_FLAG_SIGNED : 0))) {
+       if (g_hash_table_lookup(optlist, "sign"))
+         signal_emit("message silc signed_own_private_action", 3,
+                         server, msg, target);
+       else
+         signal_emit("message silc own_private_action", 3,
+                         server, msg, target);
+      }
+    }
+  }
 
-  for (i = 0; i < argc; i++)
-    silc_free(argv[i]);
-  silc_free(argv_lens);
-  silc_free(argv_types);
+  cmd_params_free(free_arg);
+  silc_free(message);
 }
 
-/* ACTION local command. Same as ME but takes the channel as mandatory
-   argument. */
+/* ME local command. */
 
-static void command_action(const char *data, SILC_SERVER_REC *server,
-                          WI_ITEM_REC *item)
+static void command_me(const char *data, SILC_SERVER_REC *server,
+                      WI_ITEM_REC *item)
 {
-  SILC_CHANNEL_REC *chanrec;
-  char *tmpcmd = "ME", *tmp;
-  SilcUInt32 argc = 0;
-  unsigned char **argv;
-  SilcUInt32 *argv_lens, *argv_types;
-  int i;
+  char *tmpcmd;
+
   CMD_SILC_SERVER(server);
   if (!IS_SILC_SERVER(server) || !server->connected)
     cmd_return_error(CMDERR_NOT_CONNECTED);
 
-  if (!IS_SILC_CHANNEL(item))
+  if (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item))
     cmd_return_error(CMDERR_NOT_JOINED);
 
-  /* Now parse all arguments */
-  tmp = g_strconcat(tmpcmd, " ", data, NULL);
-  silc_parse_command_line(tmp, &argv, &argv_lens,
-                         &argv_types, &argc, 3);
-  g_free(tmp);
+  if (IS_SILC_CHANNEL(item))
+    tmpcmd = g_strdup_printf("-channel %s %s", item->visible_name, data);
+  else
+    tmpcmd = g_strdup_printf("%s %s", item->visible_name, data);
 
-  if (argc < 3)
-    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
-
-  chanrec = silc_channel_find(server, argv[1]);
-  if (chanrec == NULL) 
-    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
-
-  /* Send the action message */
-  silc_client_send_channel_message(silc_client, server->conn, 
-                                  chanrec->entry, NULL,
-                                  SILC_MESSAGE_FLAG_ACTION, 
-                                  argv[2], argv_lens[2], TRUE);
-
-  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
-                    MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
-                     server->conn->local_entry->nickname, argv[2]);
-
-  for (i = 0; i < argc; i++)
-    silc_free(argv[i]);
-  silc_free(argv_lens);
-  silc_free(argv_types);
+  command_action(tmpcmd, server, item);
+  g_free(tmpcmd);
 }
 
 /* NOTICE local command. */
@@ -268,83 +313,132 @@ static void command_action(const char *data, SILC_SERVER_REC *server,
 static void command_notice(const char *data, SILC_SERVER_REC *server,
                           WI_ITEM_REC *item)
 {
-  SILC_CHANNEL_REC *chanrec;
-  char *tmpcmd = "ME", *tmp;
-  SilcUInt32 argc = 0;
-  unsigned char **argv;
-  SilcUInt32 *argv_lens, *argv_types;
-  int i;
+  GHashTable *optlist;
+  char *target, *msg;
+  char *message = NULL;
+  int target_type;
+  void *free_arg;
+
   CMD_SILC_SERVER(server);
   if (!IS_SILC_SERVER(server) || !server->connected)
     cmd_return_error(CMDERR_NOT_CONNECTED);
 
-  if (!IS_SILC_CHANNEL(item))
+  if ((item != NULL) && (!IS_SILC_CHANNEL(item) && !IS_SILC_QUERY(item)))
     cmd_return_error(CMDERR_NOT_JOINED);
 
   /* Now parse all arguments */
-  tmp = g_strconcat(tmpcmd, " ", data, NULL);
-  silc_parse_command_line(tmp, &argv, &argv_lens,
-                         &argv_types, &argc, 2);
-  g_free(tmp);
-
-  if (argc < 2)
-    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+  if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
+                     PARAM_FLAG_GETREST,
+                     "notice", &optlist, &target, &msg))
+    return;
 
-  chanrec = silc_channel_find(server, item->name);
-  if (chanrec == NULL) 
-    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+  if (*target == '\0' || *msg == '\0')
+    cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  if (strcmp(target, "*") == 0) {
+    /* send to active channel/query */
+    if (item == NULL)
+      cmd_param_error(CMDERR_NOT_JOINED);
+
+    target_type = IS_SILC_CHANNEL(item) ?
+           SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+    target = (char *)window_item_get_target(item);
+  } else if (g_hash_table_lookup(optlist, "channel") != NULL)
+    target_type = SEND_TARGET_CHANNEL;
+  else {
+    target_type = SEND_TARGET_NICK;
+  }
 
-  /* Send the action message */
-  silc_client_send_channel_message(silc_client, server->conn, 
-                                  chanrec->entry, NULL,
-                                  SILC_MESSAGE_FLAG_NOTICE, 
-                                  argv[1], argv_lens[1], TRUE);
+  if (!silc_term_utf8()) {
+    int len = silc_utf8_encoded_len(msg, strlen(msg),
+                                   SILC_STRING_LOCALE);
+    message = silc_calloc(len + 1, sizeof(*message));
+    g_return_if_fail(message != NULL);
+    silc_utf8_encode(msg, strlen(msg), SILC_STRING_LOCALE,
+                    message, len);
+  }
 
-  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
-                    MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
-                     server->conn->local_entry->nickname, argv[1]);
+  if (target != NULL) {
+    if (target_type == SEND_TARGET_CHANNEL) {
+      if (silc_send_channel(server, target, (message != NULL ? message : msg),
+                           SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
+                           (g_hash_table_lookup(optlist, "sign") != NULL ?
+                            SILC_MESSAGE_FLAG_SIGNED : 0))) {
+       if (g_hash_table_lookup(optlist, "sign"))
+          signal_emit("message silc signed_own_notice", 3, server, msg, target);
+       else
+          signal_emit("message silc own_notice", 3, server, msg, target);
+      }
+    } else {
+      if (silc_send_msg(server, target, (message != NULL ? message : msg),
+                       (message != NULL ? strlen(message) : strlen(msg)),
+                       SILC_MESSAGE_FLAG_NOTICE | SILC_MESSAGE_FLAG_UTF8 |
+                       (g_hash_table_lookup(optlist, "sign") != NULL ?
+                        SILC_MESSAGE_FLAG_SIGNED : 0))) {
+       if (g_hash_table_lookup(optlist, "sign"))
+         signal_emit("message silc signed_own_private_notice", 3,
+                         server, msg, target);
+       else
+         signal_emit("message silc own_private_notice", 3,
+                         server, msg, target);
+      }
+    }
+  }
 
-  for (i = 0; i < argc; i++)
-    silc_free(argv[i]);
-  silc_free(argv_lens);
-  silc_free(argv_types);
+  cmd_params_free(free_arg);
+  silc_free(message);
 }
 
 /* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
    flag. */
 
-static void command_away(const char *data, SILC_SERVER_REC *server,
-                        WI_ITEM_REC *item)
+bool silc_set_away(const char *reason, SILC_SERVER_REC *server)
 {
   bool set;
 
-  CMD_SILC_SERVER(server);
-
   if (!IS_SILC_SERVER(server) || !server->connected)
-    cmd_return_error(CMDERR_NOT_CONNECTED);
+    return FALSE;
 
-  if (*data == '\0') {
+  if (*reason == '\0') {
     /* Remove any possible away message */
     silc_client_set_away_message(silc_client, server->conn, NULL);
     set = FALSE;
 
-    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_UNSET_AWAY);
-    server->usermode_away = FALSE;
   } else {
     /* Set the away message */
-    silc_client_set_away_message(silc_client, server->conn, (char *)data);
+    silc_client_set_away_message(silc_client, server->conn, (char *)reason);
     set = TRUE;
 
-    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
-                      SILCTXT_SET_AWAY, data);
-    server->usermode_away = TRUE;
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_SET_AWAY, reason);
   }
 
+  server->usermode_away = set;
+  g_free_and_null(server->away_reason);
+  if (set)
+    server->away_reason = g_strdup((char *)reason);
+
   signal_emit("away mode changed", 1, server);
 
-  silc_command_exec(server, "UMODE", set ? "+g" : "-g");
+  return set;
+}
+
+static void command_away(const char *data, SILC_SERVER_REC *server,
+                        WI_ITEM_REC *item)
+{
+  CMD_SILC_SERVER(server);
+
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  g_free_and_null(server->away_reason);
+  if ((data) && (*data != '\0'))
+    server->away_reason = g_strdup(data);
+
+  silc_command_exec(server, "UMODE",
+                   (server->away_reason != NULL) ? "+g" : "-g");
 }
 
 typedef struct {
@@ -379,30 +473,30 @@ static void keyagr_completion(SilcClient client,
       /* Set the private key for this client */
       silc_client_del_private_message_key(client, conn, client_entry);
       silc_client_add_private_message_key_ske(client, conn, client_entry,
-                                             NULL, key, i->responder);
+                                             NULL, NULL, key, i->responder);
       printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
-                        SILCTXT_KEY_AGREEMENT_PRIVMSG, 
+                        SILCTXT_KEY_AGREEMENT_PRIVMSG,
                         client_entry->nickname);
       silc_ske_free_key_material(key);
     }
-    
+
     break;
-    
+
   case SILC_KEY_AGREEMENT_ERROR:
     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
     break;
-    
+
   case SILC_KEY_AGREEMENT_FAILURE:
     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
     break;
-    
+
   case SILC_KEY_AGREEMENT_TIMEOUT:
     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
     break;
-    
+
   case SILC_KEY_AGREEMENT_ABORTED:
     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
@@ -413,20 +507,21 @@ static void keyagr_completion(SilcClient client,
                       SILCTXT_KEY_AGREEMENT_ALREADY_STARTED,
                       client_entry->nickname);
     break;
-    
+
   case SILC_KEY_AGREEMENT_SELF_DENIED:
     printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_SELF_DENIED);
     break;
-    
+
   default:
     break;
-  } 
+  }
 
   if (i)
     silc_free(i);
 }
 
+#if 0
 /* Local command KEY. This command is used to set and unset private
    keys for channels, set and unset private keys for private messages
    with remote clients and to send key agreement requests and
@@ -454,7 +549,7 @@ static void silc_client_command_key_get_clients(SilcClient client,
   KeyGetClients internal = (KeyGetClients)context;
 
   if (!clients) {
-    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s",
              internal->nick);
     silc_free(internal->data);
     silc_free(internal->nick);
@@ -486,7 +581,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
   unsigned char **argv;
   SilcUInt32 *argv_lens, *argv_types;
   char *bindhost = NULL;
+
   CMD_SILC_SERVER(server);
 
   if (!server || !IS_SILC_SERVER(server) || !server->connected)
@@ -521,7 +616,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
                           MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
        return;
       }
-      
+
       /* Find client entry */
       entrys = silc_client_get_clients_local(silc_client, conn, nickname,
                                             argv[2], &entry_count);
@@ -567,35 +662,49 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
     command = 1;
 
     if (argc >= 5) {
+      char *cipher = NULL, *hmac = NULL;
+
       if (type == 1 && client_entry) {
        /* Set private message key */
-       
+       bool responder = FALSE;
+
        silc_client_del_private_message_key(silc_client, conn, client_entry);
 
-       if (argc >= 6)
-         silc_client_add_private_message_key(silc_client, conn, client_entry,
-                                             argv[5], argv[4],
-                                             argv_lens[4],
-                                             (argv[4][0] == '*' ?
-                                              TRUE : FALSE), FALSE);
-       else
-         silc_client_add_private_message_key(silc_client, conn, client_entry,
-                                             NULL, argv[4],
-                                             argv_lens[4],
-                                             (argv[4][0] == '*' ?
-                                              TRUE : FALSE), FALSE);
+       if (argc >= 6) {
+         if (!strcasecmp(argv[5], "-responder"))
+           responder = TRUE;
+         else
+           cipher = argv[5];
+       }
+       if (argc >= 7) {
+         if (!strcasecmp(argv[6], "-responder"))
+           responder = TRUE;
+         else
+           hmac = argv[6];
+       }
+       if (argc >= 8) {
+         if (!strcasecmp(argv[7], "-responder"))
+           responder = TRUE;
+       }
+
+       silc_client_add_private_message_key(silc_client, conn, client_entry,
+                                           cipher, hmac,
+                                           argv[4], argv_lens[4],
+                                           (argv[4][0] == '*' ?
+                                            TRUE : FALSE), responder);
 
        /* Send the key to the remote client so that it starts using it
           too. */
-       silc_client_send_private_message_key(silc_client, conn, 
+       /* XXX for now we don't do this.  This feature is pretty stupid
+          and should perhaps be removed altogether from SILC.
+       silc_client_send_private_message_key(silc_client, conn,
                                             client_entry, TRUE);
+       */
       } else if (type == 2) {
        /* Set private channel key */
-       char *cipher = NULL, *hmac = NULL;
-
        if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
          printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
-                            SILCTXT_CH_PRIVATE_KEY_NOMODE, 
+                            SILCTXT_CH_PRIVATE_KEY_NOMODE,
                             channel_entry->channel_name);
          goto out;
        }
@@ -605,26 +714,26 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
        if (argc >= 7)
          hmac = argv[6];
 
-       if (!silc_client_add_channel_private_key(silc_client, conn, 
+       if (!silc_client_add_channel_private_key(silc_client, conn,
                                                 channel_entry, NULL,
                                                 cipher, hmac,
                                                 argv[4],
-                                                argv_lens[4])) {
+                                                argv_lens[4], NULL)) {
          printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
-                            SILCTXT_CH_PRIVATE_KEY_ERROR, 
+                            SILCTXT_CH_PRIVATE_KEY_ERROR,
                             channel_entry->channel_name);
          goto out;
        }
 
        printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
-                          SILCTXT_CH_PRIVATE_KEY_ADD, 
+                          SILCTXT_CH_PRIVATE_KEY_ADD,
                           channel_entry->channel_name);
       }
     }
 
     goto out;
   }
-  
+
   /* Unset command */
   if (!strcasecmp(argv[3], "unset")) {
     command = 2;
@@ -639,12 +748,12 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
       int number;
 
       if (argc == 4)
-       silc_client_del_channel_private_keys(silc_client, conn, 
+       silc_client_del_channel_private_keys(silc_client, conn,
                                             channel_entry);
 
       if (argc > 4) {
        number = atoi(argv[4]);
-       keys = silc_client_list_channel_private_keys(silc_client, conn, 
+       keys = silc_client_list_channel_private_keys(silc_client, conn,
                                                     channel_entry,
                                                     &keys_count);
        if (!keys)
@@ -674,7 +783,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
       int k, i, len;
       char buf[1024];
 
-      keys = silc_client_list_private_message_keys(silc_client, conn, 
+      keys = silc_client_list_private_message_keys(silc_client, conn,
                                                   &keys_count);
       if (!keys)
        goto out;
@@ -692,7 +801,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
            for (i = 0; i < 30 - len; i++)
              strcat(buf, " ");
          strcat(buf, " ");
-         
+
          len = strlen(keys[k].cipher);
          strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
          if (len < 14)
@@ -723,7 +832,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
            for (i = 0; i < 30 - len; i++)
              strcat(buf, " ");
          strcat(buf, " ");
-         
+
          len = strlen(keys[k].cipher);
          strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
          if (len < 14)
@@ -748,7 +857,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
       int k, i, len;
       char buf[1024];
 
-      keys = silc_client_list_channel_private_keys(silc_client, conn, 
+      keys = silc_client_list_channel_private_keys(silc_client, conn,
                                                   channel_entry,
                                                   &keys_count);
 
@@ -758,30 +867,31 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
 
       if (!keys)
        goto out;
-      
+
       for (k = 0; k < keys_count; k++) {
        memset(buf, 0, sizeof(buf));
        strncat(buf, "  ", 2);
 
-       len = strlen(keys[k]->cipher->cipher->name);
-       strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
+       len = strlen(silc_cipher_get_name(keys[k]->cipher));
+       strncat(buf, silc_cipher_get_name(keys[k]->cipher),
+               len > 16 ? 16 : len);
        if (len < 16)
          for (i = 0; i < 16 - len; i++)
            strcat(buf, " ");
        strcat(buf, " ");
-       
+
        len = strlen(silc_hmac_get_name(keys[k]->hmac));
        strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
        if (len < 16)
          for (i = 0; i < 16 - len; i++)
            strcat(buf, " ");
        strcat(buf, " ");
-       
+
        strcat(buf, "<hidden>");
 
        silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
       }
-      
+
       silc_client_free_channel_private_keys(keys, keys_count);
     }
 
@@ -800,19 +910,19 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
     internal = silc_calloc(1, sizeof(*internal));
     internal->type = type;
     internal->server = server;
-    
+
     if (!hostname) {
       if (settings_get_bool("use_auto_addr")) {
-       
+
         hostname = (char *)settings_get_str("auto_public_ip");
 
-       /* If the hostname isn't set, treat this case as if auto_public_ip 
+       /* If the hostname isn't set, treat this case as if auto_public_ip
           wasn't set. */
         if ((hostname) && (*hostname == '\0')) {
            hostname = NULL;
         } else {
           bindhost = (char *)settings_get_str("auto_bind_ip");
-            
+
          /* if the bind_ip isn't set, but the public_ip IS, then assume then
             public_ip is the same value as the bind_ip. */
           if ((bindhost) && (*bindhost == '\0'))
@@ -847,7 +957,7 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
       SilcUInt32 keys_count;
       int number;
 
-      keys = silc_client_list_channel_private_keys(silc_client, conn, 
+      keys = silc_client_list_channel_private_keys(silc_client, conn,
                                                   channel_entry,
                                                   &keys_count);
       if (!keys)
@@ -868,8 +978,8 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
       }
 
       /* Set the current channel private key */
-      silc_client_current_channel_private_key(silc_client, conn, 
-                                             channel_entry, 
+      silc_client_current_channel_private_key(silc_client, conn,
+                                             channel_entry,
                                              keys[chanrec->cur_key]);
       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
                         SILCTXT_CH_PRIVATE_KEY_CHANGE, chanrec->cur_key + 1,
@@ -892,9 +1002,9 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
                       SILCTXT_KEY_AGREEMENT, argv[2]);
     internal->responder = TRUE;
     silc_client_send_key_agreement(
-                          silc_client, conn, client_entry, hostname, 
-                          bindhost, port, 
-                          settings_get_int("key_exchange_timeout_secs"), 
+                          silc_client, conn, client_entry, hostname,
+                          bindhost, port,
+                          settings_get_int("key_exchange_timeout_secs"),
                           keyagr_completion, internal);
     if (!hostname)
       silc_free(internal);
@@ -905,8 +1015,8 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
     printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
                       SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
     internal->responder = FALSE;
-    silc_client_perform_key_agreement(silc_client, conn, client_entry, 
-                                     hostname, port, keyagr_completion, 
+    silc_client_perform_key_agreement(silc_client, conn, client_entry,
+                                     hostname, port, keyagr_completion,
                                      internal);
     goto out;
   }
@@ -915,12 +1025,192 @@ static void command_key(const char *data, SILC_SERVER_REC *server,
   silc_free(nickname);
 }
 
-/* Lists locally saved client and server public keys. */
+void silc_list_key(const char *pub_filename, int verbose)
+{
+  SilcPublicKey public_key;
+  SilcPublicKeyIdentifier ident;
+  char *fingerprint, *babbleprint;
+  unsigned char *pk;
+  SilcUInt32 pk_len;
+  SilcPKCS pkcs;
+  SilcUInt32 key_len = 0;
+  int is_server_key = (strstr(pub_filename, "serverkeys") != NULL);
+
+  if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
+                                SILC_PKCS_FILE_PEM) == FALSE)
+    if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
+                                  SILC_PKCS_FILE_BIN) == FALSE) {
+      printformat_module("fe-common/silc", NULL, NULL,
+                         MSGLEVEL_CRAP, SILCTXT_LISTKEY_LOADPUB,
+                         pub_filename);
+      return;
+    }
 
+  ident = silc_pkcs_decode_identifier(public_key->identifier);
+
+  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+  babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+  if (silc_pkcs_alloc(public_key->name, &pkcs)) {
+    key_len = silc_pkcs_public_key_set(pkcs, public_key);
+    silc_pkcs_free(pkcs);
+  }
+
+  printformat_module("fe-common/silc", NULL, NULL,
+                     MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FILE,
+                     pub_filename);
+
+  if (verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ALG,
+                       public_key->name);
+  if (key_len && verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BITS,
+                        (unsigned int)key_len);
+  if (ident->realname && (!is_server_key || verbose))
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_RN,
+                       ident->realname);
+  if (ident->username && verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_UN,
+                       ident->username);
+  if (ident->host && (is_server_key || verbose))
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_HN,
+                       ident->host);
+  if (ident->email && verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_EMAIL,
+                       ident->email);
+  if (ident->org && verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_ORG,
+                       ident->org);
+  if (ident->country && verbose)
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_C,
+                       ident->country);
+
+  if (verbose) {
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_FINGER,
+                       fingerprint);
+    printformat_module("fe-common/silc", NULL, NULL,
+                       MSGLEVEL_CRAP, SILCTXT_LISTKEY_PUB_BABL,
+                       babbleprint);
+  }
+
+  silc_free(fingerprint);
+  silc_free(babbleprint);
+  silc_free(pk);
+  silc_pkcs_public_key_free(public_key);
+  silc_pkcs_free_identifier(ident);
+
+}
+
+void silc_list_keys_in_dir(const char *dirname, const char *where)
+{
+  DIR *dir;
+  struct dirent *entry;
+
+  dir = opendir(dirname);
+
+  if (dir == NULL)
+         cmd_return_error(CMDERR_ERRNO);
+
+  printformat_module("fe-common/silc", NULL, NULL,
+                     MSGLEVEL_CRAP, SILCTXT_LISTKEY_LIST,
+                     where);
+
+  rewinddir(dir);
+
+  while ((entry = readdir(dir)) != NULL) {
+    /* try to open everything that isn't a directory */
+    struct stat buf;
+    char filename[256];
+
+    snprintf(filename, sizeof(filename) - 1, "%s/%s", dirname, entry->d_name);
+    if (!stat(filename, &buf) && S_ISREG(buf.st_mode))
+      silc_list_key(filename, FALSE);
+  }
+
+  closedir(dir);
+}
+
+void silc_list_file(const char *filename)
+{
+
+  char path[256];
+  struct stat buf;
+
+  snprintf(path, sizeof(path) - 1, "%s", filename);
+  if (!stat(path, &buf) && S_ISREG(buf.st_mode))
+    goto list_key;
+
+  snprintf(path, sizeof(path) - 1, "%s/%s", get_irssi_dir(), filename);
+  if (!stat(path, &buf) && S_ISREG(buf.st_mode))
+    goto list_key;
+
+  snprintf(path,sizeof(path) - 1, "%s/clientkeys/%s", get_irssi_dir(),
+          filename);
+  if (!stat(path, &buf) && S_ISREG(buf.st_mode))
+    goto list_key;
+
+  snprintf(path,sizeof(path) - 1, "%s/serverkeys/%s", get_irssi_dir(),
+          filename);
+  if (!stat(path, &buf) && S_ISREG(buf.st_mode))
+    goto list_key;
+
+  return;
+
+list_key:
+
+  silc_list_key(path, TRUE);
+
+}
+#endif /* 0 */
+
+/* Lists locally saved client and server public keys. */
 static void command_listkeys(const char *data, SILC_SERVER_REC *server,
                             WI_ITEM_REC *item)
 {
+  GHashTable *optlist;
+  char *filename;
+  void *free_arg;
+  char dirname[256];
+
+  if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                     PARAM_FLAG_GETREST, "listkeys", &optlist,
+                     &filename))
+    return;
 
+  if (*filename != '\0') {
+
+    silc_list_file(filename);
+
+  } else {
+    int clients, servers;
+
+    clients = (g_hash_table_lookup(optlist, "clients") != NULL);
+    servers = (g_hash_table_lookup(optlist, "servers") != NULL);
+
+    if (!(clients || servers))
+      clients = servers = 1;
+
+    if (servers) {
+      snprintf(dirname, sizeof(dirname) - 1, "%s/serverkeys", get_irssi_dir());
+      silc_list_keys_in_dir(dirname, "server");
+    }
+
+    if (clients) {
+      snprintf(dirname, sizeof(dirname) - 1, "%s/clientkeys", get_irssi_dir());
+      silc_list_keys_in_dir(dirname, "client");
+    }
+  }
+  cmd_params_free(free_arg);
 }
 
 void silc_channels_init(void)
@@ -928,14 +1218,20 @@ void silc_channels_init(void)
   signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
   signal_add("server connected", (SIGNAL_FUNC) sig_connected);
   signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
+  signal_add("gui exit", (SIGNAL_FUNC) sig_gui_quit);
+  signal_add("mime", (SIGNAL_FUNC) sig_mime);
 
   command_bind_silc("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
   command_bind_silc("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
   command_bind_silc("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
   command_bind_silc("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
   command_bind_silc("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
-  command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
-  command_bind_silc("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
+  //  command_bind_silc("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
+  //  command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
+
+  command_set_options("listkeys", "clients servers");
+  command_set_options("action", "sign channel");
+  command_set_options("notice", "sign channel");
 
   silc_nicklist_init();
 }
@@ -945,14 +1241,16 @@ void silc_channels_deinit(void)
   signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
   signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
   signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
+  signal_remove("gui exit", (SIGNAL_FUNC) sig_gui_quit);
+  signal_remove("mime", (SIGNAL_FUNC) sig_mime);
 
   command_unbind("part", (SIGNAL_FUNC) command_part);
   command_unbind("me", (SIGNAL_FUNC) command_me);
   command_unbind("action", (SIGNAL_FUNC) command_action);
   command_unbind("notice", (SIGNAL_FUNC) command_notice);
   command_unbind("away", (SIGNAL_FUNC) command_away);
-  command_unbind("key", (SIGNAL_FUNC) command_key);
-  command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
+  //  command_unbind("key", (SIGNAL_FUNC) command_key);
+  //  command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
 
   silc_nicklist_deinit();
 }