Added support for setting specific founder public key in
[silc.git] / apps / irssi / src / silc / core / client_ops.c
index 6fca4be77b433bd3dc5524596ef2f96c95484eee..9a397ddd508b85afc399775cea5dd9ced2b71bd2 100644 (file)
 #include "signals.h"
 #include "levels.h"
 #include "settings.h"
+#include "ignore.h"
 #include "fe-common/core/printtext.h"
 #include "fe-common/core/fe-channels.h"
 #include "fe-common/core/keyboard.h"
+#include "fe-common/core/window-items.h"
 #include "fe-common/silc/module-formats.h"
 
 #include "core.h"
@@ -75,6 +77,51 @@ static void silc_get_umode_string(SilcUInt32 mode, char *buf,
     strcat(buf, " [blocks private messages]");
   if (mode & SILC_UMODE_DETACHED)
     strcat(buf, " [detached]");
+  if (mode & SILC_UMODE_REJECT_WATCHING)
+    strcat(buf, " [rejects watching]");
+  if (mode & SILC_UMODE_BLOCK_INVITE)
+    strcat(buf, " [blocks invites]");
+}
+
+/* print "nick appears as" message to every channel of a server */
+static void 
+silc_print_nick_change_channel(SILC_SERVER_REC *server, const char *channel,
+                             const char *newnick, const char *oldnick,
+                             const char *address)
+{
+  if (ignore_check(SERVER(server), oldnick, address,
+                  channel, newnick, MSGLEVEL_NICKS))
+    return;
+  
+  printformat_module("fe-common/silc", server, channel, MSGLEVEL_NICKS,
+                    SILCTXT_CHANNEL_APPEARS,
+                    oldnick, newnick, channel, address);
+}
+
+static void
+silc_print_nick_change(SILC_SERVER_REC *server, const char *newnick,
+                      const char *oldnick, const char *address)
+{
+  GSList *tmp, *windows;
+
+  /* Print to each channel/query where the nick is.
+     Don't print more than once to the same window. */
+  windows = NULL;
+    
+  for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+    CHANNEL_REC *channel = tmp->data;
+    WINDOW_REC *window = window_item_window((WI_ITEM_REC *) channel);
+
+    if (nicklist_find(channel, newnick) == NULL ||
+       g_slist_find(windows, window) != NULL)
+      continue;
+
+    windows = g_slist_append(windows, window);
+    silc_print_nick_change_channel(server, channel->visible_name,
+                                  newnick, oldnick, address);
+  }
+
+  g_slist_free(windows);
 }
 
 void silc_say(SilcClient client, SilcClientConnection conn,
@@ -138,12 +185,14 @@ void silc_channel_message(SilcClient client, SilcClientConnection conn,
 
   if (flags & SILC_MESSAGE_FLAG_DATA) {
     /* MIME object received, try to display it as well as we can */
-    char type[128];
+    char type[128], enc[128];
     unsigned char *data;
+    SilcUInt32 data_len;
 
     memset(type, 0, sizeof(type));
+    memset(enc, 0, sizeof(enc));
     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
-                        NULL, 0, &data, NULL))
+                        enc, sizeof(enc) - 1, &data, &data_len))
       return;
 
     /* Then figure out what we can display */
@@ -152,6 +201,9 @@ void silc_channel_message(SilcClient client, SilcClientConnection conn,
       /* It is something textual, display it */
       message = (const unsigned char *)data;
     } else {
+      printformat_module("fe-common/silc", server, channel->channel_name,
+                        MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
+                        nick == NULL ? "[<unknown>]" : nick->nick, type);
       message = NULL;
     }
   }
