Watcher list support added.
[silc.git] / lib / silcutil / silcutil.c
index c3af592539a49d72b430f572c80cea7b40e4b307..f622bed5eb341807ead387dc4463b032edefb2b7 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  silcutil.c 
+  silcutil.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -32,27 +32,27 @@ 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)
       return -1;
-    
+
     dest[i] = src[start];
-    
-    if (dest[i] == EOF) 
+
+    if (dest[i] == EOF)
       return EOF;
-    
-    if (dest[i] == '\n') 
+
+    if (dest[i] == '\n')
       break;
   }
   start++;
-  
+
   return start;
 }
 
@@ -60,7 +60,7 @@ int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
    were found. This is used to check for bad lines when reading data from
    for example a configuration file. */
 
-int silc_check_line(char *buf) 
+int silc_check_line(char *buf)
 {
   /* Illegal characters in line */
   if (strchr(buf, '#')) return -1;
@@ -70,11 +70,11 @@ int silc_check_line(char *buf)
   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;
 }
 
@@ -92,171 +92,37 @@ char *silc_get_time()
   return return_time;
 }
 
-/* Converts string to capital characters */
+/* Converts string to capital characters. */
 
-char *silc_to_upper(char *string)
+bool silc_to_upper(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 (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] = '=';
-    }
-  }
-
-  return pem;
-}
-
-/* Same as above but puts newline ('\n') every 72 characters. */
 
-char *silc_encode_pem_file(unsigned char *data, SilcUInt32 data_len)
-{
-  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;
-    }
+  if (strlen(string) > dest_size)
+    return FALSE;
 
-    pem2[i] = pem[j++];
-  }
+  for (i = 0; i < strlen(string); i++)
+    dest[i] = toupper(string[i]);
 
-  silc_free(pem);
-  return pem2;
+  return TRUE;
 }
 
-/* Decodes PEM into data. Returns the decoded data. Note: This is
-   originally public domain code and is still PD. */
+/* Converts string to lower letter characters. */
 
-unsigned char *silc_decode_pem(unsigned char *pem, SilcUInt32 pem_len,
-                              SilcUInt32 *ret_len)
+bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
 {
-  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;
-  }
-
-  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 (char_count == 4) {
-      data[j++] = bits >> 16;
-      data[j++] = (bits >> 8) & 0xff;
-      data[j++] = bits & 0xff;
-      bits = 0;
-      char_count = 0;
-    } else {
-      bits <<= 6;
-    }
-  }
+  int i;
 
-  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 (strlen(string) > dest_size)
+    return FALSE;
 
-  if (ret_len)
-    *ret_len = j;
+  for (i = 0; i < strlen(string); i++)
+    dest[i] = tolower(string[i]);
 
-  return data;
+  return TRUE;
 }
 
-/* Parse userfqdn string which is in user@fqdn format */
+/* Parse userfqdn string which is in user@fqdn format. */
 
 bool silc_parse_userfqdn(const char *string, char **left, char **right)
 {
@@ -273,12 +139,12 @@ bool silc_parse_userfqdn(const char *string, char **left, char **right)
 
   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);
@@ -296,7 +162,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,
@@ -313,7 +179,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 +204,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);
@@ -382,11 +251,22 @@ char *silc_format(char *fmt, ...)
 /* Renders ID to suitable to print for example to log file. */
 
 static char rid[256];
