Merged from silc_1_0_branch.
[silc.git] / lib / silcutil / silcutil.c
index fa482dcce9c89a4f422762b35ca4a9bb112cd68b..8c9064891527f6199d346681f9d3a38cf3cb0d53 100644 (file)
@@ -2,15 +2,14 @@
 
   silcutil.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2002 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 #include "silcincludes.h"
 
-/* Reads a file to a buffer. The allocated buffer is returned. Length of
-   the file read is returned to the return_len argument. */
-
-char *silc_file_read(const char *filename, uint32 *return_len)
-{
-  int fd;
-  char *buffer;
-  int filelen;
-
-  fd = open(filename, O_RDONLY);
-  if (fd < 0) {
-    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
-    return NULL;
-  }
-
-  filelen = lseek(fd, (off_t)0L, SEEK_END);
-  if (filelen < 0) {
-    close(fd);
-    return NULL;
-  }
-  if (lseek(fd, (off_t)0L, SEEK_SET) < 0) {
-    close(fd);
-    return NULL;
-  }
-
-  if (filelen < 0) {
-    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
-    close(fd);
-    return NULL;
-  }
-  
-  buffer = silc_calloc(filelen + 1, sizeof(char));
-  
-  if ((read(fd, buffer, filelen)) == -1) {
-    memset(buffer, 0, sizeof(buffer));
-    close(fd);
-    SILC_LOG_ERROR(("Cannot read from file %s: %s", filename,
-                    strerror(errno)));
-    return NULL;
-  }
-
-  close(fd);
-  buffer[filelen] = EOF;
-
-  if (return_len)
-    *return_len = filelen;
-
-  return buffer;
-}
-
-/* Writes a buffer to the file. */
-
-int silc_file_write(const char *filename, const char *buffer, uint32 len)
-{
-  int fd;
-        
-  if ((fd = creat(filename, 0644)) == -1) {
-    SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
-                   strerror(errno)));
-    return -1;
-  }
-  
-  if ((write(fd, buffer, len)) == -1) {
-    SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
-    close(fd);
-    return -1;
-  }
-
-  close(fd);
-  
-  return 0;
-}
-
-/* Writes a buffer to the file.  If the file is created specific mode is
-   set to the file. */
-
-int silc_file_write_mode(const char *filename, const char *buffer, 
-                        uint32 len, int mode)
-{
-  int fd;
-        
-  if ((fd = creat(filename, mode)) == -1) {
-    SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
-                   strerror(errno)));
-    return -1;
-  }
-  
-  if ((write(fd, buffer, len)) == -1) {
-    SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
-    close(fd);
-    return -1;
-  }
-
-  close(fd);
-  
-  return 0;
-}
-
 /* 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
    argument begin is returned and should be passed again for the function. */
@@ -131,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;
 }
 
@@ -159,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;
@@ -169,237 +70,93 @@ 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;
 }
 
-/* Returns current time as string. */
+/* Returns time as string.  If the the `timeval' is non-zero that
+   value is returned as string.  If it is zero the current time of the
+   local machine is returned. */
 
