updates.
[silc.git] / apps / irssi / src / silc / core / silc-core.c
index f67cf129aff5b99a541e8e22de7c612a4dd81ca5..8448245fb2693c3c8d7e43fe22522123c5ed42d5 100644 (file)
 #include "settings.h"
 #include "fe-common/core/printtext.h"
 #include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
+#include "fe-common/silc/module-formats.h"
 
 /* Command line option variables */
-static char *opt_server = NULL;
-static int opt_port = 0;
-static char *opt_nickname = NULL;
-static char *opt_channel = NULL;
-static char *opt_cipher = NULL;
-static char *opt_public_key = NULL;
-static char *opt_private_key = NULL;
-static char *opt_config_file = NULL;
-static bool opt_no_silcrc = FALSE;
-
 static bool opt_create_keypair = FALSE;
+static bool opt_debug = FALSE;
 static char *opt_pkcs = NULL;
 static char *opt_keyfile = NULL;
 static int opt_bits = 0;
@@ -59,6 +52,7 @@ static int idletag;
 SilcClient silc_client = NULL;
 SilcClientConfig silc_config = NULL;
 extern SilcClientOperations ops;
+extern int silc_debug;
 #ifdef SILC_SIM
 /* SIM (SILC Module) table */
 SilcSimContext **sims = NULL;
@@ -89,14 +83,18 @@ static void
 silc_command_reply(SilcClient client, SilcClientConnection conn,
                   SilcCommandPayload cmd_payload, int success,
                   SilcCommand command, SilcCommandStatus status, ...);
-
-static int silc_verify_public_key(SilcClient client,
-                                 SilcClientConnection conn, 
-                                 SilcSocketType conn_type,
-                                 unsigned char *pk, uint32 pk_len,
-                                 SilcSKEPKType pk_type);
-static unsigned char *silc_ask_passphrase(SilcClient client,
-                                         SilcClientConnection conn);
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context);
+static void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context);
+static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                               SilcAskPassphrase completion, void *context);
 static int 
 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
                     char *hostname, uint16 port,
@@ -124,7 +122,7 @@ static void silc_say(SilcClient client, SilcClientConnection conn,
   
   va_start(va, msg);
   str = g_strdup_vprintf(msg, va);
-  printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
+  printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
   g_free(str);
   va_end(va);
 }
