updates.
[silc.git] / apps / irssi / src / silc / core / silc-core.c
index ddd39e409d7445c44e422904bf306ded90e93532..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,12 +52,65 @@ 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;
 uint32 sims_count = 0;
 #endif
 
+static void silc_say(SilcClient client, SilcClientConnection conn,
+                    char *msg, ...);
+static void 
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcChannelEntry channel,
+                    SilcMessageFlags flags, char *msg);
+static void 
+silc_private_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcMessageFlags flags,
+                    char *msg);
+static void silc_notify(SilcClient client, SilcClientConnection conn,
+                       SilcNotifyType type, ...);
+static void 
+silc_connect(SilcClient client, SilcClientConnection conn, int success);
+static void 
+silc_disconnect(SilcClient client, SilcClientConnection conn);
+static void 
+silc_command(SilcClient client, SilcClientConnection conn, 
+            SilcClientCommandContext cmd_context, int success,
+            SilcCommand command);
+static void 
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+                  SilcCommandPayload cmd_payload, int success,
+                  SilcCommand command, SilcCommandStatus status, ...);
+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,
+                    SilcProtocolAuthMeth *auth_meth,
+                    unsigned char **auth_data,
+                    uint32 *auth_data_len);
+static void 
+silc_failure(SilcClient client, SilcClientConnection conn, 
+            SilcProtocol protocol, void *failure);
+static int 
+silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                  SilcClientEntry client_entry, char *hostname,
+                  int port,
+                  SilcKeyAgreementCallback *completion,
+                  void **context);
+
 static void silc_say(SilcClient client, SilcClientConnection conn,
                     char *msg, ...)
 {
@@ -76,7 +122,20 @@ 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);
+}
+
+static void silc_say_error(char *msg, ...)
+{
+  va_list va;
+  char *str;
+
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
+
   g_free(str);
   va_end(va);
 }
@@ -97,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
@@ -115,7 +180,7 @@ silc_private_message(SilcClient client, SilcClientConnection conn,
   
   server = conn == NULL ? NULL : conn->context;
   signal_emit("message private", 4, server, msg,
-             sender->nickname ? sender->nickname : "(unknown)",
+             sender->nickname ? sender->nickname : "[<unknown>]",
              sender->username ? sender->username : NULL);
 }
 