@@ -167,11 +219,32 @@ void silc_channel_message(SilcClient client, SilcClientConnection conn,
     printformat_module("fe-common/silc", server, channel->channel_name,
                       MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
                        nick == NULL ? "[<unknown>]" : nick->nick, message);
-  else
+  else {
+    if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
+      char tmp[256], *cp, *dm = NULL;
+
+      memset(tmp, 0, sizeof(tmp));
+      cp = tmp;
+      if (message_len > sizeof(tmp) - 1) {
+       dm = silc_calloc(message_len + 1, sizeof(*dm));
+       cp = dm;
+      }
+
+      silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
+                      cp, message_len);
+      signal_emit("message public", 6, server, cp,
+                 nick == NULL ? "[<unknown>]" : nick->nick,
+                 nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
+                 chanrec->name, nick);
+      silc_free(dm);
+      return;
+    }
+
     signal_emit("message public", 6, server, message,
                nick == NULL ? "[<unknown>]" : nick->nick,
                nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
                chanrec->name, nick);
+  }
 }
 
 /* Private message to the client. The `sender' is the nickname of the
@@ -195,12 +268,14 @@ void silc_private_message(SilcClient client, SilcClientConnection conn,
 
   if (flags & SILC_MESSAGE_FLAG_DATA) {
     /* MIME object received, try to display it as well as we can */
-    char type[128];
+    char type[128], enc[128];
     unsigned char *data;
+    SilcUInt32 data_len;
 
     memset(type, 0, sizeof(type));
+    memset(enc, 0, sizeof(enc));
     if (!silc_mime_parse(message, message_len, NULL, 0, type, sizeof(type) - 1,
-                        NULL, 0, &data, NULL))
+                        enc, sizeof(enc) - 1, &data, &data_len))
       return;
 
     /* Then figure out what we can display */
@@ -209,6 +284,10 @@ void silc_private_message(SilcClient client, SilcClientConnection conn,
       /* It is something textual, display it */
       message = (const unsigned char *)data;
     } else {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_MESSAGE_DATA,
+                        sender->nickname ? sender->nickname : "[<unknown>]",
+                        type);
       message = NULL;
     }
   }
@@ -216,6 +295,25 @@ void silc_private_message(SilcClient client, SilcClientConnection conn,
   if (!message)
     return;
 
+  if (flags & SILC_MESSAGE_FLAG_UTF8 && !silc_term_utf8()) {
+    char tmp[256], *cp, *dm = NULL;
+
+    memset(tmp, 0, sizeof(tmp));
+    cp = tmp;
+    if (message_len > sizeof(tmp) - 1) {
+      dm = silc_calloc(message_len + 1, sizeof(*dm));
+      cp = dm;
+    }
+
+    silc_utf8_decode(message, message_len, SILC_STRING_LANGUAGE,
+                    cp, message_len);
+    signal_emit("message private", 4, server, cp,
+               sender->nickname ? sender->nickname : "[<unknown>]",
+               sender->username ? userhost : NULL);
+    silc_free(dm);
+    return;
+  }
+
   signal_emit("message private", 4, server, message,
              sender->nickname ? sender->nickname : "[<unknown>]",
              sender->username ? userhost : NULL);
@@ -442,7 +540,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
 
     tmp = silc_client_chmode(mode,
                             channel->channel_key ? 
-                            channel->channel_key->cipher->name : "",
+                            silc_cipher_get_name(channel->channel_key) : "",
                             channel->hmac ? 
                             silc_hmac_get_name(channel->hmac) : "");
     
@@ -534,6 +632,11 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
                         SILCTXT_CHANNEL_FOUNDER,
                         channel->channel_name, client_entry2->nickname);
 
+    if (mode & SILC_CHANNEL_UMODE_QUIET && conn->local_entry == client_entry2)
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_QUIETED, channel->channel_name);
+
     silc_free(tmp);
     break;
 
