Integer type name change.
[silc.git] / apps / silc / client_ops.c
index e7c3b7f655beb289a9c5710ae85903e3e238d374..19dbb519c25741e10976790412e86d85af51061d 100644 (file)
 
 #include "clientincludes.h"
 
+static bool 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               SilcUInt32 pk_len, SilcSKEPKType pk_type)
+{
+  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");
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    return FALSE;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw)
+    return FALSE;
+
+  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);
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+
+    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);
+
+    /* Ask user to verify the key and save it */
+    if (silc_client_ask_yes_no(client, 
+       "Would you like to accept the key (y/n)? "))
+      {
+       /* Save the key for future checking */
+       silc_pkcs_save_public_key_data(filename, pk, pk_len, 
+                                      SILC_PKCS_FILE_PEM);
+       silc_free(fingerprint);
+       return TRUE;
+      }
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    SilcUInt32 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);
+       if (silc_client_ask_yes_no(client, 
+          "Would you like to accept the key anyway (y/n)? "))
+         {
+           /* Save the key for future checking */
+           unlink(filename);
+           silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                          SILC_PKCS_FILE_PEM);
+           silc_free(fingerprint);
+           return TRUE;
+         }
+       
+       silc_free(fingerprint);
+       return FALSE;
+      }
+  
+    /* 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);
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    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 */
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_say(client, conn, "Will not accept the %s key", entity);
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    /* Local copy matched */
+    silc_free(fingerprint);
+    return TRUE;
+  }
+
+  silc_say(client, conn, "Will not accept the %s key", entity);
+  silc_free(fingerprint);
+  return FALSE;
+}
+
+void silc_say(SilcClient client, SilcClientConnection conn, 
+             char *msg, ...)
+{
+  va_list vp;
+  char message[2048];
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  memset(message, 0, sizeof(message));
+  strncat(message, "\n***  ", 5);
+
+  va_start(vp, msg);
+  vsprintf(message + 5, msg, vp);
+  va_end(vp);
+  
+  /* Print the message */
+  silc_print_to_window(app->screen->output_win[0], message);
+}
+
 /* Prints a message with three star (*) sign before the actual message
    on the current output window. This is used to print command outputs
    and error messages. */
 
-void silc_say(SilcClient client, SilcClientConnection conn, 
-             char *msg, ...)
+void silc_op_say(SilcClient client, SilcClientConnection conn, 
+                SilcClientMessageType type, char *msg, ...)
 {
   va_list vp;
   char message[2048];
@@ -46,24 +221,42 @@ void silc_say(SilcClient client, SilcClientConnection conn,
    received in the packet. The `channel_name' is the name of the channel. */
 
 void silc_channel_message(SilcClient client, SilcClientConnection conn,
-                         SilcClientEntry sender, SilcChannelEntry channel
-                         , char *msg)
+                         SilcClientEntry sender, SilcChannelEntry channel,
+                         SilcMessageFlags flags, char *msg)
 {
   /* Message from client */
   if (conn && !strcmp(conn->current_channel->channel_name, 
                      channel->channel_name))
-    silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]", 
-              msg);
+    if (flags & SILC_MESSAGE_FLAG_ACTION)
+      silc_print(client, "* %s %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
+    else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+      silc_print(client, "- %s %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
+    else
+      silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
   else
-    silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[<unknown>]",
-              channel->channel_name, msg);
+    if (flags & SILC_MESSAGE_FLAG_ACTION)
+      silc_print(client, "* %s:%s %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
+    else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+      silc_print(client, "- %s:%s %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
+    else
+      silc_print(client, "<%s:%s> %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
 }
 
 /* Private message to the client. The `sender' is the nickname of the
    sender received in the packet. */
 
 void silc_private_message(SilcClient client, SilcClientConnection conn,
-                         SilcClientEntry sender, char *msg)
+                         SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg)
 {
   silc_print(client, "*%s* %s", sender->nickname, msg);
 }
@@ -86,7 +279,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
   SilcClientEntry client_entry, client_entry2;
   SilcChannelEntry channel_entry;
   char *tmp = NULL;
-  unsigned int tmp_int;
+  SilcUInt32 tmp_int;
 
   va_start(vp, type);
 
@@ -102,10 +295,11 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     break;
 
   case SILC_NOTIFY_TYPE_INVITE:
+    (void)va_arg(vp, SilcChannelEntry);
+    tmp = va_arg(vp, char *);
     client_entry = va_arg(vp, SilcClientEntry);
-    channel_entry = va_arg(vp, SilcChannelEntry);
     snprintf(message, sizeof(message), "%s invites you to channel %s", 
-            client_entry->nickname, channel_entry->channel_name);
+            client_entry->nickname, tmp);
     break;
 
   case SILC_NOTIFY_TYPE_JOIN:
@@ -182,14 +376,34 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
 
   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
     client_entry = va_arg(vp, SilcClientEntry);
-    tmp = silc_client_chmode(va_arg(vp, unsigned int));
+    tmp_int = va_arg(vp, SilcUInt32);
+    (void)va_arg(vp, char *);
+    (void)va_arg(vp, char *);
     channel_entry = va_arg(vp, SilcChannelEntry);
-    if (tmp)
-      snprintf(message, sizeof(message), "%s changed channel mode to +%s", 
-              client_entry->nickname, tmp);
-    else
-      snprintf(message, sizeof(message), "%s removed all channel modes", 
-              client_entry->nickname);
+    
+    tmp = silc_client_chmode(tmp_int, 
+                            channel_entry->channel_key->cipher->name,
+                            channel_entry->hmac->hmac->name);
+    
+    if (tmp) {
+      if (client_entry) {
+       snprintf(message, sizeof(message), "%s changed channel mode to +%s",
+                client_entry->nickname, tmp);
+      } else {
+       snprintf(message, sizeof(message), 
+                "channel mode was changed to +%s (forced by router)",
+                tmp);
+      }
+    } else {
+      if (client_entry) {
+       snprintf(message, sizeof(message), "%s removed all channel modes", 
+                client_entry->nickname);
+      } else {
+       snprintf(message, sizeof(message), 
+                "Removed all channel modes (forced by router)");
+      }
+    }
+
     if (app->screen->bottom_line->channel_mode)
       silc_free(app->screen->bottom_line->channel_mode);
     app->screen->bottom_line->channel_mode = tmp;
@@ -198,7 +412,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
     client_entry = va_arg(vp, SilcClientEntry);
-    tmp_int = va_arg(vp, unsigned int);
+    tmp_int = va_arg(vp, SilcUInt32);
     tmp = silc_client_chumode(tmp_int);
     client_entry2 = va_arg(vp, SilcClientEntry);
     channel_entry = va_arg(vp, SilcChannelEntry);
@@ -241,6 +455,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     return;
 
   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    return;
     break;
 
   case SILC_NOTIFY_TYPE_KICKED:
@@ -283,6 +498,31 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    {
+      SilcClientEntry *clients;
+      SilcUInt32 clients_count;
+      int i;
+
+      (void)va_arg(vp, void *);
+      clients = va_arg(vp, SilcClientEntry *);
+      clients_count = va_arg(vp, SilcUInt32);
+
+      for (i = 0; i < clients_count; i++) {
+       if (clients[i]->server)
+         snprintf(message, sizeof(message), "Server signoff: %s@%s %s%s%s", 
+                  clients[i]->nickname, clients[i]->server,
+                  tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+       else
+         snprintf(message, sizeof(message), "Server signoff: %s %s%s%s", 
+                  clients[i]->nickname,
+                  tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+       silc_print(client, "*** %s", message);
+       memset(message, 0, sizeof(message));
+      }
+      return;
+    }
+
   default:
     break;
   }
@@ -317,12 +557,9 @@ void silc_command(SilcClient client, SilcClientConnection conn,
       break;
 
     case SILC_COMMAND_LEAVE:
-#if 0
-      if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
-       app->screen->bottom_line->channel = NULL;
-       silc_screen_print_bottom_line(app->screen, 0);
-      }
-#endif
+      /* We won't talk anymore on this channel */
+      silc_say(client, conn, "You have left channel %s", 
+              conn->current_channel->channel_name);
       break;
 
     }