@@ -140,10 +205,16 @@ static NOTIFY_REC notifies[] = {
   { SILC_NOTIFY_TYPE_LEAVE,            "leave" },
   { SILC_NOTIFY_TYPE_SIGNOFF,          "signoff" },
   { SILC_NOTIFY_TYPE_TOPIC_SET,                "topic" },
-  { SILC_NOTIFY_TYPE_NICK_CHANGE,              "nick" },
+  { SILC_NOTIFY_TYPE_NICK_CHANGE,      "nick" },
   { SILC_NOTIFY_TYPE_CMODE_CHANGE,     "cmode" },
   { SILC_NOTIFY_TYPE_CUMODE_CHANGE,    "cumode" },
-  { SILC_NOTIFY_TYPE_MOTD,             "motd" }
+  { SILC_NOTIFY_TYPE_MOTD,             "motd" },
+  { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,   "channel_change" },
+  { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,   "server_signoff" },
+  { SILC_NOTIFY_TYPE_KICKED,           "kick" },
+  { SILC_NOTIFY_TYPE_KILLED,           "kill" },
+  { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
+  { SILC_NOTIFY_TYPE_BAN,               "ban" },
 };
 
 static void silc_notify(SilcClient client, SilcClientConnection conn,
@@ -156,21 +227,18 @@ static void silc_notify(SilcClient client, SilcClientConnection conn,
   va_start(va, type);
   
   if (type == SILC_NOTIFY_TYPE_NONE) {
-    /* some generic notice from server */
-    printtext(server, NULL, MSGLEVEL_CRAP, "%s",
-             (char *) va_arg(va, char *));
+    /* Some generic notice from server */
+    printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
   } else if (type < MAX_NOTIFY) {
-    /* send signal about the notify event */
+    /* Send signal about the notify event */
     char signal[50];
-    
-    g_snprintf(signal, sizeof(signal), "silc event %s",
-              notifies[type].name);
+    g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
     signal_emit(signal, 2, server, va);
   } else {
-    /* unknown notify */
-    printtext(server, NULL, MSGLEVEL_CRAP,
-             "Unknown notify %d", type);
+    /* Unknown notify */
+    printtext(server, NULL, MSGLEVEL_CRAP, "Unknown notify type %d", type);
   }
+
   va_end(va);
 }
 
@@ -182,7 +250,7 @@ static void
 silc_connect(SilcClient client, SilcClientConnection conn, int success)
 {
   SILC_SERVER_REC *server = conn->context;
-  
+
   if (success) {
     server->connected = TRUE;
     signal_emit("event connected", 1, server);
@@ -198,12 +266,12 @@ silc_connect(SilcClient client, SilcClientConnection conn, int success)
 static void 
 silc_disconnect(SilcClient client, SilcClientConnection conn)
 {
-       SILC_SERVER_REC *server = conn->context;
+  SILC_SERVER_REC *server = conn->context;
 
-       server->conn->context = NULL;
-       server->conn = NULL;
-       server->connection_lost = TRUE;
-       server_disconnect(SERVER(server));
+  server->conn->context = NULL;
+  server->conn = NULL;
+  server->connection_lost = TRUE;
+  server_disconnect(SERVER(server));
 }
 
 /* Command handler. This function is called always in the command function.
@@ -222,6 +290,61 @@ silc_command(SilcClient client, SilcClientConnection conn,
 {
 }
 
+/* Client info resolving callback when JOIN command reply is received.
+   This will cache all users on the channel. */
+
+void silc_client_join_get_users(SilcClient client,
+                               SilcClientConnection conn,
+                               SilcClientEntry *clients,
+                               uint32 clients_count,
+                               void *context)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+  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(server, channel->channel_name);
+  if (chanrec == NULL)
+    return;
+
+  silc_list_start(channel->clients);
+  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
    function. If error occurs it will be called as well. Normal scenario
    is that it will be called after the received command data has been parsed
@@ -246,99 +369,713 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
 {
   SILC_SERVER_REC *server = conn->context;
   SILC_CHANNEL_REC *chanrec;
-  va_list va;
-
-  va_start(va, status);
+  va_list vp;
 
-  /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
-    silc_commands[type]);
-    signal_emit(signal, 2, server, va);*/
+  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);
+      }
+       
+      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 (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);
+      }
+      
+      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 (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: 
     {
-      char *channel, *mode;
+      char *channel, *mode, *topic;
       uint32 modei;
       SilcChannelEntry channel_entry;
-      
-      channel = va_arg(va, char *);
-      channel_entry = va_arg(va, SilcChannelEntry);
-      modei = va_arg(va, uint32);
-      mode = silc_client_chmode(modei, channel_entry);
-      
+      SilcBuffer client_id_list;
+      uint32 list_count;
+
+      channel = va_arg(vp, char *);
+      channel_entry = va_arg(vp, SilcChannelEntry);
+      modei = va_arg(vp, uint32);
+      (void)va_arg(vp, uint32);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      topic = va_arg(vp, char *);
+      (void)va_arg(vp, unsigned char *);
+      list_count = va_arg(vp, uint32);
+      client_id_list = va_arg(vp, SilcBuffer);
+
+      if (!success)
+       return;
+
       chanrec = silc_channel_find(server, channel);
       if (chanrec != NULL && !success)
        channel_destroy(CHANNEL(chanrec));
       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);
       signal_emit("channel mode changed", 1, chanrec);
+
+      /* Resolve the client information */
+      silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
+                                     silc_client_join_get_users, 
+                                     channel_entry);
       break;
     }
+
   case SILC_COMMAND_NICK: 
     {
-      SilcClientEntry client = va_arg(va, SilcClientEntry);
+      SilcClientEntry client = va_arg(vp, SilcClientEntry);
       char *old;
       
+      if (!success)
+       return;
+
       old = g_strdup(server->nick);
       server_change_nick(SERVER(server), client->nickname);
       nicklist_rename_unique(SERVER(server),
                             server->conn->local_entry, server->nick,
                             client, client->nickname);
       
-      signal_emit("message own_nick", 4,
-                 server, server->nick, old, "");
+      signal_emit("message own_nick", 4, server, server->nick, old, "");
       g_free(old);
       break;
     }