@@ -777,7 +880,7 @@ void silc_connect(SilcClient client, SilcClientConnection conn,
 {
   SILC_SERVER_REC *server = conn->context;
 
-  if (!server) {
+  if (!server || server->disconnected) {
     silc_client_close_connection(client, conn);
     return;
   }
@@ -819,7 +922,8 @@ void silc_connect(SilcClient client, SilcClientConnection conn,
 
 /* Called to indicate that connection was disconnected to the server. */
 
-void silc_disconnect(SilcClient client, SilcClientConnection conn)
+void silc_disconnect(SilcClient client, SilcClientConnection conn,
+                    SilcStatus status, const char *message)
 {
   SILC_SERVER_REC *server = conn->context;
 
@@ -836,6 +940,12 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn)
     silc_change_nick(server, silc_client->username);
   }
 
+  if (message)
+    silc_say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+            "Server closed connection: %s (%d) %s",
+            silc_get_status_message(status), status,
+            message ? message : "");
+
   server->conn->context = NULL;
   server->conn = NULL;
   server->connection_lost = TRUE;
@@ -859,18 +969,27 @@ void silc_command(SilcClient client, SilcClientConnection conn,
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (!success)
+  if (!success) {
+    silc_say_error("%s", silc_get_status_message(status));
     return;
+  }
+
+  switch (command) {
 
-  switch(command) {
   case SILC_COMMAND_INVITE:
-    printformat_module("fe-common/silc", server, NULL,
-                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
-                      cmd_context->argv[2], 
-                      (cmd_context->argv[1][0] == '*' ?
-                       (char *)conn->current_channel->channel_name :
-                       (char *)cmd_context->argv[1]));
+    if (cmd_context->argc > 2)
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
+                        cmd_context->argv[2], 
+                        (cmd_context->argv[1][0] == '*' ?
+                         (char *)conn->current_channel->channel_name :
+                         (char *)cmd_context->argv[1]));
     break;
+
+  case SILC_COMMAND_DETACH:
+    server->no_reconnect = TRUE;
+    break;
+
   default:
     break;
   }
@@ -923,8 +1042,6 @@ static void silc_client_join_get_users(SilcClient client,
                       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", 
@@ -957,10 +1074,11 @@ void silc_getkey_cb(bool success, void *context)
 
   if (success) {
     printformat_module("fe-common/silc", NULL, NULL,
-                      MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
+                      MSGLEVEL_CRAP, SILCTXT_PUBKEY_VERIFIED, entity, name);
   } else {
     printformat_module("fe-common/silc", NULL, NULL,
-                      MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
+                      MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOTVERIFIED,
+                      entity, name);
   }
 
   silc_free(getkey->fingerprint);
@@ -1005,6 +1123,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       SilcUInt32 idle, mode;
       SilcBuffer channels, user_modes;
       SilcClientEntry client_entry;
+      SilcDList attrs;
       
       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
        /* Print the unknown nick for user */
@@ -1035,11 +1154,11 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
          }
        }
        break;
-      }
-      
-      if (!success)
+      } else if (!success) {
+       silc_say_error("WHOIS: %s", silc_get_status_message(status));
        return;
-      
+      }
+
       client_entry = va_arg(vp, SilcClientEntry);
       nickname = va_arg(vp, char *);
       username = va_arg(vp, char *);
@@ -1049,6 +1168,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       idle = va_arg(vp, SilcUInt32);
       fingerprint = va_arg(vp, unsigned char *);
       user_modes = va_arg(vp, SilcBuffer);
+      attrs = va_arg(vp, SilcDList);
       
       silc_parse_userfqdn(nickname, &nick, NULL);
       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
@@ -1112,6 +1232,10 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
                           SILCTXT_WHOIS_FINGERPRINT, fingerprint);
        silc_free(fingerprint);
       }
+
+      if (attrs)
+       silc_query_attributes_print(server, silc_client, conn, attrs,
+                                   client_entry);
     }
     break;
     
@@ -1166,10 +1290,10 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
          silc_say_error("%s: %s", tmp, 
                         silc_get_status_message(status));
        break;
-      }
-      
-      if (!success)
+      } else if (!success) {
+       silc_say_error("WHOWAS: %s", silc_get_status_message(status));
        return;
+      }
       
       (void)va_arg(vp, SilcClientEntry);
       nickname = va_arg(vp, char *);
@@ -1235,7 +1359,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
 
       chanrec = silc_channel_find(server, channel);
       if (!chanrec)