@@ -158,10 +156,16 @@ silc_channel_message(SilcClient client, SilcClientConnection conn,
   chanrec = silc_channel_find_entry(server, channel);
   
   nick = silc_nicklist_find(chanrec, sender);
-  signal_emit("message public", 6, server, msg,
-             nick == NULL ? "[<unknown>]" : nick->nick,
-             nick == NULL ? NULL : nick->host,
-             chanrec->name, nick);
+
+  if (flags & SILC_MESSAGE_FLAG_ACTION)
+    ;
+  else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+    ;
+  else
+    signal_emit("message public", 6, server, msg,
+               nick == NULL ? "[<unknown>]" : nick->nick,
+               nick == NULL ? NULL : nick->host,
+               chanrec->name, nick);
 }
 
 /* Private message to the client. The `sender' is the nickname of the
@@ -299,23 +303,46 @@ void silc_client_join_get_users(SilcClient client,
   SilcChannelUser chu;
   SILC_SERVER_REC *server = conn->context;
   SILC_CHANNEL_REC *chanrec;
+  SilcClientEntry founder = NULL;
   NICK_REC *ownnick;
 
   if (!clients)
     return;
 
-  chanrec = silc_channel_find_entry(server, channel);
+  chanrec = silc_channel_find(server, channel->channel_name);
   if (chanrec == NULL)
     return;
 
   silc_list_start(channel->clients);
-  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END)
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+      founder = chu->client;
     silc_nicklist_insert(chanrec, chu, FALSE);
+  }
 
   ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
   nicklist_set_own(CHANNEL(chanrec), ownnick);
   signal_emit("channel joined", 1, chanrec);
+
+  if (chanrec->topic)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
+                      channel->channel_name, chanrec->topic);
+
   fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
+
+  if (founder) {
+    if (founder == conn->local_entry)
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER_YOU,
+                        channel->channel_name);
+    else
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER,
+                        channel->channel_name, founder->nickname);
+  }
 }
 
 /* Command reply handler. This function is called always in the command reply
@@ -347,179 +374,179 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
   va_start(vp, status);
 
   switch(command) {
-    case SILC_COMMAND_WHOIS:
-      {
-       char buf[1024], *nickname, *username, *realname;
-       int len;
-       uint32 idle, mode;
-       SilcBuffer channels;
-
-       /* XXX should use irssi routines */
-
-       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
-           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-         char *tmp;
-         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
-                                          3, NULL);
-         if (tmp)
-           client->ops->say(client, conn, "%s: %s", tmp,
-                            silc_client_command_status_message(status));
-         else
-           client->ops->say(client, conn, "%s",
-                            silc_client_command_status_message(status));
-         break;
-       }
-
-       if (!success)
-         return;
-
-       (void)va_arg(vp, SilcClientEntry);
-       nickname = va_arg(vp, char *);
-       username = va_arg(vp, char *);
-       realname = va_arg(vp, char *);
-       channels = va_arg(vp, SilcBuffer);
-       mode = va_arg(vp, uint32);
-       idle = va_arg(vp, uint32);
-
-       memset(buf, 0, sizeof(buf));
-
-       if (nickname) {
-         len = strlen(nickname);
-         strncat(buf, nickname, len);
-         strncat(buf, " is ", 4);
-       }
+  case SILC_COMMAND_WHOIS:
+    {
+      char buf[1024], *nickname, *username, *realname;
+      int len;
+      uint32 idle, mode;
+      SilcBuffer channels;
+      
+      /* XXX should use irssi routines */
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+         status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       char *tmp;
+       tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                        3, NULL);
+       if (tmp)
+         client->ops->say(client, conn, "%s: %s", tmp,
+                          silc_client_command_status_message(status));
+       else
+         client->ops->say(client, conn, "%s",
+                          silc_client_command_status_message(status));
+       break;
+      }
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      channels = va_arg(vp, SilcBuffer);
+      mode = va_arg(vp, uint32);
+      idle = va_arg(vp, uint32);
+      
+      memset(buf, 0, sizeof(buf));
+      
+      if (nickname) {
+       len = strlen(nickname);
+       strncat(buf, nickname, len);
+       strncat(buf, " is ", 4);
+      }
        
-       if (username) {
-         strncat(buf, username, strlen(username));
-       }
+      if (username) {
+       strncat(buf, username, strlen(username));
+      }
        
-       if (realname) {
-         strncat(buf, " (", 2);
-         strncat(buf, realname, strlen(realname));
-         strncat(buf, ")", 1);
-       }
-
-       client->ops->say(client, conn, "%s", buf);
-
-       if (channels) {
-         SilcDList list = silc_channel_payload_parse_list(channels);
-         if (list) {
-           SilcChannelPayload entry;
-
-           memset(buf, 0, sizeof(buf));
-           strcat(buf, "on channels: ");
-
-           silc_dlist_start(list);
-           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
-             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
-             uint32 name_len;
-             char *name = silc_channel_get_name(entry, &name_len);
-
-             if (m)
-               strncat(buf, m, strlen(m));
-             strncat(buf, name, name_len);
-             strncat(buf, " ", 1);
-             silc_free(m);
-           }
-
-           client->ops->say(client, conn, "%s", buf);
-           silc_channel_payload_list_free(list);
+      if (realname) {
+       strncat(buf, " (", 2);
+       strncat(buf, realname, strlen(realname));
+       strncat(buf, ")", 1);
+      }
+      
+      client->ops->say(client, conn, "%s", buf);
+      
+      if (channels) {
+       SilcDList list = silc_channel_payload_parse_list(channels);
+       if (list) {
+         SilcChannelPayload entry;
+         
+         memset(buf, 0, sizeof(buf));
+         strcat(buf, "on channels: ");
+         
+         silc_dlist_start(list);
+         while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+           char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
+           uint32 name_len;
+           char *name = silc_channel_get_name(entry, &name_len);
+           
+           if (m)
+             strncat(buf, m, strlen(m));
+           strncat(buf, name, name_len);
+           strncat(buf, " ", 1);
+           silc_free(m);
          }
-       }
 
-       if (mode) {
-         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
-             (mode & SILC_UMODE_ROUTER_OPERATOR))
-           client->ops->say(client, conn, "%s is %s", nickname,
-                            (mode & SILC_UMODE_SERVER_OPERATOR) ?
-                            "Server Operator" :
-                            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
-                            "SILC Operator" : "[Unknown mode]");
-
-         if (mode & SILC_UMODE_GONE)
-           client->ops->say(client, conn, "%s is gone", nickname);
+         client->ops->say(client, conn, "%s", buf);
+         silc_channel_payload_list_free(list);
        }
