updates.
[silc.git] / apps / silc / client_ops.c
index 3165b7d86a57afcfc3c8ea2bdc39eb245f01c623..59079fb453056769486daeea65f23cff6db42290 100644 (file)
@@ -104,7 +104,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
   SilcClientEntry client_entry, client_entry2;
   SilcChannelEntry channel_entry;
   char *tmp = NULL;
-  unsigned int tmp_int;
+  uint32 tmp_int;
 
   va_start(vp, type);
 
@@ -201,7 +201,7 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
 
   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
     client_entry = va_arg(vp, SilcClientEntry);
-    tmp_int = va_arg(vp, unsigned int);
+    tmp_int = va_arg(vp, uint32);
     (void)va_arg(vp, char *);
     (void)va_arg(vp, char *);
     channel_entry = va_arg(vp, SilcChannelEntry);
@@ -235,7 +235,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, uint32);
     tmp = silc_client_chumode(tmp_int);
     client_entry2 = va_arg(vp, SilcClientEntry);
     channel_entry = va_arg(vp, SilcChannelEntry);
@@ -321,6 +321,31 @@ void silc_notify(SilcClient client, SilcClientConnection conn,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    {
+      SilcClientEntry *clients;
+      uint32 clients_count;
+      int i;
+
+      (void)va_arg(vp, void *);
+      clients = va_arg(vp, SilcClientEntry *);
+      clients_count = va_arg(vp, uint32);
+
+      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;
   }
@@ -372,7 +397,7 @@ void silc_command(SilcClient client, SilcClientConnection conn,
 void silc_client_show_users(SilcClient client,
                            SilcClientConnection conn,
                            SilcClientEntry *clients,
-                           unsigned int clients_count,
+                           uint32 clients_count,
                            void *context)
 {
   SilcChannelEntry channel = (SilcChannelEntry)context;
@@ -448,7 +473,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       {
        char buf[1024], *nickname, *username, *realname;
        int len;
-       unsigned int idle, mode;
+       uint32 idle, mode;
        SilcBuffer channels;
 
        if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
@@ -473,8 +498,8 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
        username = va_arg(vp, char *);
        realname = va_arg(vp, char *);
        channels = va_arg(vp, SilcBuffer);
-       mode = va_arg(vp, unsigned int);
-       idle = va_arg(vp, unsigned int);
+       mode = va_arg(vp, uint32);
+       idle = va_arg(vp, uint32);
 
        memset(buf, 0, sizeof(buf));
 
@@ -507,7 +532,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
            silc_dlist_start(list);
            while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
              char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
-             unsigned int name_len;
+             uint32 name_len;
              char *name = silc_channel_get_name(entry, &name_len);
 
              if (m)
@@ -614,10 +639,10 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
 
     case SILC_COMMAND_JOIN:
       {
-       unsigned int mode;
+       uint32 mode;
        char *topic;
        SilcBuffer client_id_list;
-       unsigned int list_count;
+       uint32 list_count;
        SilcChannelEntry channel;
 
        if (!success)
@@ -625,14 +650,14 @@ 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, 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, unsigned int);
+       list_count = va_arg(vp, uint32);
        client_id_list = va_arg(vp, SilcBuffer);
 
        if (topic)
@@ -667,7 +692,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
     case SILC_COMMAND_LIST:
       {
        char *topic, *name;
-       unsigned int usercount;
+       int usercount;
        unsigned char buf[256], tmp[16];
        int i, len;
 
@@ -677,7 +702,7 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
        (void)va_arg(vp, SilcChannelEntry);
        name = va_arg(vp, char *);
        topic = va_arg(vp, char *);
-       usercount = va_arg(vp, unsigned int);
+       usercount = va_arg(vp, int);
 
        if (status == SILC_STATUS_LIST_START ||
            status == SILC_STATUS_OK)
@@ -715,12 +740,12 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
 
     case SILC_COMMAND_UMODE:
       {
-       unsigned int mode;
+       uint32 mode;
 
        if (!success)
          return;
 
-       mode = va_arg(vp, unsigned int);
+       mode = va_arg(vp, uint32);
 
        if (!mode && app->screen->bottom_line->umode) {
          silc_free(app->screen->bottom_line->umode);
@@ -764,19 +789,88 @@ 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'c 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;
 
@@ -799,6 +893,28 @@ void silc_command_reply(SilcClient client, SilcClientConnection conn,
       }
       break;
 
+    case SILC_COMMAND_GETKEY:
+      {
+       SilcIdType id_type;
+       void *entry;
+       SilcPublicKey public_key;
+       unsigned char *pk;
+       uint32 pk_len;
+
+       id_type = va_arg(vp, SilcIdType);
+       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(client, conn, SILC_SOCKET_TYPE_CLIENT,
+                                pk, pk_len, SILC_SKE_PK_TYPE_SILC);
+       }
+
+       silc_free(pk);
+      }
+
     default:
       break;
     }
@@ -885,24 +1001,22 @@ unsigned char *silc_ask_passphrase(SilcClient client,
 int silc_verify_public_key(SilcClient client,
                           SilcClientConnection conn, 
                           SilcSocketType conn_type,
-                          unsigned char *pk, unsigned int pk_len,
+                          unsigned char *pk, uint32 pk_len,
                           SilcSKEPKType pk_type)
 {
-  SilcSocketConnection sock = conn->sock;
+  int i;
   char filename[256];
   char file[256];
-  char *hostname, *fingerprint;
+  char *fingerprint;
   struct passwd *pw;
   struct stat st;
   char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
                   conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
                  "server" : "client");
 
-  hostname = sock->hostname ? sock->hostname : sock->ip;
-
   if (pk_type != SILC_SKE_PK_TYPE_SILC) {
-    silc_say(client, conn, "We don't support %s %s key type", 
-            entity, hostname);
+    silc_say(client, conn, "We don't support %s key type %d", 
+            entity, pk_type);
     return FALSE;
   }
 
@@ -910,22 +1024,27 @@ int silc_verify_public_key(SilcClient client,
   if (!pw)
     return FALSE;
 
+  /* Replace all whitespaces with `_'. */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+  for (i = 0; i < strlen(fingerprint); i++)
+    if (fingerprint[i] == ' ')
+      fingerprint[i] = '_';
+
   memset(filename, 0, sizeof(filename));
   memset(file, 0, sizeof(file));
-  snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, hostname,
-          sock->port);
+  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);
+
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
 
-  /* Check wheter this key already exists */
+  /* Check whether this key already exists */
   if (stat(filename, &st) < 0) {
 
-    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-    silc_say(client, conn, "Received %s %s public key", entity, hostname);
-    silc_say(client, conn, "Fingerprint for the %s %s key is", entity, 
-            hostname);
+    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_free(fingerprint);
 
     /* Ask user to verify the key and save it */
     if (silc_client_ask_yes_no(client, 
@@ -934,27 +1053,25 @@ int silc_verify_public_key(SilcClient client,
        /* 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;
-    unsigned int encpk_len;
+    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)) {
-       fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-       silc_say(client, conn, "Received %s %s public key", entity, hostname);
-       silc_say(client, conn, "Fingerprint for the %s %s key is", 
-                entity, hostname);
+       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_free(fingerprint);
-       silc_say(client, conn, "Could not load your local copy of the %s %s key",
-                entity, hostname);
+       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)? "))
          {
@@ -962,23 +1079,22 @@ int silc_verify_public_key(SilcClient client,
            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) {
-      fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received %s %s public key", entity, hostname);
-      silc_say(client, conn, "Fingerprint for the %s %s key is", 
-              entity, hostname);
+      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_free(fingerprint);
-      silc_say(client, conn, "Your local copy of the %s %s key is malformed",
-              entity, hostname);
+      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)? "))
        {
@@ -986,22 +1102,22 @@ int silc_verify_public_key(SilcClient client,
          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)) {
-      fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
-      silc_say(client, conn, "Received %s %s public key", entity, hostname);
-      silc_say(client, conn, "Fingerprint for the %s %s key is", 
-              entity, hostname);
+      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_free(fingerprint);
-      silc_say(client, conn, "%s %s key does not match with your local copy",
-              entity, hostname);
-      silc_say(client, conn, "It is possible that the key has expired or changed");
+      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");
       
@@ -1013,18 +1129,22 @@ int silc_verify_public_key(SilcClient client,
          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 %s %s key", entity, hostname);
+      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 %s %s key", entity, hostname);
+  silc_say(client, conn, "Will not accept the %s key", entity);
+  silc_free(fingerprint);
   return FALSE;
 }
 
@@ -1035,14 +1155,14 @@ int silc_verify_public_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, uint16 port,
                         SilcProtocolAuthMeth *auth_meth,
                         unsigned char **auth_data,
-                        unsigned int *auth_data_len)
+                        uint32 *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 */
@@ -1079,7 +1199,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) {
+    uint32 err = (uint32)failure;
 
+    if (err == SILC_AUTH_FAILED)
+      silc_say(client, conn, "Authentication failed");
+  }
 }
 
 /* Asks whether the user would like to perform the key agreement protocol.
@@ -1116,17 +1269,17 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn,
 
 /* 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_public_key:    silc_verify_public_key,
-  ask_passphrase:       silc_ask_passphrase,
-  failure:              silc_failure,
-  key_agreement:        silc_key_agreement,
+  silc_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,
 };