Moved generic string and data hashing and comparison functions
[silc.git] / lib / silcutil / silcutil.c
index c3af592539a49d72b430f572c80cea7b40e4b307..eb0d989e835791fd78d5e2514e1a2709cd8a3c47 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcutil.c 
+  silcutil.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
  */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 
 /* Gets line from a buffer. Stops reading when a newline or EOF occurs.
    This doesn't remove the newline sign from the destination buffer. The
@@ -32,263 +32,113 @@ int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
 {
   static int start = 0;
   int i;
-  
+
   memset(dest, 0, destlen);
-  
+
   if (begin != start)
     start = 0;
-  
+
   i = 0;
   for ( ; start <= srclen; i++, start++) {
-    if (i > destlen)
+    if (i > destlen) {
+      silc_set_errno(SILC_ERR_OVERFLOW);
       return -1;
-    
+    }
+
     dest[i] = src[start];
-    
-    if (dest[i] == EOF) 
+
+    if (dest[i] == EOF) {
+      silc_set_errno(SILC_ERR_EOF);
       return EOF;
-    
-    if (dest[i] == '\n') 
+    }
+
+    if (dest[i] == '\n')
       break;
   }
   start++;
-  
+
   return start;
 }
 
-/* Checks line for illegal characters. Return -1 when illegal character
-   were found. This is used to check for bad lines when reading data from
-   for example a configuration file. */
+/* Converts string to capital characters. */
 
-int silc_check_line(char *buf) 
+SilcBool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
 {
-  /* Illegal characters in line */
-  if (strchr(buf, '#')) return -1;
-  if (strchr(buf, '\'')) return -1;
-  if (strchr(buf, '\\')) return -1;
-  if (strchr(buf, '\r')) return -1;
-  if (strchr(buf, '\a')) return -1;
-  if (strchr(buf, '\b')) return -1;
-  if (strchr(buf, '\f')) return -1;
-  
-  /* Empty line */
-  if (buf[0] == '\n')
-    return -1;
-  
-  return 0;
-}
-
-/* Returns current time as string. */
+  int i;
 
-char *silc_get_time()
-{
-  time_t curtime;
-  char *return_time;
+  if (strlen(string) > dest_size) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
+    return FALSE;
+  }
 
-  curtime = time(NULL);
-  return_time = ctime(&curtime);
-  return_time[strlen(return_time) - 1] = '\0';
+  for (i = 0; i < strlen(string); i++)
+    dest[i] = (char)toupper((int)string[i]);
 
-  return return_time;
+  return TRUE;
 }
 
-/* Converts string to capital characters */
+/* Converts string to lower letter characters. */
 
-char *silc_to_upper(char *string)
+SilcBool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
 {
   int i;
-  char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
-
-  for (i = 0; i < strlen(string); i++)
-    ret[i] = toupper(string[i]);
-
-  return ret;
-}
-
-static unsigned char pem_enc[64] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
-   data string. Note: This is originally public domain code and is 
-   still PD. */
-
-char *silc_encode_pem(unsigned char *data, SilcUInt32 len)
-{
-  int i, j;
-  SilcUInt32 bits, c, char_count;
-  char *pem;
-
-  char_count = 0;
-  bits = 0;
-  j = 0;
-
-  pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
-
-  for (i = 0; i < len; i++) {
-    c = data[i];
-    bits += c;
-    char_count++;
-
-    if (char_count == 3) {
-      pem[j++] = pem_enc[bits  >> 18];
-      pem[j++] = pem_enc[(bits >> 12) & 0x3f];
-      pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
-      pem[j++] = pem_enc[bits & 0x3f];
-      bits = 0;
-      char_count = 0;
-    } else {
-      bits <<= 8;
-    }
+  if (strlen(string) > dest_size) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
+    return FALSE;
   }
 
-  if (char_count != 0) {
-    bits <<= 16 - (8 * char_count);
-    pem[j++] = pem_enc[bits >> 18];
-    pem[j++] = pem_enc[(bits >> 12) & 0x3f];
-
-    if (char_count == 1) {
-      pem[j++] = '=';
-      pem[j] = '=';
-    } else {
-      pem[j++] = pem_enc[(bits >> 6) & 0x3f];
-      pem[j] = '=';
-    }
-  }
+  for (i = 0; i < strlen(string); i++)
+    dest[i] = (char)tolower((int)string[i]);
 
-  return pem;
+  return TRUE;
 }
 