-
-       if (idle && nickname)
-         client->ops->say(client, conn, "%s has been idle %d %s",
-                          nickname,
-                          idle > 60 ? (idle / 60) : idle,
-                          idle > 60 ? "minutes" : "seconds");
       }
-      break;
-
-    case SILC_COMMAND_WHOWAS:
-      {
-       char buf[1024], *nickname, *username, *realname;
-       int len;
-
-       /* XXX should use irssi routines */
-
-       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
-           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
-         char *tmp;
-         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
-                                          3, NULL);
-         if (tmp)
-           client->ops->say(client, conn, "%s: %s", tmp,
-                            silc_client_command_status_message(status));
-         else
-           client->ops->say(client, conn, "%s",
-                            silc_client_command_status_message(status));
-         break;
-       }
-
-       if (!success)
-         return;
-
-       (void)va_arg(vp, SilcClientEntry);
-       nickname = va_arg(vp, char *);
-       username = va_arg(vp, char *);
-       realname = va_arg(vp, char *);
-
-       memset(buf, 0, sizeof(buf));
-
-       if (nickname) {
-         len = strlen(nickname);
-         strncat(buf, nickname, len);
-         strncat(buf, " was ", 5);
-       }
-       
-       if (username) {
-         strncat(buf, username, strlen(nickname));
-       }
+      
+      if (mode) {
+       if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+           (mode & SILC_UMODE_ROUTER_OPERATOR))
+         client->ops->say(client, conn, "%s is %s", nickname,
+                          (mode & SILC_UMODE_SERVER_OPERATOR) ?
+                          "Server Operator" :
+                          (mode & SILC_UMODE_ROUTER_OPERATOR) ?
+                          "SILC Operator" : "[Unknown mode]");
        
-       if (realname) {
-         strncat(buf, " (", 2);
-         strncat(buf, realname, strlen(realname));
-         strncat(buf, ")", 1);
-       }
-
-       client->ops->say(client, conn, "%s", buf);
+       if (mode & SILC_UMODE_GONE)
+         client->ops->say(client, conn, "%s is gone", nickname);
       }
-      break;
-
-    case SILC_COMMAND_INVITE:
-      {
-       SilcChannelEntry channel;
-       char *invite_list;
-
-       if (!success)
-         return;
-       
-       /* XXX should use irssi routines */
-
-       channel = va_arg(vp, SilcChannelEntry);
-       invite_list = va_arg(vp, char *);
-
-       if (invite_list)
-         silc_say(client, conn, "%s invite list: %s", channel->channel_name,
-                  invite_list);
+      
+      if (idle && nickname)
+       client->ops->say(client, conn, "%s has been idle %d %s",
+                        nickname,
+                        idle > 60 ? (idle / 60) : idle,
+                        idle > 60 ? "minutes" : "seconds");
+    }
+    break;
+    
+  case SILC_COMMAND_WHOWAS:
+    {
+      char buf[1024], *nickname, *username, *realname;
+      int len;
+      
+      /* XXX should use irssi routines */
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+         status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       char *tmp;
+       tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                        3, NULL);
+       if (tmp)
+         client->ops->say(client, conn, "%s: %s", tmp,
+                          silc_client_command_status_message(status));
        else
-         silc_say(client, conn, "%s invite list not set", 
-                  channel->channel_name);
+         client->ops->say(client, conn, "%s",
+                          silc_client_command_status_message(status));
+       break;
       }