+#define _PUT_STRING(__d__, __s__)                                      \
+do {                                                                   \
+  int __sp = sizeof(__d__) - 1 - strlen(__d__);                                \
+  if (__sp < strlen(__s__)) {                                          \
+    if (__sp)                                                          \
+      strncat(__d__, __s__, (sizeof(__d__) - 1) - strlen(__d__));      \
+  } else {                                                             \
+    strncat(__d__, __s__, strlen(__s__));                              \
+  }                                                                    \
+} while(0)
 
 char *silc_id_render(void *id, SilcUInt16 type)
 {
   char tmp[100];
   unsigned char tmps[2];
+  char *cp;
 
   memset(rid, 0, sizeof(rid));
   switch(type) {
@@ -395,24 +275,29 @@ char *silc_id_render(void *id, SilcUInt16 type)
       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);
+       struct sockaddr_in6 ipv6;
+       memset(&ipv6, 0, sizeof(ipv6));
+       ipv6.sin6_family = AF_INET6;
+       memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
+       if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
+                        tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
+         _PUT_STRING(rid, tmp);
 #endif
       } else {
        struct in_addr ipv4;
        memmove(&ipv4.s_addr, server_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
+       cp = inet_ntoa(ipv4);
+       if (cp)
+         _PUT_STRING(rid, cp);
       }
 
       memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
-      strcat(rid, tmp);
+      snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(server_id->port));
+      _PUT_STRING(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);
+      snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
+      _PUT_STRING(rid, tmp);
     }
     break;
   case SILC_ID_CLIENT:
@@ -420,25 +305,30 @@ char *silc_id_render(void *id, SilcUInt16 type)
       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);
+       struct sockaddr_in6 ipv6;
+       memset(&ipv6, 0, sizeof(ipv6));
+       ipv6.sin6_family = AF_INET6;
+       memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
+       if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
+                        tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
+         _PUT_STRING(rid, tmp);
 #endif
       } else {
        struct in_addr ipv4;
        memmove(&ipv4.s_addr, client_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
+       cp = inet_ntoa(ipv4);
+       if (cp)
+         _PUT_STRING(rid, cp);
       }
 
       memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
-      strcat(rid, tmp);
+      snprintf(tmp, sizeof(tmp) - 1, ",%02x,", client_id->rnd);
+      _PUT_STRING(rid, tmp);
       memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
+      snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x %02x %02x...]",
               client_id->hash[0], client_id->hash[1],
               client_id->hash[2], client_id->hash[3]);
-      strcat(rid, tmp);
+      _PUT_STRING(rid, tmp);
     }
     break;
   case SILC_ID_CHANNEL:
@@ -446,32 +336,38 @@ char *silc_id_render(void *id, SilcUInt16 type)
       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);
+       struct sockaddr_in6 ipv6;
+       memset(&ipv6, 0, sizeof(ipv6));
+       ipv6.sin6_family = AF_INET6;
+       memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
+       if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
+                        tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
+         _PUT_STRING(rid, tmp);
 #endif
       } else {
        struct in_addr ipv4;
        memmove(&ipv4.s_addr, channel_id->ip.data, 4);
-       strcat(rid, inet_ntoa(ipv4));
+       cp = inet_ntoa(ipv4);
+       if (cp)
+         _PUT_STRING(rid, cp);
       }
 
       memset(tmp, 0, sizeof(tmp));
-      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
-      strcat(rid, tmp);
+      snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(channel_id->port));
+      _PUT_STRING(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);
+      snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
+      _PUT_STRING(rid, tmp);
     }
     break;
   }
 
   return rid;
 }
+#undef _PUT_STRING
 
-/* Compares two strings. Strings may include wildcards * and ?.
+/* Compares two strings. Strings may include wildcards '*' and '?'.
    Returns TRUE if strings match. */
 
 int silc_string_compare(char *string1, char *string2)
@@ -494,15 +390,15 @@ int silc_string_compare(char *string1, char *string2)
   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)) {
@@ -510,7 +406,7 @@ int silc_string_compare(char *string1, char *string2)
        strncpy(tmpstr2, tmpstr1, i);
        break;
       }
-    
+
     /* ? wildcard */
     if (tmpstr1[i] == '?') {
       if (!strncmp(tmpstr1, tmpstr2, i)) {
@@ -518,17 +414,17 @@ int silc_string_compare(char *string1, char *string2)
          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);
@@ -536,7 +432,7 @@ int silc_string_compare(char *string1, char *string2)
     silc_free(tmpstr2);
     return TRUE;
   }
-  
+
   memset(tmpstr1, 0, slen1);
   memset(tmpstr2, 0, slen2);
   silc_free(tmpstr1);
@@ -544,7 +440,7 @@ int silc_string_compare(char *string1, char *string2)
   return FALSE;
 }
 