-       chanrec = silc_channel_create(server, channel, TRUE);
+       chanrec = silc_channel_create(server, channel, channel, TRUE);
 
       if (topic) {
        g_free_not_null(chanrec->topic);
@@ -1245,7 +1369,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
 
       mode = silc_client_chmode(modei, 
                                channel_entry->channel_key ? 
-                               channel_entry->channel_key->cipher->name : "",
+                               silc_cipher_get_name(channel_entry->
+                                                    channel_key) : "",
                                channel_entry->hmac ? 
                                silc_hmac_get_name(channel_entry->hmac) : "");
       g_free_not_null(chanrec->mode);
@@ -1263,16 +1388,37 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
   case SILC_COMMAND_NICK: 
     {
       char *old;
-      SilcClientEntry client = va_arg(vp, SilcClientEntry);
+      SilcClientEntry client_entry = va_arg(vp, SilcClientEntry);
+      GSList *nicks;
       
       if (!success)
        return;
 
+      nicks = nicklist_get_same(SERVER(server), client_entry->nickname);
+      if (nicks != NULL) {
+       char buf[512];
+       SilcClientEntry collider, old;
+
+       old = ((SILC_NICK_REC *)(nicks->next->data))->silc_user->client;
+       collider = silc_client_get_client_by_id(client, conn,
+                                               old->id);
+       
+        memset(buf, 0, sizeof(buf));
+        snprintf(buf, sizeof(buf) - 1, "%s@%s",
+                collider->username, collider->hostname);
+       nicklist_rename_unique(SERVER(server),
+                              old, old->nickname,
+                              collider, collider->nickname);
+       silc_print_nick_change(server, collider->nickname,
+                              client_entry->nickname, buf);
+       g_slist_free(nicks);
+      }
+
       old = g_strdup(server->nick);
-      server_change_nick(SERVER(server), client->nickname);
+      server_change_nick(SERVER(server), client_entry->nickname);
       nicklist_rename_unique(SERVER(server),
                             server->conn->local_entry, server->nick,
-                            client, client->nickname);
+                            client_entry, client_entry->nickname);
       signal_emit("message own_nick", 4, server, server->nick, old, "");
       g_free(old);
       break;
@@ -1283,6 +1429,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       char *topic, *name;
       int usercount;
       char users[20];
+      char tmp[256], *cp, *dm = NULL;
       
       if (!success)
        return;
@@ -1291,6 +1438,20 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       name = va_arg(vp, char *);
       topic = va_arg(vp, char *);
       usercount = va_arg(vp, int);
+
+      if (topic && !silc_term_utf8() &&
+         silc_utf8_valid(topic, strlen(topic))) {
+       memset(tmp, 0, sizeof(tmp));
+       cp = tmp;
+       if (strlen(topic) > sizeof(tmp) - 1) {
+         dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
+         cp = dm;
+       }
+
+       silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
+                        cp, strlen(topic));
+       topic = cp;
+      }
       
       if (status == SILC_STATUS_LIST_START ||
          status == SILC_STATUS_OK)
@@ -1304,12 +1465,14 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
       printformat_module("fe-common/silc", server, NULL,
                         MSGLEVEL_CRAP, SILCTXT_LIST,
                         name, users, topic ? topic : "");
+      silc_free(dm);
     }
     break;
     
   case SILC_COMMAND_UMODE:
     {
       SilcUInt32 mode;
+      char *reason;
       
       if (!success)
        return;
@@ -1326,6 +1489,20 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
        printformat_module("fe-common/silc", server, NULL,
                           MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
 
+      if ((mode & SILC_UMODE_GONE) != (server->umode & SILC_UMODE_GONE)) {
+       if (mode & SILC_UMODE_GONE) {      
+         if ((server->away_reason != NULL) && (server->away_reason[0] != '\0'))
+           reason = g_strdup(server->away_reason);
+         else
+           reason = g_strdup("away");
+       } else
+         reason = g_strdup("");
+
+       silc_set_away(reason, server);
+
+       g_free(reason);
+      }
+
       server->umode = mode;
       signal_emit("user mode changed", 2, server, NULL);
     }
@@ -1472,7 +1649,7 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
        silc_free(pk);
       } else {
        printformat_module("fe-common/silc", server, NULL,
-                          MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
+                          MSGLEVEL_CRAP, SILCTXT_PUBKEY_NOKEY);
       }
     }
     break;