-char *silc_get_time()
+const char *silc_get_time(SilcUInt32 timeval)
 {
   time_t curtime;
   char *return_time;
 
-  curtime = time(NULL);
+  if (!timeval)
+    curtime = time(NULL);
+  else
+    curtime = (time_t)timeval;
   return_time = ctime(&curtime);
   return_time[strlen(return_time) - 1] = '\0';
 
-  return return_time;
+  return (const char *)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, uint32 len)
-{
-  int i, j;
-  uint32 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, uint32 data_len)
-{
-  int i, j;
-  uint32 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, uint32 pem_len,
-                              uint32 *ret_len)
+bool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
 {
-  int i, j;
-  uint32 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 nickname string. The format may be <num>!<nickname>@<server> to
-   support multiple same nicknames. The <num> is the final unifier if same
-   nickname is on same server. Note, this is only local format and server
-   does not know anything about these. */
+/* Parse userfqdn string which is in user@fqdn format. */
 
-int silc_parse_nickname(char *string, char **nickname, char **server,
-                       uint32 *num)
+bool silc_parse_userfqdn(const char *string, char **left, char **right)
 {
-  uint32 tlen;
+  SilcUInt32 tlen;
 
   if (!string)
     return FALSE;
 
-  if (strchr(string, '!')) {
-    char *tmp;
-    tlen = strcspn(string, "!");
-    tmp = silc_calloc(tlen + 1, sizeof(*tmp));
-    memcpy(tmp, string, tlen);
-
-    if (num)
-      *num = atoi(tmp);
-
-    silc_free(tmp);
-
-    if (tlen >= strlen(string))
-      return FALSE;
-
-    string += tlen + 1;
+  if (string[0] == '@') {
+    if (left)
+      *left = strdup(string);
+    return TRUE;
   }
 
   if (strchr(string, '@')) {
     tlen = strcspn(string, "@");
-    
-    if (nickname) {
-      *nickname = silc_calloc(tlen + 1, sizeof(char));
-      memcpy(*nickname, string, tlen);
+
+    if (left) {
+      *left = silc_calloc(tlen + 1, sizeof(char));
+      memcpy(*left, string, tlen);
     }
-    
-    if (server) {
-      *server = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
-      memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1);
+
+    if (right) {
+      *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
+      memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
     }
   } else {
-    if (nickname)
-      *nickname = strdup(string);
+    if (left)
+      *left = strdup(string);
   }
 
   return TRUE;
@@ -410,12 +167,12 @@ int silc_parse_nickname(char *string, char **nickname, char **server,
    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,
-                            uint32 **parsed_lens,
-                            uint32 **parsed_types,
-                            uint32 *parsed_num,
-                            uint32 max_args)
+                            SilcUInt32 **parsed_lens,
+                            SilcUInt32 **parsed_types,
+                            SilcUInt32 *parsed_num,
+                            SilcUInt32 max_args)
 {
   int i, len = 0;
   int argc = 0;
@@ -427,7 +184,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);
@@ -449,9 +209,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);
@@ -496,112 +256,154 @@ char *silc_format(char *fmt, ...)
 /* Renders ID to suitable to print for example to log file. */
 
 static char rid[256];
-
-char *silc_id_render(void *id, uint16 type)
+#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) {
   case SILC_ID_SERVER:
     {
       SilcServerID *server_id = (SilcServerID *)id;
-      struct in_addr ipv4;
-
       if (server_id->ip.data_len > 4) {
-
+#ifdef HAVE_IPV6
+       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 {
-       SILC_GET32_MSB(ipv4.s_addr, server_id->ip.data);
-       strcat(rid, inet_ntoa(ipv4));
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, server_id->ip.data, 4);
+       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:
     {
       SilcClientID *client_id = (SilcClientID *)id;
-      struct in_addr ipv4;
-
       if (client_id->ip.data_len > 4) {
-
+#ifdef HAVE_IPV6
+       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 {
-       SILC_GET32_MSB(ipv4.s_addr, client_id->ip.data);
-       strcat(rid, inet_ntoa(ipv4));
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, client_id->ip.data, 4);
+       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:
     {
       SilcChannelID *channel_id = (SilcChannelID *)id;
-      struct in_addr ipv4;
-
       if (channel_id->ip.data_len > 4) {
-
+#ifdef HAVE_IPV6
+       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 {
-       SILC_GET32_MSB(ipv4.s_addr, channel_id->ip.data);
-       strcat(rid, inet_ntoa(ipv4));
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, channel_id->ip.data, 4);
+       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)
 {
   int i;
-  int slen1 = strlen(string1);
-  int slen2 = strlen(string2);
+  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)))
+  if (!strncmp(string1, string2, slen2) && slen2 == slen1)
     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)) {
@@ -609,7 +411,7 @@ int silc_string_compare(char *string1, char *string2)
        strncpy(tmpstr2, tmpstr1, i);
        break;
       }
-    
+
     /* ? wildcard */
     if (tmpstr1[i] == '?') {
       if (!strncmp(tmpstr1, tmpstr2, i)) {
@@ -617,17 +419,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);
@@ -635,7 +437,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);
@@ -643,16 +445,16 @@ 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. */
 
-uint32 silc_hash_string(void *key, void *user_context)
+SilcUInt32 silc_hash_string(void *key, void *user_context)
 {
   char *s = (char *)key;
-  uint32 h = 0, g;
-  
+  SilcUInt32 h = 0, g;
+
   while (*s != '\0') {
     h = (h << 4) + tolower(*s);
     if ((g = h & 0xf0000000)) {
@@ -661,73 +463,63 @@ uint32 silc_hash_string(void *key, void *user_context)
     }
     s++;
   }
-  
+
   return h;
 }
 
 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
 
-uint32 silc_hash_uint(void *key, void *user_context)
+SilcUInt32 silc_hash_uint(void *key, void *user_context)
 {
-  return *(uint32 *)key;
+  return *(SilcUInt32 *)key;
 }
 
 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
 
-uint32 silc_hash_ptr(void *key, void *user_context)
+SilcUInt32 silc_hash_ptr(void *key, void *user_context)
 {
-  return (uint32)key;
+  return (SilcUInt32)key;
 }
 
 /* Hash a ID. The `user_context' is the ID type. */
 
-uint32 silc_hash_id(void *key, void *user_context)
+SilcUInt32 silc_hash_id(void *key, void *user_context)
 {
-  SilcIdType id_type = (SilcIdType)(uint32)user_context;
-  uint32 h = 0;
+  SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
+  SilcUInt32 h = 0;
   int i;
 
   switch (id_type) {
   case SILC_ID_CLIENT:
     {
       SilcClientID *id = (SilcClientID *)key;
-      uint32 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;
@@ -738,11 +530,30 @@ uint32 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. */
 
-uint32 silc_hash_data(void *key, void *user_context)
+SilcUInt32 silc_hash_data(void *key, void *user_context)
 {
-  uint32 len = (uint32)user_context, h = 0;
+  SilcUInt32 len = (SilcUInt32)user_context, h = 0;
   unsigned char *data = (unsigned char *)key;
   int i;
 
@@ -753,21 +564,32 @@ uint32 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)(uint32)user_context;
-  return (id_type == SILC_ID_CLIENT ? 
+  SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
+  return (id_type == SILC_ID_CLIENT ?
          SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
          SILC_ID_COMPARE_TYPE(key1, key2, id_type));
 }
@@ -783,13 +605,21 @@ bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
 
 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
 {
-  uint32 len = (uint32)user_context;
+  SilcUInt32 len = (SilcUInt32)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(uint32 mode, const char *cipher, const char *hmac)
+char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
 {
   char string[100];
 
@@ -822,11 +652,31 @@ char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
     strncat(string, "f", 1);
 
-  if (mode & SILC_CHANNEL_MODE_CIPHER)
-    strncat(string, cipher, strlen(cipher));
+  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, "c", 1);
+  
   if (mode & SILC_CHANNEL_MODE_HMAC)
-    strncat(string, hmac, strlen(hmac));
+    strncat(string, "h", 1);
+
+  if (mode & SILC_CHANNEL_MODE_CIPHER) {
+    if (strlen(cipher) + strlen(string) + 1< sizeof(string)) {
+      strncat(string, " ", 1);
+      strncat(string, cipher, strlen(cipher));
+    }
+  }
+
+  if (mode & SILC_CHANNEL_MODE_HMAC) {
+    if (strlen(hmac) + strlen(string) + 1< sizeof(string)) {
+      strncat(string, " ", 1);
+      strncat(string, hmac, strlen(hmac));
+    }
+  }
 
   /* Rest of mode is ignored */
 
@@ -835,9 +685,9 @@ char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
 
 /* Parses channel user mode mask and returns te mode as string */
 
-char *silc_client_chumode(uint32 mode)
+char *silc_client_chumode(SilcUInt32 mode)
 {
-  char string[4];
+  char string[64];
 
   if (!mode)
     return NULL;
@@ -850,14 +700,26 @@ char *silc_client_chumode(uint32 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);
+
+  if (mode & SILC_CHANNEL_UMODE_QUIET)
+    strncat(string, "q", 1);
+
   return strdup(string);
 }
 
 /* Parses channel user mode and returns it as special mode character. */
 
-char *silc_client_chumode_char(uint32 mode)
+char *silc_client_chumode_char(SilcUInt32 mode)
 {
-  char string[4];
+  char string[64];
 
   if (!mode)
     return NULL;
@@ -870,5 +732,436 @@ char *silc_client_chumode_char(uint32 mode)
   if (mode & SILC_CHANNEL_UMODE_CHANOP)
     strncat(string, "@", 1);
 
+  if (mode & SILC_CHANNEL_UMODE_QUIET)
+    strncat(string, "&", 1);
+
   return strdup(string);
 }
+
+/* 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++) {
+    snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
+    cp += 2;
+
+    if ((i + 1) % 2 == 0)
+      snprintf(cp++, sizeof(fingerprint), " ");
+
+    if ((i + 1) % 10 == 0)
+      snprintf(cp++, sizeof(fingerprint), " ");
+  }
+  i--;
+  if ((i + 1) % 2 == 0)
+    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);
+  if (!strchr(cp, '.'))
+    return FALSE;
+  cp = strchr(cp, '.') + 1;
+  if (!cp || !(*cp))
+    return FALSE;
+  min = atoi(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;
+  if (!strchr(cp, '-'))
+    return FALSE;
+  cp = strchr(cp, '-') + 1;
+  if (!cp || !(*cp))
+    return FALSE;
+
+  maj = atoi(cp);
+  if (strchr(cp, '.')) {
+    cp = strchr(cp, '.') + 1;
+    if (cp && *cp)
+      min = atoi(cp);
+  }
+
+  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 */
+
+  if (strchr(cp, '.')) {
+    cp = strchr(cp, '.') + 1;
+    if (cp && *cp && vendor_version)
+      *vendor_version = strdup(cp);
+  }
+
+  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
+  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) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      return NULL;
+    }
+
+    signal(SIGINT, SIG_IGN);
+
+    /* Get terminal info */
+    tcgetattr(fd, &to);
+    to_old = to;
+
+    /* 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);
+
+    memset(input, 0, sizeof(input));
+
+    printf("%s", prompt);
+    fflush(stdout);
+
+    if ((read(fd, input, sizeof(input))) < 0) {
+      fprintf(stderr, "silc: %s\n", strerror(errno));
+      tcsetattr(fd, TCSANOW, &to_old);
+      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_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) {
+      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;
+}
+
+/* Status message structure. Messages are defined below. */
+typedef struct {
+  SilcStatus status;
+  const char *message;
+} SilcStatusMessage;
+
+#define STAT(x) SILC_STATUS_ERR_##x
+static const SilcStatusMessage silc_status_messages[] = {
+
+  { STAT(NO_SUCH_NICK),      "There was no such nickname" },
+  { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
+  { STAT(NO_SUCH_SERVER),    "There was no such server" },
+  { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
+  { STAT(NO_RECIPIENT),      "No recipient given" },
+  { STAT(UNKNOWN_COMMAND),   "Unknown command" },
+  { STAT(WILDCARDS),         "Wilcrads not allowed" },
+  { STAT(NO_CLIENT_ID),      "No Client ID given" },
+  { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
+  { STAT(NO_SERVER_ID),      "No Server ID given" },
+  { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
+  { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
+  { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
+  { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
+  { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
+  { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
+  { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
+  { STAT(USER_ON_CHANNEL),   "User already on the channel" },
+  { STAT(NOT_REGISTERED),    "You have not registered" },
+  { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
+  { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
+  { STAT(PERM_DENIED),       "Permission denied" },
+  { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
+  { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
+  { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
+  { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
+  { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
+  { STAT(UNKNOWN_MODE),    "Unknown mode" },
+  { STAT(NOT_YOU),         "Cannot change mode for other users" },
+  { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
+  { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
+  { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
+  { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
+  { STAT(BAD_NICKNAME),    "Bad nickname" },
+  { STAT(BAD_CHANNEL),     "Bad channel name" },
+  { STAT(AUTH_FAILED),     "Authentication failed" },
+  { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
+  { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
+  { STAT(RESOURCE_LIMIT), "No more free resources" },
+  { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
+  { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
+  { STAT(BAD_SERVER_ID), "Server ID is not valid" },
+  { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
+  { STAT(BAD_VERSION), "Bad version" },
+  { STAT(TIMEDOUT), "Service timed out" },
+  { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" },
+  { STAT(OPERATION_ALLOWED), "Operation is not allowed" },
+
+  { 0, NULL }
+};
+
+/* Returns status message string */
+
+const char *silc_get_status_message(unsigned char status)
+{
+  int i;
+
+  for (i = 0; silc_status_messages[i].message; i++) {
+    if (silc_status_messages[i].status == status)
+      break;
+  }
+
+  if (silc_status_messages[i].message == NULL)
+    return "";
+
+  return silc_status_messages[i].message;
+}
+
+static const char *packet_name[] = {
+  "NONE",
+  "DISCONNECT",
+  "SUCCESS",
+  "FAILURE",
+  "REJECT",
+  "NOTIFY",
+  "ERROR",
+  "CHANNEL MESSAGE",
+  "CHANNEL KEY",
+  "PRIVATE MESSAGE",
+  "PRIVATE MESSAGE KEY",
+  "COMMAND",
+  "COMMAND REPLY",
+  "KEY EXCHANGE",
+  "KEY EXCHANGE 1",
+  "KEY EXCHANGE 2",
+  "CONNECTION AUTH REQUEST",
+  "CONNECTION AUTH",
+  "NEW ID",
+  "NEW CLIENT",
+  "NEW SERVER",
+  "NEW CHANNEL",
+  "REKEY",
+  "REKEY_DONE",
+  "HEARTBEAT",
+  "KEY AGREEMENT",
+  "RESUME ROUTER",
+  "FTP",
+  "RESUME CLIENT",
+};
+
+/* Returns packet type name */
+
+const char *silc_get_packet_name(unsigned char type)
+{
+  if (type >= SILC_PACKET_MAX)
+    return "RESERVED";
+  if (type >= SILC_PACKET_PRIVATE)
+    return "PRIVATE RANGE";
+  if (type > (sizeof(packet_name) / sizeof(*packet_name)))
+    return "UNKNOWN";
+  return packet_name[type];
+}
+
+static const char *command_name[] = {
+  "NONE",
+  "WHOIS",
+  "WHOWAS",
+  "IDENTIFY",
+  "NICK",
+  "LIST",
+  "TOPIC",
+  "INVITE",
+  "QUIT",
+  "KILL",
+  "INFO",
+  "STATS",
+  "PING",
+  "OPER",
+  "JOIN",
+  "MOTD",
+  "UMODE",
+  "CMODE",
+  "CUMODE",
+  "KICK",
+  "BAN",
+  "DETACH",
+  "WATCH",
+  "SILCOPER",
+  "LEAVE",
+  "USERS",
+  "GETKEY",
+  "SERVICE",
+};
+
+/* Returns command name */
+
+const char *silc_get_command_name(unsigned char command)
+{
+  if (command >= SILC_COMMAND_RESERVED)
+    return "RESERVED";
+  if (command >= SILC_COMMAND_PRIVATE)
+    return "PRIVATE RANGE";
+  if (command > (sizeof(command_name) / sizeof(*command_name)))
+    return "UNKNOWN";
+  return command_name[command];
+}
+
+/* Return TRUE if `smaller' is smaller than `bigger'. */
+
+bool silc_compare_timeval(struct timeval *smaller, 
+                         struct timeval *bigger)
+{
+  if ((smaller->tv_sec < bigger->tv_sec) ||
+      ((smaller->tv_sec == bigger->tv_sec) &&
+       (smaller->tv_usec < bigger->tv_usec)))
+    return TRUE;
+
+  return FALSE;
+}