-/* Basic has function to hash strings. May be used with the SilcHashTable. 
+/* 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. */
@@ -553,7 +449,7 @@ 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)) {
@@ -562,7 +458,7 @@ SilcUInt32 silc_hash_string(void *key, void *user_context)
     }
     s++;
   }
-  
+
   return h;
 }
 
@@ -592,43 +488,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,6 +525,25 @@ SilcUInt32 silc_hash_id(void *key, void *user_context)
   return h;
 }
 
+/* Hash Client ID's hash. */
+
+SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
+{
+  int i;
+  unsigned char *hash = key;
+  SilcUInt32 h = 0, g;
+
+  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;
+}
+
 /* Hash binary data. The `user_context' is the data length. */
 
 SilcUInt32 silc_hash_data(void *key, void *user_context)
@@ -654,21 +559,32 @@ SilcUInt32 silc_hash_data(void *key, void *user_context)
   return h;
 }
 
-/* Compares two strings. May be used as SilcHashTable comparison function. */
+/* Hashed SILC Public key. */
+
+SilcUInt32 silc_hash_public_key(void *key, void *user_context)
+{
+  SilcPublicKey pk = (SilcPublicKey)key;
+  return (pk->len + silc_hash_string(pk->name, NULL) +
+         silc_hash_string(pk->identifier, NULL) +
+         silc_hash_data(pk->pk, (void *)pk->pk_len));
+}
+
+/* Compares two strings. It 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)
 {
   SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
-  return (id_type == SILC_ID_CLIENT ? 
+  return (id_type == SILC_ID_CLIENT ?
          SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
          SILC_ID_COMPARE_TYPE(key1, key2, id_type));
 }
@@ -688,6 +604,14 @@ bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
   return !memcmp(key1, key2, len);
 }
 
+/* Compares two SILC Public keys. It may be used as SilcHashTable
+   comparison function. */
+
+bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
+{
+  return silc_pkcs_public_key_compare(key1, key2);
+}
+
 /* Parses mode mask and returns the mode as string. */
 
 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
@@ -723,6 +647,12 @@ char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
     strncat(string, "f", 1);
 
+  if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
+    strncat(string, "m", 1);
+
+  if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
+    strncat(string, "M", 1);
+
   if (mode & SILC_CHANNEL_MODE_CIPHER)
     strncat(string, cipher, strlen(cipher));
 
@@ -751,6 +681,15 @@ char *silc_client_chumode(SilcUInt32 mode)
   if (mode & SILC_CHANNEL_UMODE_CHANOP)
     strncat(string, "o", 1);
 
+  if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
+    strncat(string, "b", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
+    strncat(string, "u", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
+    strncat(string, "r", 1);
+
   return strdup(string);
 }
 
@@ -786,7 +725,7 @@ char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
   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), " ");
 
@@ -798,6 +737,223 @@ char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
     cp[-2] = 0;
   if ((i + 1) % 10 == 0)
     cp[-1] = 0;
-  
+
   return strdup(fingerprint);
 }