@@ -334,7 +571,7 @@ void silc_command(SilcClient client, SilcClientConnection conn,
 void silc_client_show_users(SilcClient client,
                            SilcClientConnection conn,
                            SilcClientEntry *clients,
-                           unsigned int clients_count,
+                           SilcUInt32 clients_count,
                            void *context)
 {
   SilcChannelEntry channel = (SilcChannelEntry)context;
@@ -373,7 +610,7 @@ void silc_client_show_users(SilcClient client,
     k++;
   }
 
-  client->ops->say(client, conn, "Users on %s: %s", channel->channel_name, 
+  silc_say(client, conn, "Users on %s: %s", channel->channel_name, 
                   name_list);
   silc_free(name_list);
 }
@@ -410,17 +647,19 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       {
        char buf[1024], *nickname, *username, *realname;
        int len;
-       unsigned int idle, mode;
+       SilcUInt32 idle, mode;
+       SilcBuffer channels;
 
-       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+       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_say(client, conn, "%s: %s", tmp,
                             silc_client_command_status_message(status));
          else
-           client->ops->say(client, conn, "%s",
+           silc_say(client, conn, "%s",
                             silc_client_command_status_message(status));
          break;
        }
@@ -432,9 +671,9 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
        nickname = va_arg(vp, char *);
        username = va_arg(vp, char *);
        realname = va_arg(vp, char *);
-       (void)va_arg(vp, void *);
-       mode = va_arg(vp, unsigned int);
-       idle = va_arg(vp, unsigned int);
+       channels = va_arg(vp, SilcBuffer);
+       mode = va_arg(vp, SilcUInt32);
+       idle = va_arg(vp, SilcUInt32);
 
        memset(buf, 0, sizeof(buf));
 
@@ -454,17 +693,49 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
          strncat(buf, ")", 1);
        }
 
-       client->ops->say(client, conn, "%s", buf);
+       silc_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));
+             SilcUInt32 name_len;
+             char *name = silc_channel_get_name(entry, &name_len);
 
-       if (mode)
-         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 (m)
+               strncat(buf, m, strlen(m));
+             strncat(buf, name, name_len);
+             strncat(buf, " ", 1);
+             silc_free(m);
+           }
+
+           silc_say(client, conn, "%s", buf);
+           silc_channel_payload_list_free(list);
+         }
+       }
+
+       if (mode) {
+         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+             (mode & SILC_UMODE_ROUTER_OPERATOR))
+           silc_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)
+           silc_say(client, conn, "%s is gone", nickname);
+       }
 
        if (idle && nickname)
-         client->ops->say(client, conn, "%s has been idle %d %s",
+         silc_say(client, conn, "%s has been idle %d %s",
                           nickname,
                           idle > 60 ? (idle / 60) : idle,
                           idle > 60 ? "minutes" : "seconds");
@@ -476,15 +747,16 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
        char buf[1024], *nickname, *username, *realname;
        int len;
 
-       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+       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_say(client, conn, "%s: %s", tmp,
                             silc_client_command_status_message(status));
          else
-           client->ops->say(client, conn, "%s",
+           silc_say(client, conn, "%s",
                             silc_client_command_status_message(status));
          break;
        }
@@ -515,16 +787,36 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
          strncat(buf, ")", 1);
        }
 