+    
+  case SILC_COMMAND_LIST:
+    {
+      char *topic, *name;
+      int usercount;
+      unsigned char buf[256], tmp[16];
+      int i, len;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+      
+      (void)va_arg(vp, SilcChannelEntry);
+      name = va_arg(vp, char *);
+      topic = va_arg(vp, char *);
+      usercount = va_arg(vp, int);
+      
+      if (status == SILC_STATUS_LIST_START ||
+         status == SILC_STATUS_OK)
+       silc_say(client, conn, 
+                "  Channel                                  Users     Topic");
+      
+      memset(buf, 0, sizeof(buf));
+      strncat(buf, "  ", 2);
+      len = strlen(name);
+      strncat(buf, name, len > 40 ? 40 : len);
+      if (len < 40)
+       for (i = 0; i < 40 - len; i++)
+         strcat(buf, " ");
+      strcat(buf, " ");
+      
+      memset(tmp, 0, sizeof(tmp));
+      if (usercount) {
+       snprintf(tmp, sizeof(tmp), "%d", usercount);
+       strcat(buf, tmp);
+      }
+      len = strlen(tmp);
+      if (len < 10)
+       for (i = 0; i < 10 - len; i++)
+         strcat(buf, " ");
+      strcat(buf, " ");
+      
+      if (topic) {
+       len = strlen(topic);
+       strncat(buf, topic, len);
+      }
+      
+      silc_say(client, conn, "%s", buf);
+    }
+    break;
+    
+  case SILC_COMMAND_UMODE:
+    {
+      uint32 mode;
+      
+      if (!success)
+       return;
+      
+      mode = va_arg(vp, uint32);
+      
+      /* XXX todo */
+    }
+    break;
+    
+  case SILC_COMMAND_OPER:
+#if 0
+    if (status == SILC_STATUS_OK) {
+      conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
+      if (app->screen->bottom_line->umode)
+       silc_free(app->screen->bottom_line->umode);
+      app->screen->bottom_line->umode = strdup("Server Operator");;
+      silc_screen_print_bottom_line(app->screen, 0);
+    }
+#endif
+    break;
+    
+  case SILC_COMMAND_SILCOPER:
+#if 0
+    if (status == SILC_STATUS_OK) {
+      conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
+      if (app->screen->bottom_line->umode)
+       silc_free(app->screen->bottom_line->umode);
+      app->screen->bottom_line->umode = strdup("SILC Operator");;
+      silc_screen_print_bottom_line(app->screen, 0);
+    }
+#endif
+    break;
+    
   case SILC_COMMAND_USERS: 
     {
       SilcChannelEntry channel;
-      SilcChannelUser user;
-      NICK_REC *ownnick;
+      SilcChannelUser chu;
+      int line_len;
+      char *line;
       
-      channel = va_arg(va, SilcChannelEntry);
-      chanrec = silc_channel_find_entry(server, channel);
-      if (chanrec == NULL)
-       break;
+      if (!success)
+       return;
       
+      channel = va_arg(vp, SilcChannelEntry);
+      
+      /* There are two ways to do this, either parse the list (that
+        the command_reply sends (just take it with va_arg()) or just
+        traverse the channel's client list.  I'll do the latter.  See
+        JOIN command reply for example for the list. */
+      
+      silc_say(client, conn, "Users on %s", channel->channel_name);
+       
+      line = silc_calloc(1024, sizeof(*line));
+      line_len = 1024;
       silc_list_start(channel->clients);
-      while ((user = silc_list_get(channel->clients)) != NULL)
-       silc_nicklist_insert(chanrec, user, FALSE);
-      
-      ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
-      nicklist_set_own(CHANNEL(chanrec), ownnick);
-      signal_emit("channel joined", 1, chanrec);
-      fe_channels_nicklist(CHANNEL(chanrec),
-                          CHANNEL_NICKLIST_FLAG_ALL);
-      break;
+      while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+       SilcClientEntry e = chu->client;
+       int i, len1;
+       char *m, tmp[80];
+       
+       memset(line, 0, line_len);
+       
+       if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
+         silc_free(line);
+         line_len += strlen(e->nickname) + strlen(e->server) + 100;
+         line = silc_calloc(line_len, sizeof(*line));
+       }
+       
+       memset(tmp, 0, sizeof(tmp));
+       m = silc_client_chumode_char(chu->mode);
+       
+       strncat(line, " ", 1);
+       strncat(line, e->nickname, strlen(e->nickname));
+       strncat(line, e->server ? "@" : "", 1);
+       
+       len1 = 0;
+       if (e->server)
+         len1 = strlen(e->server);
+       strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
+       
+       len1 = strlen(line);
+       if (len1 >= 30) {
+         memset(&line[29], 0, len1 - 29);
+       } else {
+         for (i = 0; i < 30 - len1 - 1; i++)
+           strcat(line, " ");
+       }
+       
+       if (e->mode & SILC_UMODE_GONE)
+         strcat(line, "  G");
+       else
+         strcat(line, "  H");
+       strcat(tmp, m ? m : "");
+       strncat(line, tmp, strlen(tmp));
+       
+       if (strlen(tmp) < 5)
+         for (i = 0; i < 5 - strlen(tmp); i++)
+           strcat(line, " ");
+       
+       strcat(line, e->username ? e->username : "");
+       
+       silc_say(client, conn, "%s", line);
+       
+       if (m)
+         silc_free(m);
+      }
+      
+      silc_free(line);
     }