-      break;
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      
+      memset(buf, 0, sizeof(buf));
+      
+      if (nickname) {
+       len = strlen(nickname);
+       strncat(buf, nickname, len);
+       strncat(buf, " was ", 5);
+      }
+      
+      if (username) {
+       strncat(buf, username, strlen(nickname));
+      }
+       
+      if (realname) {
+       strncat(buf, " (", 2);
+       strncat(buf, realname, strlen(realname));
+       strncat(buf, ")", 1);
+      }
+      
+      client->ops->say(client, conn, "%s", buf);
+    }
+    break;
+    
+  case SILC_COMMAND_INVITE:
+    {
+      SilcChannelEntry channel;
+      char *invite_list;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      invite_list = va_arg(vp, char *);
+      
+      if (invite_list)
+       silc_say(client, conn, "%s invite list: %s", channel->channel_name,
+                invite_list);
+      else
+       silc_say(client, conn, "%s invite list not set", 
+                channel->channel_name);
+    }
+    break;
 
   case SILC_COMMAND_JOIN: 
     {
@@ -541,8 +568,6 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       list_count = va_arg(vp, uint32);
       client_id_list = va_arg(vp, SilcBuffer);
 
-      /* XXX what an earth do I do with the topic??? */
-
       if (!success)
        return;
 
@@ -552,6 +577,12 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       else if (chanrec == NULL && success)
        chanrec = silc_channel_create(server, channel, TRUE);
       
+      if (topic) {
+       g_free_not_null(chanrec->topic);
+       chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+       signal_emit("channel topic changed", 1, chanrec);
+      }
+
       mode = silc_client_chmode(modei, channel_entry);
       g_free_not_null(chanrec->mode);
       chanrec->mode = g_strdup(mode == NULL ? "" : mode);
@@ -785,8 +816,9 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
       
       if (id_type == SILC_ID_CLIENT) {
-       silc_verify_public_key(client, conn, SILC_SOCKET_TYPE_CLIENT,
-                              pk, pk_len, SILC_SKE_PK_TYPE_SILC);
+       silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
+                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                       NULL, NULL);
       }
       
       silc_free(pk);
@@ -816,25 +848,234 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
   va_end(vp);
 }
 
-/* Verifies received public key. If user decides to trust the key it is
-   saved as public server key for later use. If user does not trust the
-   key this returns FALSE. */
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. */
 