-       client->ops->say(client, conn, "%s", buf);
+       silc_say(client, conn, "%s", buf);
+      }
+      break;
+
+    case SILC_COMMAND_INVITE:
+      {
+       SilcChannelEntry channel;
+       char *invite_list;
+
+       if (!success)
+         return;
+       
+       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:
       {
-       unsigned int mode;
+       SilcUInt32 mode;
        char *topic;
        SilcBuffer client_id_list;
-       unsigned int list_count;
+       SilcUInt32 list_count;
        SilcChannelEntry channel;
 
        if (!success)
@@ -532,21 +824,24 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
 
        app->screen->bottom_line->channel = va_arg(vp, char *);
        channel = va_arg(vp, SilcChannelEntry);
-       mode = va_arg(vp, unsigned int);
-       (void)va_arg(vp, unsigned int);
+       mode = va_arg(vp, SilcUInt32);
+       (void)va_arg(vp, SilcUInt32);
        (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, unsigned int);
+       list_count = va_arg(vp, SilcUInt32);
        client_id_list = va_arg(vp, SilcBuffer);
 
        if (topic)
-         client->ops->say(client, conn, "Topic for %s: %s", 
+         silc_say(client, conn, "Topic for %s: %s", 
                           app->screen->bottom_line->channel, topic);
        
-       app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
+       app->screen->bottom_line->channel_mode = 
+         silc_client_chmode(mode,
+                            channel->channel_key->cipher->name,
+                            channel->hmac->hmac->name);
        silc_screen_print_bottom_line(app->screen, 0);
 
        /* Resolve the client information */
@@ -570,14 +865,63 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       }
       break;
 
+    case SILC_COMMAND_LIST:
+      {
+       char *topic, *name;
+       int usercount;
+       unsigned char buf[256], tmp[16];
+       int i, len;
+
+       if (!success)
+         return;
+
+       (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:
       {
-       unsigned int mode;
+       SilcUInt32 mode;
 
        if (!success)
          return;
 
-       mode = va_arg(vp, unsigned int);
+       mode = va_arg(vp, SilcUInt32);
 
        if (!mode && app->screen->bottom_line->umode) {
          silc_free(app->screen->bottom_line->umode);
@@ -621,21 +965,153 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       break;
 
     case SILC_COMMAND_USERS:
-      if (!success)
-       return;
+      {
+       SilcChannelEntry channel;
+       int line_len;
+       char *line;
+       
+       if (!success)
+         return;
 
-      silc_list_start(conn->current_channel->clients);
-      while ((chu = silc_list_get(conn->current_channel->clients)) 
-            != SILC_LIST_END) {
-       if (chu->client == conn->local_entry) {
-         if (app->screen->bottom_line->mode)
-           silc_free(app->screen->bottom_line->mode);
-         app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
-         silc_screen_print_bottom_line(app->screen, 0);
-         break;
+       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 ((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 (chu->client == conn->local_entry) {
+           /* Update status line */
+           if (app->screen->bottom_line->mode)
+             silc_free(app->screen->bottom_line->mode);
+           app->screen->bottom_line->mode = 
+             silc_client_chumode_char(chu->mode);
+           silc_screen_print_bottom_line(app->screen, 0);
+         }
+
+         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;
+       
+       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;
+       SilcUInt32 pk_len;
+
+       id_type = va_arg(vp, SilcUInt32);
+       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);
+       }
+
+       silc_free(pk);
       }
+
+    case SILC_COMMAND_TOPIC:
+      {
+       SilcChannelEntry channel;
+       char *topic;
+
+       if (!success)
+         return;
+       
+       channel = va_arg(vp, SilcChannelEntry);
+       topic = va_arg(vp, char *);
+
+       if (topic)
+         silc_say(client, conn, 
+                  "Topic on channel %s: %s", channel->channel_name,
+                  topic);
+      }
+      break;
+
+    default:
+      break;
     }
 }
 
@@ -667,12 +1143,11 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn)
 
 /* Asks passphrase from user on the input line. */
 
-unsigned char *silc_ask_passphrase(SilcClient client, 
-                                  SilcClientConnection conn)
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context)
 {
   SilcClientInternal app = (SilcClientInternal)conn->client->application;
   char pass1[256], pass2[256];
-  char *ret;
   int try = 3;
 
   while(try) {
@@ -701,157 +1176,33 @@ unsigned char *silc_ask_passphrase(SilcClient client,
     try--;
   }
 
-  ret = silc_calloc(strlen(pass1), sizeof(char));
-  memcpy(ret, pass1, strlen(pass1));
-
-  memset(pass1, 0, sizeof(pass1));
-  memset(pass2, 0, sizeof(pass2));
-
   wattroff(app->screen->input_win, A_INVIS);
   silc_screen_input_reset(app->screen);
 
-  return ret;
+  /* Deliver the passphrase to the library */
+  completion(pass1, strlen(pass1), context);
+
+  memset(pass1, 0, sizeof(pass1));
+  memset(pass2, 0, sizeof(pass2));
 }
 
-/* Verifies received public key. If user decides to trust the key it is
-   saved as trusted 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. */
 
-int silc_verify_server_key(SilcClient client,
-                          SilcClientConnection conn
-                          unsigned char *pk, unsigned int pk_len,
-                          SilcSKEPKType pk_type)
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk
+                           SilcUInt32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context)
 {
-  SilcSocketConnection sock = conn->sock;
-  char filename[256];
-  char file[256];
-  char *hostname, *fingerprint;
-  struct passwd *pw;
-  struct stat st;
-
-  hostname = sock->hostname ? sock->hostname : sock->ip;
-
-  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
-    silc_say(client, conn, "We don't support server %s key type", hostname);
-    return FALSE;
-  }
-
-  pw = getpwuid(getuid());
-  if (!pw)
-    return FALSE;
-
-  memset(filename, 0, sizeof(filename));
-  memset(file, 0, sizeof(file));
-  snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
-          sock->port);
-  snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s", 
-          pw->pw_dir, file);
-
-  /* Check wheter this key already exists */
-  if (stat(filename, &st) < 0) {
-
-    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-    silc_say(client, conn, "Received server %s public key", hostname);
-    silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
-    silc_say(client, conn, "%s", fingerprint);
-    silc_free(fingerprint);
-
-    /* Ask user to verify the key and save it */
-    if (silc_client_ask_yes_no(client, 
-       "Would you like to accept the key (y/n)? "))
-      {
-       /* Save the key for future checking */
-       silc_pkcs_save_public_key_data(filename, pk, pk_len, 
-                                      SILC_PKCS_FILE_PEM);
-       return TRUE;
-      }
-  } else {
-    /* The key already exists, verify it. */
-    SilcPublicKey public_key;
-    unsigned char *encpk;
-    unsigned int 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)) {
-       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-       silc_say(client, conn, "Received server %s public key", hostname);
-       silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
-       silc_say(client, conn, "%s", fingerprint);
-       silc_free(fingerprint);
-       silc_say(client, conn, "Could not load your local copy of the server %s key",
-                hostname);
-       if (silc_client_ask_yes_no(client, 
-          "Would you like to accept the key anyway (y/n)? "))
-         {
-           /* Save the key for future checking */
-           unlink(filename);
-           silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                          SILC_PKCS_FILE_PEM);
-           return TRUE;
-         }
-       
-       return FALSE;
-      }
-  
-    /* Encode the key data */
-    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
-    if (!encpk) {
-      fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received server %s public key", hostname);
-      silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
-      silc_say(client, conn, "%s", fingerprint);
-      silc_free(fingerprint);
-      silc_say(client, conn, "Your local copy of the server %s key is malformed",
-              hostname);
-      if (silc_client_ask_yes_no(client, 
-         "Would you like to accept the key anyway (y/n)? "))
-       {
-         /* Save the key for future checking */
-         unlink(filename);
-         silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                        SILC_PKCS_FILE_PEM);
-         return TRUE;
-       }
-
-      return FALSE;
-    }
-
-    if (memcmp(encpk, pk, encpk_len)) {
-      fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received server %s public key", hostname);
-      silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
-      silc_say(client, conn, "%s", fingerprint);
-      silc_free(fingerprint);
-      silc_say(client, conn, "Server %s key does not match with your local copy",
-              hostname);
-      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 */
-      if (silc_client_ask_yes_no(client, 
-         "Would you like to accept the key anyway (y/n)? "))
-       {
-         /* Save the key for future checking */
-         unlink(filename);
-         silc_pkcs_save_public_key_data(filename, pk, pk_len,
-                                        SILC_PKCS_FILE_PEM);
-         return TRUE;
-       }
-
-      silc_say(client, conn, "Will not accept server %s key", hostname);
-      return FALSE;
-    }
-
-    /* Local copy matched */
-    return TRUE;
+  if (silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                     pk_len, pk_type)) {
+    completion(TRUE, context);
+    return;
   }
 