+    break;
+
+  case SILC_COMMAND_BAN:
+    {
+      SilcChannelEntry channel;
+      char *ban_list;
+      
+      if (!success)
+       return;
+      
+      /* XXX should use irssi routines */
+          
+      channel = va_arg(vp, SilcChannelEntry);
+      ban_list = va_arg(vp, char *);
+      
+      if (ban_list)
+       silc_say(client, conn, "%s ban list: %s", channel->channel_name,
+                ban_list);
+      else
+       silc_say(client, conn, "%s ban list not set", channel->channel_name);
+    }
+    break;
+    
+  case SILC_COMMAND_GETKEY:
+    {
+      SilcIdType id_type;
+      void *entry;
+      SilcPublicKey public_key;
+      unsigned char *pk;
+      uint32 pk_len;
+      
+      id_type = va_arg(vp, uint32);
+      entry = va_arg(vp, void *);
+      public_key = va_arg(vp, SilcPublicKey);
+      
+      pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+      
+      if (id_type == SILC_ID_CLIENT) {
+       silc_verify_public_key_internal(client, conn, SILC_SOCKET_TYPE_CLIENT,
+                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                       NULL, NULL);
+      }
+      
+      silc_free(pk);
+    }
+    
+  case SILC_COMMAND_TOPIC:
+    {
+      SilcChannelEntry channel;
+      char *topic;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      topic = va_arg(vp, char *);
+      
+      /* XXX should use irssi routines */
+      
+      if (topic)
+       silc_say(client, conn, 
+                "Topic on channel %s: %s", channel->channel_name,
+                topic);
+    }
+    break;
+  }
+
+  va_end(vp);
+}
+
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. */
+
+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)
+{
+  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;
+      }
   
-  va_end(va);
+    /* 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. 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. */
+/* 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 int silc_verify_public_key(SilcClient client,
-                                 SilcClientConnection conn, 
-                                 SilcSocketType conn_type,
-                                 unsigned char *pk, uint32 pk_len,
-                                 SilcSKEPKType pk_type)
+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)
 {
-  return TRUE;
+  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)
 {
-  return NULL;
+  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)
+{
+  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
@@ -354,7 +1091,15 @@ silc_get_auth_method(SilcClient client, SilcClientConnection conn,
                     unsigned char **auth_data,
                     uint32 *auth_data_len)
 {
-  return FALSE;
+
+  /* XXX must resolve from configuration whether this connection has
+     any specific authentication data */
+
+  *auth_meth = SILC_AUTH_NONE;
+  *auth_data = NULL;
+  *auth_data_len = 0;
+
+  return TRUE;
 }
 
 /* Notifies application that failure packet was received.  This is called
@@ -372,28 +1117,23 @@ silc_failure(SilcClient client, SilcClientConnection conn,
     SilcSKEStatus status = (SilcSKEStatus)failure;
     
     if (status == SILC_SKE_STATUS_BAD_VERSION)
-      silc_say(client, conn, 
-              "You are running incompatible client version (it may be "
-              "too old or too new)");
+      silc_say_error("You are running incompatible client version (it may be "
+                    "too old or too new)");
     if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
-      silc_say(client, conn, "Server does not support your public key type");
+      silc_say_error("Server does not support your public key type");
     if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
-      silc_say(client, conn, 
-              "Server does not support one of your proposed KE group");
+      silc_say_error("Server does not support one of your proposed KE group");
     if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
-      silc_say(client, conn, 
-              "Server does not support one of your proposed cipher");
+      silc_say_error("Server does not support one of your proposed cipher");
     if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
-      silc_say(client, conn, 
-              "Server does not support one of your proposed PKCS");
+      silc_say_error("Server does not support one of your proposed PKCS");
     if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
-      silc_say(client, conn, 
-              "Server does not support one of your proposed hash function");
+      silc_say_error("Server does not support one of your proposed "
+                    "hash function");
     if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
-      silc_say(client, conn, 
-              "Server does not support one of your proposed HMAC");
+      silc_say_error("Server does not support one of your proposed HMAC");
     if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
-      silc_say(client, conn, "Incorrect signature");
+      silc_say_error("Incorrect signature");
   }
 
   if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
@@ -545,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)
@@ -558,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 }
   };
 
@@ -591,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);
 
@@ -658,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 */