@@ -1502,13 +1679,28 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
     {
       SilcChannelEntry channel;
       char *topic;
+      char tmp[256], *cp, *dm = NULL;
       
       if (!success)
        return;
       
       channel = va_arg(vp, SilcChannelEntry);
       topic = va_arg(vp, char *);
-      
+
+      if (topic && !silc_term_utf8() &&
+         silc_utf8_valid(topic, strlen(topic))) {
+       memset(tmp, 0, sizeof(tmp));
+       cp = tmp;
+       if (strlen(topic) > sizeof(tmp) - 1) {
+         dm = silc_calloc(strlen(topic) + 1, sizeof(*dm));
+         cp = dm;
+       }
+
+       silc_utf8_decode(topic, strlen(topic), SILC_STRING_LANGUAGE,
+                        cp, strlen(topic));
+       topic = cp;
+      }
+
       if (topic) {
        chanrec = silc_channel_find_entry(server, channel);
        if (chanrec) {
@@ -1524,11 +1716,141 @@ silc_command_reply(SilcClient client, SilcClientConnection conn,
                           MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
                           channel->channel_name);
       }
+      silc_free(dm);
     }
     break;
 
   case SILC_COMMAND_WATCH:
     break;
+  
+  case SILC_COMMAND_STATS:
+    {
+      SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
+                my_router_ops, cell_clients, cell_channels, cell_servers,
+                clients, channels, servers, routers, server_ops, router_ops;
+      SilcUInt32 buf_len;
+      SilcBufferStruct buf;
+      unsigned char *tmp_buf;
+      char tmp[40];
+      const char *tmptime;
+      int days, hours, mins, secs;
+
+      if (!success)
+       return;
+
+      tmp_buf = va_arg(vp, unsigned char *);
+      buf_len = va_arg(vp, SilcUInt32);
+
+      if (!tmp_buf || !buf_len) {
+       printtext(server, NULL, MSGLEVEL_CRAP, "No statistics available");
+       return;
+      }
+
+      /* Get statistics structure */
+      silc_buffer_set(&buf, tmp_buf, buf_len);
+      silc_buffer_unformat(&buf,
+                          SILC_STR_UI_INT(&starttime),
+                          SILC_STR_UI_INT(&uptime),
+                          SILC_STR_UI_INT(&my_clients),
+                          SILC_STR_UI_INT(&my_channels),
+                          SILC_STR_UI_INT(&my_server_ops),
+                          SILC_STR_UI_INT(&my_router_ops),
+                          SILC_STR_UI_INT(&cell_clients),
+                          SILC_STR_UI_INT(&cell_channels),
+                          SILC_STR_UI_INT(&cell_servers),
+                          SILC_STR_UI_INT(&clients),
+                          SILC_STR_UI_INT(&channels),
+                          SILC_STR_UI_INT(&servers),
+                          SILC_STR_UI_INT(&routers),
+                          SILC_STR_UI_INT(&server_ops),
+                          SILC_STR_UI_INT(&router_ops),
+                          SILC_STR_END);
+
+      tmptime = silc_get_time(starttime);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local server start time", tmptime);
+
+      days = uptime / (24 * 60 * 60);
+      uptime -= days * (24 * 60 * 60);
+      hours = uptime / (60 * 60);
+      uptime -= hours * (60 * 60);
+      mins = uptime / 60;
+      uptime -= mins * 60;
+      secs = uptime;
+      snprintf(tmp, sizeof(tmp) - 1, "%d days %d hours %d mins %d secs",
+              days, hours, mins, secs);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local server uptime", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_clients);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local server clients", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_channels);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local server channels", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_server_ops);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local server operators", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)my_router_ops);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local router operators", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_clients);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local cell clients", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_channels);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local cell channels", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)cell_servers);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Local cell servers", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)clients);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Total clients", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)channels);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Total channels", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)servers);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Total servers", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)routers);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Total routers", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)server_ops);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                          "Total server operators", tmp);
+
+      snprintf(tmp, sizeof(tmp) - 1, "%d", (int)router_ops);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_STATS,
+                        "Total router operators", tmp);
+    }
+    break;
+
   }
 
   va_end(vp);