-  silc_say(client, conn, "Will not accept server %s key", hostname);
-  return FALSE;
+  completion(FALSE, context);
 }
 
 /* Find authentication method and authentication data by hostname and
@@ -861,14 +1212,14 @@ int silc_verify_server_key(SilcClient client,
    is found and FALSE if not. `conn' may be NULL. */
 
 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
-                        char *hostname, unsigned short port,
+                        char *hostname, SilcUInt16 port,
                         SilcProtocolAuthMeth *auth_meth,
                         unsigned char **auth_data,
-                        unsigned int *auth_data_len)
+                        SilcUInt32 *auth_data_len)
 {
   SilcClientInternal app = (SilcClientInternal)client->application;
 
-  if (app->config->conns) {
+  if (app->config && app->config->conns) {
     SilcClientConfigSectionConnection *conn = NULL;
 
     /* Check if we find a match from user configured connections */
@@ -888,7 +1239,11 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
     }
   }
 
-  return FALSE;
+  *auth_meth = SILC_AUTH_NONE;
+  *auth_data = NULL;
+  *auth_data_len = 0;
+
+  return TRUE;
 }
 
 /* Notifies application that failure packet was received.  This is called
@@ -901,7 +1256,40 @@ int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
 void silc_failure(SilcClient client, SilcClientConnection conn, 
                  SilcProtocol protocol, void *failure)
 {
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+    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)");
+    if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
+      silc_say(client, conn, "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");
+    if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
+      silc_say(client, conn, 
+              "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");
+    if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
+      silc_say(client, conn, 
+              "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");
+    if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
+      silc_say(client, conn, "Incorrect signature");
+  }
+
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+    SilcUInt32 err = (SilcUInt32)failure;
 
+    if (err == SILC_AUTH_FAILED)
+      silc_say(client, conn, "Authentication failed");
+  }
 }
 
 /* Asks whether the user would like to perform the key agreement protocol.
@@ -917,23 +1305,38 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn,
                       SilcKeyAgreementCallback *completion,
                       void **context)
 {
+  char host[256];
+
+  /* We will just display the info on the screen and return FALSE and user
+     will have to start the key agreement with a command. */
+
+  if (hostname) {
+    memset(host, 0, sizeof(host));
+    snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
+  }
+
+  silc_say(client, conn, "%s wants to perform key agreement %s",
+          client_entry->nickname, hostname ? host : "");
+
+  *completion = NULL;
+  *context = NULL;
 
   return FALSE;
 }
 
 /* SILC client operations */
 SilcClientOperations ops = {
-  say:                  silc_say,
-  channel_message:      silc_channel_message,
-  private_message:      silc_private_message,
-  notify:               silc_notify,
-  command:              silc_command,
-  command_reply:        silc_command_reply,
-  connect:              silc_connect,
-  disconnect:           silc_disconnect,
-  get_auth_method:      silc_get_auth_method,
-  verify_server_key:    silc_verify_server_key,
-  ask_passphrase:       silc_ask_passphrase,
-  failure:              silc_failure,
-  key_agreement:        silc_key_agreement,
+  silc_op_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
 };