-static int silc_verify_public_key(SilcClient client,
-                                 SilcClientConnection conn, 
-                                 SilcSocketType conn_type,
-                                 unsigned char *pk, uint32 pk_len,
-                                 SilcSKEPKType pk_type)
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  char *filename;
+  char *entity;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcSKEPKType pk_type;
+  SilcVerifyPublicKey completion;
+  void *context;
+} *PublicKeyVerify;
+
+static void verify_public_key_completion(const char *line, void *context)
 {
-  return TRUE;
+  PublicKeyVerify verify = (PublicKeyVerify)context;
+
+  if (line[0] == 'Y' || line[0] == 'y') {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(TRUE, verify->context);
+
+    /* Save the key for future checking */
+    silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
+                                  verify->pk_len, SILC_PKCS_FILE_PEM);
+  } else {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(FALSE, verify->context);
+
+    silc_say(verify->client, 
+            verify->conn, "Will not accept the %s key", verify->entity);
+  }
+
+  silc_free(verify->filename);
+  silc_free(verify->entity);
+  silc_free(verify);
+}
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context)
+{
+  int i;
+  char file[256], filename[256], *fingerprint;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+  PublicKeyVerify verify;
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+            conn->sock->hostname, conn->sock->port);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->client = client;
+  verify->conn = conn;
+  verify->filename = strdup(filename);
+  verify->entity = strdup(entity);
+  verify->pk = pk;
+  verify->pk_len = pk_len;
+  verify->pk_type = pk_type;
+  verify->completion = completion;
+  verify->context = context;
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+    /* Key does not exist, ask user to verify the key and save it */
+
+    silc_say(client, conn, "Received %s public key", entity);
+    silc_say(client, conn, "Fingerprint for the %s key is", entity);
+    silc_say(client, conn, "%s", fingerprint);
+
+    keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                           "Would you like to accept the key (y/n)? ", 0,
+                           verify);
+    silc_free(fingerprint);
+    return;
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       silc_say(client, conn, "Received %s public key", entity);
+       silc_say(client, conn, "Fingerprint for the %s key is", entity);
+       silc_say(client, conn, "%s", fingerprint);
+       silc_say(client, conn, "Could not load your local copy of the %s key",
+                entity);
+       keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                               "Would you like to accept the key "
+                               "anyway (y/n)? ", 0,
+                               verify);
+       silc_free(fingerprint);
+       return;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "Your local copy of the %s key is malformed",
+              entity);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Compare the keys */
+    if (memcmp(encpk, pk, encpk_len)) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "%s key does not match with your local copy",
+              entity);
+      silc_say(client, conn, 
+              "It is possible that the key has expired or changed");
+      silc_say(client, conn, "It is also possible that some one is performing "
+                      "man-in-the-middle attack");
+
+      /* Ask user to verify the key and save it */
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             "Would you like to accept the key "
+                             "anyway (y/n)? ", 0,
+                             verify);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Local copy matched */
+    if (completion)
+      completion(TRUE, context);
+    silc_free(fingerprint);
+  }
+}
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the key may be saved as trusted public key for later use. The 
+   `completion' must be called after the public key has been verified. */
+
+static void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                 pk_len, pk_type,
+                                 completion, context);
 }
 
 /* Asks passphrase from user on the input line. */
 
-static unsigned char *silc_ask_passphrase(SilcClient client,
-                                         SilcClientConnection conn)
+typedef struct {
+  SilcAskPassphrase completion;
+  void *context;
+} *AskPassphrase;
+
+static void ask_passphrase_completion(const char *passphrase, void *context)
+{
+  AskPassphrase p = (AskPassphrase)context;
+  p->completion((unsigned char *)passphrase, 
+               passphrase ? strlen(passphrase) : 0, p->context);
+  silc_free(p);
+}
+
+static void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                               SilcAskPassphrase completion, void *context)
 {
-  return NULL;
+  AskPassphrase p = silc_calloc(1, sizeof(*p));
+  p->completion = completion;
+  p->context = context;
+
+  keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
+                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
 }
 
 /* Find authentication method and authentication data by hostname and
@@ -888,7 +1129,7 @@ silc_failure(SilcClient client, SilcClientConnection conn,
       silc_say_error("Server does not support one of your proposed PKCS");
     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
       silc_say_error("Server does not support one of your proposed "
-                   "hash function");
+                    "hash function");
     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
       silc_say_error("Server does not support one of your proposed HMAC");
     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
@@ -1044,6 +1285,23 @@ static void silc_init_userinfo(void)
   }
 }
 
+/* Log callbacks */
+
+static void silc_log_info(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
+static void silc_log_warning(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
+static void silc_log_error(char *message)
+{
+  fprintf(stderr, "%s\n", message);
+}
+
 /* Init SILC. Called from src/fe-text/silc.c */
 
 void silc_core_init(void)
@@ -1057,6 +1315,8 @@ void silc_core_init(void)
       "Set the length of the public key pair", "VALUE" },
     { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
       "Show the contents of the public key", "FILE" },
+    { "debug", 'd', POPT_ARG_NONE, &opt_debug, 0,
+      "Enable debugging", NULL },
     { NULL, '\0', 0, NULL }
   };
 
@@ -1090,6 +1350,15 @@ void silc_core_init_finish(void)
     exit(0);
   }
 
+  silc_debug = opt_debug;
+  silc_log_set_callbacks(silc_log_info, silc_log_warning,
+                        silc_log_error, NULL);
+
+  /* Do some irssi initializing */
+  settings_add_bool("server", "skip_motd", FALSE);
+  settings_add_str("server", "alternate_nick", NULL);
+  silc_init_userinfo();
+
   /* Allocate SILC client */
   silc_client = silc_client_alloc(&ops, NULL);
 
@@ -1157,12 +1426,11 @@ void silc_core_init_finish(void)
   chat_protocol_register(rec);
   g_free(rec);
 
-  silc_init_userinfo();
   silc_server_init();
   silc_channels_init();
   silc_queries_init();
 
-  idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
+  idletag = g_timeout_add(50, (GSourceFunc) my_silc_scheduler, NULL);
 }
 
 /* Deinit SILC. Called from src/fe-text/silc.c */