+
+/* Return TRUE if the `data' is ASCII string. */
+
+bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
+{
+  int i;
+
+  for (i = 0; i < data_len; i++) {
+    if (!isascii(data[i]))
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Parses SILC protocol style version string. */
+
+bool silc_parse_version_string(const char *version,
+                              SilcUInt32 *protocol_version,
+                              char **protocol_version_string,
+                              SilcUInt32 *software_version,
+                              char **software_version_string,
+                              char **vendor_version)
+{
+  char *cp, buf[32];
+  int maj = 0, min = 0;
+
+  if (!strstr(version, "SILC-"))
+    return FALSE;
+
+  cp = (char *)version + 5;
+  if (!cp)
+    return FALSE;
+
+  /* Take protocol version */
+
+  maj = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp) {
+    min = atoi(cp + 1);
+    cp++;
+  }
+
+  memset(buf, 0, sizeof(buf));
+  snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
+  if (protocol_version)
+    *protocol_version = atoi(buf);
+  memset(buf, 0, sizeof(buf));
+  snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
+  if (protocol_version_string)
+    *protocol_version_string = strdup(buf);
+
+  /* Take software version */
+
+  maj = 0;
+  min = 0;
+  cp = strchr(cp, '-');
+  if (!cp)
+    return FALSE;
+
+  maj = atoi(cp + 1);
+  cp = strchr(cp, '.');
+  if (cp)
+    min = atoi(cp + 1);
+
+  memset(buf, 0, sizeof(buf));
+  snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
+  if (software_version)
+    *software_version = atoi(buf);
+  memset(buf, 0, sizeof(buf));
+  snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
+  if (software_version_string)
+    *software_version_string = strdup(buf);
+
+  /* Take vendor string */
+
+  cp++;
+  if (cp) {
+    cp = strchr(cp, '.');
+    if (cp && cp + 1 && vendor_version)
+      *vendor_version = strdup(cp + 1);
+  }
+
+  return TRUE;
+}
+
+/* Converts version string x.x into number representation. */
+
+SilcUInt32 silc_version_to_num(const char *version)
+{
+  int maj = 0, min = 0;
+  char *cp, buf[32];
+
+  if (!version)
+    return 0;
+
+  cp = (char *)version;
+  maj = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp)
+    min = atoi(cp + 1);
+
+  memset(buf, 0, sizeof(buf));
+  snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
+  return (SilcUInt32)atoi(buf);
+}
+
+/* Displays input prompt on command line and takes input data from user */
+
+char *silc_get_input(const char *prompt, bool echo_off)
+{
+#ifdef SILC_UNIX
+  if (echo_off) {
+    char *ret = NULL;
+#ifdef HAVE_TERMIOS_H
+    char input[2048];
+    int fd;
+    struct termios to;
+    struct termios to_old;
+
+    fd = open("/dev/tty", O_RDONLY);
+    if (fd < 0) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      return NULL;
+    }
+
+    signal(SIGINT, SIG_IGN);
+
+    /* Get terminal info */
+    tcgetattr(fd, &to);
+    to_old = to;
+
+    /* Echo OFF */
+    to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+    tcsetattr(fd, TCSANOW, &to);
+
+    memset(input, 0, sizeof(input));
+
+    printf("%s", prompt);
+    fflush(stdout);
+
+    if ((read(fd, input, sizeof(input))) < 0) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      return NULL;
+    }
+
+    if (strlen(input) <= 1) {
+      tcsetattr(fd, TCSANOW, &to_old);
+      return NULL;
+    }
+
+    if (strchr(input, '\n'))
+      *strchr(input, '\n') = '\0';
+
+    /* Restore old terminfo */
+    tcsetattr(fd, TCSANOW, &to_old);
+    signal(SIGINT, SIG_DFL);
+
+    ret = silc_calloc(strlen(input), sizeof(char));
+    memcpy(ret, input, strlen(input));
+    memset(input, 0, sizeof(input));
+#endif /* HAVE_TERMIOS_H */
+    return ret;
+  } else {
+    char input[2048];
+    int fd;
+
+    fd = open("/dev/tty", O_RDONLY);
+    if (fd < 0) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      return NULL;
+    }
+
+    memset(input, 0, sizeof(input));
+
+    printf("%s", prompt);
+    fflush(stdout);
+
+    if ((read(fd, input, sizeof(input))) < 0) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      return NULL;
+    }
+
+    if (strlen(input) <= 1)
+      return NULL;
+
+    if (strchr(input, '\n'))
+      *strchr(input, '\n') = '\0';
+
+    return strdup(input);
+  }
+#else
+  return NULL;
+#endif /* SILC_UNIX */
+}
+
+/* Return mode list */
+
+bool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
+                       SilcUInt32 **list)
+{
+  int i;
+
+  if (mode_list->len / 4 != mode_list_count)
+    return FALSE;
+
+  *list = silc_calloc(mode_list_count, sizeof(**list));
+
+  for (i = 0; i < mode_list_count; i++) {
+    SILC_GET32_MSB((*list)[i], mode_list->data);
+    silc_buffer_pull(mode_list, 4);
+  }
+
+  silc_buffer_push(mode_list, mode_list->data - mode_list->head);
+
+  return TRUE;
+}