@@ -1669,8 +1991,7 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
   verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
                         (name ? strdup(name) : strdup(conn->sock->hostname))
                         : NULL);
-  verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
-  memcpy(verify->pk, pk, pk_len);
+  verify->pk = silc_memdup(pk, pk_len);
   verify->pk_len = pk_len;
   verify->pk_type = pk_type;
   verify->completion = completion;
@@ -1778,6 +2099,11 @@ silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
     if (completion)
       completion(TRUE, context);
     silc_free(fingerprint);
+    silc_free(verify->filename);
+    silc_free(verify->entity);
+    silc_free(verify->entity_name);
+    silc_free(verify->pk);
+    silc_free(verify);
   }
 }
 
@@ -1849,9 +2175,20 @@ static void silc_get_auth_method_callback(SilcClient client,
     (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
     break;
   case SILC_AUTH_PASSWORD:
-    /* Do not ask the passphrase from user, the library will ask it if
-       we do not provide it here. */
-    (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+    {
+      /* Check whether we find the password for this server in our
+        configuration.  If not, then don't provide so library will ask
+        it from the user. */
+      SERVER_SETUP_REC *setup = server_setup_find_port(conn->remote_host,
+                                                      conn->remote_port);
+      if (!setup || !setup->password) {
+       (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+       break;
+      }
+      
+      (*internal->completion)(TRUE, auth_meth, setup->password,
+                             strlen(setup->password), internal->context);
+    }
     break;
   case SILC_AUTH_PUBLIC_KEY:
     /* Do not get the authentication data now, the library will generate
@@ -1879,9 +2216,6 @@ void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* XXX must resolve from configuration whether this connection has
-     any specific authentication data */
-
   /* If we do not have this connection configured by the user in a
      configuration file then resolve the authentication method from the
      server for this session. */
@@ -1954,10 +2288,10 @@ void silc_failure(SilcClient client, SilcClientConnection conn,
    desired (application may start it later by calling the function
    silc_client_perform_key_agreement). */
 
-int silc_key_agreement(SilcClient client, SilcClientConnection conn,
-                      SilcClientEntry client_entry, const char *hostname,
-                      SilcUInt16 port, SilcKeyAgreementCallback *completion,
-                      void **context)
+bool silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                       SilcClientEntry client_entry, const char *hostname,
+                       SilcUInt16 port, SilcKeyAgreementCallback *completion,
+                       void **context)
 {
   char portstr[12];
 
@@ -1996,18 +2330,29 @@ void silc_ftp(SilcClient client, SilcClientConnection conn,
 {
   SILC_SERVER_REC *server;
   char portstr[12];
-  FtpSession ftp = silc_calloc(1, sizeof(*ftp));
+  FtpSession ftp = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
   server = conn->context;
 
-  ftp->client_entry = client_entry;
-  ftp->session_id = session_id;
-  ftp->send = FALSE;
-  ftp->conn = conn;
-  silc_dlist_add(server->ftp_sessions, ftp);
-  server->current_session = ftp;
+  silc_dlist_start(server->ftp_sessions);
+  while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+    if (ftp->client_entry == client_entry &&
+       ftp->session_id == session_id) {
+      server->current_session = ftp;
+      break;
+    }
+  }
+  if (ftp == SILC_LIST_END) {
+    ftp = silc_calloc(1, sizeof(*ftp));
+    ftp->client_entry = client_entry;
+    ftp->session_id = session_id;
+    ftp->send = FALSE;
+    ftp->conn = conn;
+    silc_dlist_add(server->ftp_sessions, ftp);
+    server->current_session = ftp;
+  }
 
   if (hostname) 
     snprintf(portstr, sizeof(portstr) - 1, "%d", port);