-/* Same as above but puts newline ('\n') every 72 characters. */
+/* Parse userfqdn string which is in user@fqdn format. */
 
-char *silc_encode_pem_file(unsigned char *data, SilcUInt32 data_len)
+int silc_parse_userfqdn(const char *string,
+                       char *user, SilcUInt32 user_size,
+                       char *fqdn, SilcUInt32 fqdn_size)
 {
-  int i, j;
-  SilcUInt32 len, cols;
-  char *pem, *pem2;
-
-  pem = silc_encode_pem(data, data_len);
-  len = strlen(pem);
-
-  pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
-
-  for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
-    if (cols == 72) {
-      pem2[i] = '\n';
-      cols = 0;
-      len++;
-      continue;
-    }
+  SilcUInt32 tlen;
 
-    pem2[i] = pem[j++];
+  if (!user && !fqdn) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return 0;
   }
 
-  silc_free(pem);
-  return pem2;
-}
+  memset(user, 0, user_size);
+  memset(fqdn, 0, fqdn_size);
 
-/* Decodes PEM into data. Returns the decoded data. Note: This is
-   originally public domain code and is still PD. */
-
-unsigned char *silc_decode_pem(unsigned char *pem, SilcUInt32 pem_len,
-                              SilcUInt32 *ret_len)
-{
-  int i, j;
-  SilcUInt32 len, c, char_count, bits;
-  unsigned char *data;
-  static char ialpha[256], decoder[256];
-
-  for (i = 64 - 1; i >= 0; i--) {
-    ialpha[pem_enc[i]] = 1;
-    decoder[pem_enc[i]] = i;
+  if (!string) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return 0;
   }
 
-  char_count = 0;
-  bits = 0;
-  j = 0;
-
-  if (!pem_len)
-    len = strlen(pem);
-  else
-    len = pem_len;
-
-  data = silc_calloc(((len * 6) / 8), sizeof(*data));
-
-  for (i = 0; i < len; i++) {
-    c = pem[i];
-
-    if (c == '=')
-      break;
-
-    if (c > 127 || !ialpha[c])
-      continue;
-
-    bits += decoder[c];
-    char_count++;
+  if (string[0] == '@') {
+    if (user)
+      silc_strncat(user, user_size, string, strlen(string));
 
-    if (char_count == 4) {
-      data[j++] = bits >> 16;
-      data[j++] = (bits >> 8) & 0xff;
-      data[j++] = bits & 0xff;
-      bits = 0;
-      char_count = 0;
-    } else {
-      bits <<= 6;
-    }
+    return 1;
   }
 
-  switch(char_count) {
-  case 1:
-    silc_free(data);
-    return NULL;
-    break;
-  case 2:
-    data[j++] = bits >> 10;
-    break;
-  case 3:
-    data[j++] = bits >> 16;
-    data[j++] = (bits >> 8) & 0xff;
-    break;
-  }
+  if (strchr(string, '@')) {
+    tlen = strcspn(string, "@");
 
-  if (ret_len)
-    *ret_len = j;
+    if (user)
+      silc_strncat(user, user_size, string, tlen);
 
-  return data;
-}
+    if (fqdn)
+      silc_strncat(fqdn, fqdn_size, string + tlen + 1,
+                  strlen(string) - tlen - 1);
 
-/* Parse userfqdn string which is in user@fqdn format */
-
-bool silc_parse_userfqdn(const char *string, char **left, char **right)
-{
-  SilcUInt32 tlen;
-
-  if (!string)
-    return FALSE;
-
-  if (string[0] == '@') {
-    if (left)
-      *left = strdup(string);
-    return TRUE;
+    return 2;
   }
 
-  if (strchr(string, '@')) {
-    tlen = strcspn(string, "@");
-    
-    if (left) {
-      *left = silc_calloc(tlen + 1, sizeof(char));
-      memcpy(*left, string, tlen);
-    }
-    
-    if (right) {
-      *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
-      memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
-    }
-  } else {
-    if (left)
-      *left = strdup(string);
-  }
+  if (user)
+    silc_strncat(user, user_size, string, strlen(string));
 
-  return TRUE;
+  return 1;
 }
 
 /* Parses command line. At most `max_args' is taken. Rest of the line
@@ -296,7 +146,7 @@ bool silc_parse_userfqdn(const char *string, char **left, char **right)
    arguments in the line. Note that the command name is counted as one
    argument and is saved. */
 
-void silc_parse_command_line(unsigned char *buffer, 
+void silc_parse_command_line(unsigned char *buffer,
                             unsigned char ***parsed,
                             SilcUInt32 **parsed_lens,
                             SilcUInt32 **parsed_types,
@@ -305,7 +155,7 @@ void silc_parse_command_line(unsigned char *buffer,
 {
   int i, len = 0;
   int argc = 0;
-  const char *cp = buffer;
+  const char *cp = (const char *)buffer;
   char *tmp;
 
   *parsed = silc_calloc(1, sizeof(**parsed));
@@ -313,7 +163,10 @@ void silc_parse_command_line(unsigned char *buffer,
 
   /* Get the command first */
   len = strcspn(cp, " ");
-  tmp = silc_to_upper((char *)cp);
+  tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
+  if (!tmp)
+    return;
+  silc_to_upper(cp, tmp, strlen(cp));
   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
   memcpy((*parsed)[0], tmp, len);
   silc_free(tmp);
@@ -335,9 +188,9 @@ void silc_parse_command_line(unsigned char *buffer,
        len--;
       if (!len)
        break;
-      
+
       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
-      *parsed_lens = silc_realloc(*parsed_lens, 
+      *parsed_lens = silc_realloc(*parsed_lens,
                                  sizeof(**parsed_lens) * (argc + 1));
       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
       memcpy((*parsed)[argc], cp, len);
@@ -369,222 +222,21 @@ void silc_parse_command_line(unsigned char *buffer,
 char *silc_format(char *fmt, ...)
 {
   va_list args;
-  static char buf[8192];
+  char buf[8192];
 
   memset(buf, 0, sizeof(buf));
   va_start(args, fmt);
-  vsnprintf(buf, sizeof(buf) - 1, fmt, args);
+  silc_vsnprintf(buf, sizeof(buf) - 1, fmt, args);
   va_end(args);
 
-  return strdup(buf);
-}
-
-/* Renders ID to suitable to print for example to log file. */
-
-static char rid[256];
-
-char *silc_id_render(void *id, SilcUInt16 type)
-{
-  char tmp[100];
-  unsigned char tmps[2];
-
-  memset(rid, 0, sizeof(rid));
-  switch(type) {
-  case SILC_ID_SERVER:
-    {
-      SilcServerID *server_id = (SilcServerID *)id;
-      if (server_id->ip.data_len > 4) {
-#ifdef HAVE_IPV6
-       struct in6_addr ipv6;
-       memmove(&ipv6, server_id->ip.data, sizeof(ipv6));
-       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
-         strcat(rid, tmp);
-#endif
-      } else {
-       struct in_addr ipv4;
-       memmove(&ipv4.s_addr, server_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
-      }
-
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
-      strcat(rid, tmp);
-      SILC_PUT16_MSB(server_id->rnd, tmps);
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
-      strcat(rid, tmp);
-    }
-    break;
-  case SILC_ID_CLIENT:
-    {
-      SilcClientID *client_id = (SilcClientID *)id;
-      if (client_id->ip.data_len > 4) {
-#ifdef HAVE_IPV6
-       struct in6_addr ipv6;
-       memmove(&ipv6, client_id->ip.data, sizeof(ipv6));
-       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
-         strcat(rid, tmp);
-#endif
-      } else {
-       struct in_addr ipv4;
-       memmove(&ipv4.s_addr, client_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
-      }
-
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
-      strcat(rid, tmp);
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
-              client_id->hash[0], client_id->hash[1],
-              client_id->hash[2], client_id->hash[3]);
-      strcat(rid, tmp);
-    }
-    break;
-  case SILC_ID_CHANNEL:
-    {
-      SilcChannelID *channel_id = (SilcChannelID *)id;
-      if (channel_id->ip.data_len > 4) {
-#ifdef HAVE_IPV6
-       struct in6_addr ipv6;
-       memmove(&ipv6, channel_id->ip.data, sizeof(ipv6));
-       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
-         strcat(rid, tmp);
-#endif
-      } else {
-       struct in_addr ipv4;
-       memmove(&ipv4.s_addr, channel_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
-      }
-
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
-      strcat(rid, tmp);
-      SILC_PUT16_MSB(channel_id->rnd, tmps);
-      memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
-      strcat(rid, tmp);
-    }
-    break;
-  }
-
-  return rid;
-}
-
-/* Compares two strings. Strings may include wildcards * and ?.
-   Returns TRUE if strings match. */
-
-int silc_string_compare(char *string1, char *string2)
-{
-  int i;
-  int slen1;
-  int slen2;
-  char *tmpstr1, *tmpstr2;
-
-  if (!string1 || !string2)
-    return FALSE;
-
-  slen1 = strlen(string1);
-  slen2 = strlen(string2);
-
-  /* See if they are same already */
-  if (!strncmp(string1, string2, strlen(string2)))
-    return TRUE;
-
-  if (slen2 < slen1)
-    if (!strchr(string1, '*'))
-      return FALSE;
-  
-  /* Take copies of the original strings as we will change them */
-  tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
-  memcpy(tmpstr1, string1, slen1);
-  tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
-  memcpy(tmpstr2, string2, slen2);
-  
-  for (i = 0; i < slen1; i++) {
-    
-    /* * wildcard. Only one * wildcard is possible. */
-    if (tmpstr1[i] == '*')
-      if (!strncmp(tmpstr1, tmpstr2, i)) {
-       memset(tmpstr2, 0, slen2);
-       strncpy(tmpstr2, tmpstr1, i);
-       break;
-      }
-    
-    /* ? wildcard */
-    if (tmpstr1[i] == '?') {
-      if (!strncmp(tmpstr1, tmpstr2, i)) {
-       if (!(slen1 < i + 1))
-         if (tmpstr1[i + 1] != '?' &&
-             tmpstr1[i + 1] != tmpstr2[i + 1])
-           continue;
-       
-       if (!(slen1 < slen2))
-         tmpstr2[i] = '?';
-      }
-    }
-  }
-  
-  /* if using *, remove it */
-  if (strchr(tmpstr1, '*'))
-    *strchr(tmpstr1, '*') = 0;
-  
-  if (!strcmp(tmpstr1, tmpstr2)) {
-    memset(tmpstr1, 0, slen1);
-    memset(tmpstr2, 0, slen2);
-    silc_free(tmpstr1);
-    silc_free(tmpstr2);
-    return TRUE;
-  }
-  
-  memset(tmpstr1, 0, slen1);
-  memset(tmpstr2, 0, slen2);
-  silc_free(tmpstr1);
-  silc_free(tmpstr2);
-  return FALSE;
-}
-
-/* Basic has function to hash strings. May be used with the SilcHashTable. 
-   Note that this lowers the characters of the string (with tolower()) so
-   this is used usually with nicknames, channel and server names to provide
-   case insensitive keys. */
-
-SilcUInt32 silc_hash_string(void *key, void *user_context)
-{
-  char *s = (char *)key;
-  SilcUInt32 h = 0, g;
-  
-  while (*s != '\0') {
-    h = (h << 4) + tolower(*s);
-    if ((g = h & 0xf0000000)) {
-      h = h ^ (g >> 24);
-      h = h ^ g;
-    }
-    s++;
-  }
-  
-  return h;
-}
-
-/* Basic hash function to hash integers. May be used with the SilcHashTable. */
-
-SilcUInt32 silc_hash_uint(void *key, void *user_context)
-{
-  return *(SilcUInt32 *)key;
-}
-
-/* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
-
-SilcUInt32 silc_hash_ptr(void *key, void *user_context)
-{
-  return (SilcUInt32)key;
+  return silc_strdup(buf);
 }
 
 /* Hash a ID. The `user_context' is the ID type. */
 
 SilcUInt32 silc_hash_id(void *key, void *user_context)
 {
-  SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
+  SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
   SilcUInt32 h = 0;
   int i;
 
@@ -592,43 +244,33 @@ SilcUInt32 silc_hash_id(void *key, void *user_context)
   case SILC_ID_CLIENT:
     {
       SilcClientID *id = (SilcClientID *)key;
-      SilcUInt32 g;
-  
-      /* The client ID is hashed by hashing the hash of the ID 
+
+      /* The client ID is hashed by hashing the hash of the ID
         (which is a truncated MD5 hash of the nickname) so that we
         can access the entry from the cache with both Client ID but
         with just a hash from the ID as well. */
-
-      for (i = 0; i < sizeof(id->hash); i++) {
-       h = (h << 4) + id->hash[i];
-       if ((g = h & 0xf0000000)) {
-         h = h ^ (g >> 24);
-         h = h ^ g;
-       }
-      }
-
-      return h;
+      return silc_hash_client_id_hash(id->hash, NULL);
     }
     break;
   case SILC_ID_SERVER:
     {
       SilcServerID *id = (SilcServerID *)key;
-      
+
       h = id->port * id->rnd;
       for (i = 0; i < id->ip.data_len; i++)
        h ^= id->ip.data[i];
-      
+
       return h;
     }
     break;
   case SILC_ID_CHANNEL:
     {
       SilcChannelID *id = (SilcChannelID *)key;
-      
+
       h = id->port * id->rnd;
       for (i = 0; i < id->ip.data_len; i++)
        h ^= id->ip.data[i];
-      
+
       return h;
     }
     break;
@@ -639,165 +281,310 @@ SilcUInt32 silc_hash_id(void *key, void *user_context)
   return h;
 }
 
-/* Hash binary data. The `user_context' is the data length. */
+/* Hash Client ID's hash. */
 
-SilcUInt32 silc_hash_data(void *key, void *user_context)
+SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
 {
-  SilcUInt32 len = (SilcUInt32)user_context, h = 0;
-  unsigned char *data = (unsigned char *)key;
   int i;
+  unsigned char *hash = key;
+  SilcUInt32 h = 0, g;
 
-  h = (data[0] * data[len - 1] + 1) * len;
-  for (i = 0; i < len; i++)
-    h ^= data[i];
+  for (i = 0; i < CLIENTID_HASH_LEN; i++) {
+    h = (h << 4) + hash[i];
+    if ((g = h & 0xf0000000)) {
+      h = h ^ (g >> 24);
+      h = h ^ g;
+    }
+  }
 
   return h;
 }
 
-/* Compares two strings. May be used as SilcHashTable comparison function. */
-
-bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
-{
-  return !strcasecmp((char *)key1, (char *)key2);
-}
-
-/* Compares two ID's. May be used as SilcHashTable comparison function. 
+/* Compares two ID's. May be used as SilcHashTable comparison function.
    The Client ID's compares only the hash of the Client ID not any other
    part of the Client ID. Other ID's are fully compared. */
 
-bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
+SilcBool silc_hash_id_compare(void *key1, void *key2, void *user_context)
 {
-  SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
-  return (id_type == SILC_ID_CLIENT ? 
+  SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
+  return (id_type == SILC_ID_CLIENT ?
          SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
          SILC_ID_COMPARE_TYPE(key1, key2, id_type));
 }
 
+/* Compares two ID's. Compares full IDs. */
+
+SilcBool silc_hash_id_compare_full(void *key1, void *key2, void *user_context)
+{
+  SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
+  return SILC_ID_COMPARE_TYPE(key1, key2, id_type);
+}
+
 /* Compare two Client ID's entirely and not just the hash from the ID. */
 
-bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
+SilcBool silc_hash_client_id_compare(void *key1, void *key2,
+                                    void *user_context)
 {
   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
 }
 
-/* Compares binary data. May be used as SilcHashTable comparison function. */
+/* Creates fingerprint from data, usually used with SHA1 digests */
+
+char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
+{
+  char fingerprint[64], *cp;
+  int i;
+
+  memset(fingerprint, 0, sizeof(fingerprint));
+  cp = fingerprint;
+  for (i = 0; i < data_len; i++) {
+    silc_snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
+    cp += 2;
+
+    if ((i + 1) % 2 == 0)
+      silc_snprintf(cp++, sizeof(fingerprint), " ");
+
+    if ((i + 1) % 10 == 0)
+      silc_snprintf(cp++, sizeof(fingerprint), " ");
+  }
+  i--;
+  if ((i + 1) % 2 == 0)
+    cp[-2] = 0;
+  if ((i + 1) % 10 == 0)
+    cp[-1] = 0;
+
+  return silc_strdup(fingerprint);
+}
+
+/* Return TRUE if the `data' is ASCII string. */
 
-bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
+SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
 {
-  SilcUInt32 len = (SilcUInt32)user_context;
-  return !memcmp(key1, key2, len);
+  int i;
+
+  for (i = 0; i < data_len; i++) {
+    if (!isascii(data[i]))
+      return FALSE;
+  }
+
+  return TRUE;
 }
 
-/* Parses mode mask and returns the mode as string. */
+/* Displays input prompt on command line and takes input data from user */
 
-char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
+char *silc_get_input(const char *prompt, SilcBool echo_off)
 {
-  char string[100];
+#ifdef SILC_UNIX
+  int fd;
+  char input[2048];
+
+  if (echo_off) {
+    char *ret = NULL;
+#ifdef HAVE_TERMIOS_H
+    struct termios to;
+    struct termios to_old;
+
+    fd = open("/dev/tty", O_RDONLY);
+    if (fd < 0) {
+      silc_set_errno_posix(errno);
+      return NULL;
+    }
+
+    signal(SIGINT, SIG_IGN);
 
-  if (!mode)
-    return NULL;
+    /* Get terminal info */
+    tcgetattr(fd, &to);
+    to_old = to;
 
-  memset(string, 0, sizeof(string));
+    /* Echo OFF, and assure we can prompt and get input */
+    to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+    to.c_lflag |= ICANON;
+    to.c_cc[VMIN] = 255;
+    tcsetattr(fd, TCSANOW, &to);
 
-  if (mode & SILC_CHANNEL_MODE_PRIVATE)
-    strncat(string, "p", 1);
+    memset(input, 0, sizeof(input));
 
-  if (mode & SILC_CHANNEL_MODE_SECRET)
-    strncat(string, "s", 1);
+    printf("%s", prompt);
+    fflush(stdout);
 
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
-    strncat(string, "k", 1);
+    if ((read(fd, input, sizeof(input))) < 0) {
+      silc_set_errno_posix(errno);
+      tcsetattr(fd, TCSANOW, &to_old);
+      return NULL;
+    }
 
-  if (mode & SILC_CHANNEL_MODE_INVITE)
-    strncat(string, "i", 1);
+    if (strlen(input) <= 1) {
+      tcsetattr(fd, TCSANOW, &to_old);
+      silc_set_errno(SILC_ERR_EOF);
+      return NULL;
+    }
 
-  if (mode & SILC_CHANNEL_MODE_TOPIC)
-    strncat(string, "t", 1);
+    if (strchr(input, '\n'))
+      *strchr(input, '\n') = '\0';
 
-  if (mode & SILC_CHANNEL_MODE_ULIMIT)
-    strncat(string, "l", 1);
+    /* Restore old terminfo */
+    tcsetattr(fd, TCSANOW, &to_old);
+    signal(SIGINT, SIG_DFL);
+
+    ret = silc_memdup(input, strlen(input));
+    memset(input, 0, sizeof(input));
+#endif /* HAVE_TERMIOS_H */
+    return ret;
+  } else {
+    fd = open("/dev/tty", O_RDONLY);
+    if (fd < 0) {
+      silc_set_errno_posix(errno);
+      return NULL;
+    }
 
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
-    strncat(string, "a", 1);
+    memset(input, 0, sizeof(input));
 
-  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
-    strncat(string, "f", 1);
+    printf("%s", prompt);
+    fflush(stdout);
 
-  if (mode & SILC_CHANNEL_MODE_CIPHER)
-    strncat(string, cipher, strlen(cipher));
+    if ((read(fd, input, sizeof(input))) < 0) {
+      silc_set_errno_posix(errno);
+      return NULL;
+    }
 
-  if (mode & SILC_CHANNEL_MODE_HMAC)
-    strncat(string, hmac, strlen(hmac));
+    if (strlen(input) <= 1) {
+      silc_set_errno(SILC_ERR_EOF);
+      return NULL;
+    }
 
-  /* Rest of mode is ignored */
+    if (strchr(input, '\n'))
+      *strchr(input, '\n') = '\0';
 
-  return strdup(string);
+    return silc_strdup(input);
+  }
+#else
+  return NULL;
+#endif /* SILC_UNIX */
 }
 
-/* Parses channel user mode mask and returns te mode as string */
+/* Hexdump */
 
-char *silc_client_chumode(SilcUInt32 mode)
+void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
+                 FILE *output)
 {
-  char string[4];
+  int i, k;
+  int off, pos, count;
+  int len = data_len;
+
+  k = 0;
+  pos = 0;
+  count = 16;
+  off = len % 16;
+  while (1) {
+    if (off) {
+      if ((len - pos) < 16 && (len - pos <= len - off))
+       count = off;
+    } else {
+      if (pos == len)
+       count = 0;
+    }
+    if (off == len)
+      count = len;
+
+    if (count)
+      fprintf(output, "%08X  ", k++ * 16);
 
-  if (!mode)
-    return NULL;
+    for (i = 0; i < count; i++) {
+      fprintf(output, "%02X ", data[pos + i]);
+
+      if ((i + 1) % 4 == 0)
+       fprintf(output, " ");
+    }
 
-  memset(string, 0, sizeof(string));
+    if (count && count < 16) {
+      int j;
+
+      for (j = 0; j < 16 - count; j++) {
+       fprintf(output, "   ");
+
+       if ((j + count + 1) % 4 == 0)
+         fprintf(output, " ");
+      }
+    }
+
+    for (i = 0; i < count; i++) {
+      char ch;
+
+      if (data[pos] < 32 || data[pos] >= 127)
+       ch = '.';
+      else
+       ch = data[pos];
 
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "f", 1);
+      fprintf(output, "%c", ch);
+      pos++;
+    }
 
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "o", 1);
+    if (count)
+      fprintf(output, "\n");
 
-  return strdup(string);
+    if (count < 16)
+      break;
+  }
 }
 
-/* Parses channel user mode and returns it as special mode character. */
+/* Convert hex string to data.  Each hex number must have two characters. */
 
-char *silc_client_chumode_char(SilcUInt32 mode)
+SilcBool silc_hex2data(const char *hex, unsigned char *data,
+                      SilcUInt32 data_size, SilcUInt32 *ret_data_len)
 {
-  char string[4];
+  char *cp = (char *)hex;
+  unsigned char l, h;
+  int i;
 
-  if (!mode)
-    return NULL;
+  if (data_size < strlen(hex) / 2) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
+    return FALSE;
+  }
 
-  memset(string, 0, sizeof(string));
+  for (i = 0; i < strlen(hex) / 2; i++) {
+    h = *cp++;
+    l = *cp++;
 
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "*", 1);
+    h -= h < 'A' ? '0' : 'A' - 10;
+    l -= l < 'A' ? '0' : 'A' - 10;
+
+    data[i] = (h << 4) | (l & 0xf);
+  }
 
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "@", 1);
+  if (ret_data_len)
+    *ret_data_len = i;
 
-  return strdup(string);
+  SILC_LOG_HEXDUMP(("len %d", i), data, i);
+
+  return TRUE;
 }
 
-/* Creates fingerprint from data, usually used with SHA1 digests */
+/* Converts binary data to HEX string */
 
-char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
+SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
+                      char *hex, SilcUInt32 hex_size)
 {
-  char fingerprint[64], *cp;
+  unsigned char l, h;
+  char *cp = hex;
   int i;
 
-  memset(fingerprint, 0, sizeof(fingerprint));
-  cp = fingerprint;
+  if (hex_size - 1 < data_len * 2) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
+    return FALSE;
+  }
+
+  memset(hex, 0, hex_size);
+
   for (i = 0; i < data_len; i++) {
-    snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
-    cp += 2;
-    
-    if ((i + 1) % 2 == 0)
-      snprintf(cp++, sizeof(fingerprint), " ");
+    l = data[i];
+    h = l >> 4;
+    l &= 0xf;
 
-    if ((i + 1) % 10 == 0)
-      snprintf(cp++, sizeof(fingerprint), " ");
+    *cp++ = h + (h > 9 ? 'A' - 10 : '0');
+    *cp++ = l + (l > 9 ? 'A' - 10 : '0');
   }
-  i--;
-  if ((i + 1) % 2 == 0)
-    cp[-2] = 0;
-  if ((i + 1) % 10 == 0)
-    cp[-1] = 0;
-  
-  return strdup(fingerprint);
+
+  SILC_LOG_DEBUG(("HEX string: '%s'", hex));
+
+  return TRUE;
 }