Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcstrutil.c
index fadd31174069a4c6eb49adbb51c45d52e01f6ceb..3251c2b0a4d47c43255ca71746e17cf899ffedb4 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2005 Pekka Riikonen
+  Copyright (C) 2002 - 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
 #include "silc.h"
 #include "silcstrutil.h"
 
-static unsigned char pem_enc[64] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
-   data string. */
+/* Concatenates the `src' into `dest'.  If `src_len' is more than the
+   size of the `dest' (minus NULL at the end) the `src' will be
+   truncated to fit. */
 
-char *silc_pem_encode(unsigned char *data, SilcUInt32 len)
+char *silc_strncat(char *dest, SilcUInt32 dest_size,
+                  const char *src, SilcUInt32 src_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));
+  int len;
 
-  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;
-    }
-  }
+  dest[dest_size - 1] = '\0';
 
-  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] = '=';
-    }
+  len = dest_size - 1 - strlen(dest);
+  if (len < src_len) {
+    if (len > 0)
+      strncat(dest, src, len);
+  } else {
+    strncat(dest, src, src_len);
   }
 
-  return pem;
+  return dest;
 }
 
-/* Same as above but puts newline ('\n') every 72 characters. */
+/* Compares two strings. Strings may include wildcards '*' and '?'.
+   Returns TRUE if strings match. */
 
-char *silc_pem_encode_file(unsigned char *data, SilcUInt32 data_len)
+int silc_string_compare(char *string1, char *string2)
 {
-  int i, j;
-  SilcUInt32 len, cols;
-  char *pem, *pem2;
-
-  pem = silc_pem_encode(data, data_len);
-  len = strlen(pem);
+  int i;
+  int slen1;
+  int slen2;
+  char *tmpstr1, *tmpstr2;
 
-  pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
+  if (!string1 || !string2)
+    return FALSE;
 
-  for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
-    if (cols == 72) {
-      pem2[i] = '\n';
-      cols = 0;
-      len++;
-      continue;
+  slen1 = strlen(string1);
+  slen2 = strlen(string2);
+
+  /* See if they are same already */
+  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)) {
+       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] = '?';
+      }
     }
+  }
 
-    pem2[i] = pem[j++];
+  /* 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;
   }
 
-  silc_free(pem);
-  return pem2;
+  memset(tmpstr1, 0, slen1);
+  memset(tmpstr2, 0, slen2);
+  silc_free(tmpstr1);
+  silc_free(tmpstr2);
+  return FALSE;
 }
 
-/* Decodes PEM into data. Returns the decoded data. */
+/* Splits a string containing separator `ch' and returns an array of the
+   splitted strings. */
 
-unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len,
-                              SilcUInt32 *ret_len)
+char **silc_string_split(const char *string, char ch, int *ret_count)
 {
-  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 **splitted = NULL, sep[2], *item, *cp;
+  int i = 0, len;
+
+  if (!string || !ret_count) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return NULL;
   }
 
-  char_count = 0;
-  bits = 0;
-  j = 0;
+  splitted = silc_calloc(1, sizeof(*splitted));
+  if (!splitted)
+    return NULL;
 
-  if (!pem_len)
-    len = strlen(pem);
-  else
-    len = pem_len;
+  if (!strchr(string, ch)) {
+    splitted[0] = silc_memdup(string, strlen(string));
+    *ret_count = 1;
+    return splitted;
+  }
 
-  data = silc_calloc(((len * 6) / 8), sizeof(*data));
+  sep[0] = ch;
+  sep[1] = '\0';
+  cp = (char *)string;
+  while (cp) {
+    len = strcspn(cp, sep);
+    if (!len)
+      break;
 
-  for (i = 0; i < len; i++) {
-    c = pem[i];
+    item = silc_memdup(cp, len);
+    if (!item) {
+      silc_free(splitted);
+      return NULL;
+    }
 
-    if (c == '=')
-      break;
+    cp += len;
+    if (strlen(cp) == 0)
+      cp = NULL;
+    else
+      cp++;
 
-    if (c > 127 || !ialpha[c])
-      continue;
+    splitted[i++] = item;
 
-    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;
+    if (cp) {
+      splitted = silc_realloc(splitted, (i + 1) * sizeof(*splitted));
+      if (!splitted)
+       return NULL;
     }
   }
+  *ret_count = 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 (ret_len)
-    *ret_len = j;
-
-  return data;
+  return splitted;
 }
 
-/* Concatenates the `src' into `dest'.  If `src_len' is more than the
-   size of the `dest' (minus NULL at the end) the `src' will be
-   truncated to fit. */
+/* Inspects the `string' for wildcards and returns regex string that can
+   be used by the GNU regex library. A comma (`,') in the `string' means
+   that the string is list. */
 
-char *silc_strncat(char *dest, SilcUInt32 dest_size,
-                  const char *src, SilcUInt32 src_len)
+char *silc_string_regexify(const char *string)
 {
-  int len;
-
-  dest[dest_size - 1] = '\0';
+  int i, len, count;
+  char *regex;
 
-  len = dest_size - 1 - strlen(dest);
-  if (len < src_len) {
-    if (len > 0)
-      strncat(dest, src, len);
-  } else {
-    strncat(dest, src, src_len);
+  if (!string) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return NULL;
   }
 
-  return dest;
-}
-
-/* Checks that the 'identifier' string is valid identifier string
-   and does not contain any unassigned or prohibited character.  This
-   function is used to check for valid nicknames, channel names,
-   server names, usernames, hostnames, service names, algorithm names,
-   other security property names, and SILC Public Key name. */
-
-unsigned char *silc_identifier_check(const unsigned char *identifier,
-                                    SilcUInt32 identifier_len,
-                                    SilcStringEncoding identifier_encoding,
-                                    SilcUInt32 max_allowed_length,
-                                    SilcUInt32 *out_len)
-{
-  unsigned char *utf8s;
-  SilcUInt32 utf8s_len;
-  SilcStringprepStatus status;
+  len = strlen(string);
+  count = 4;
+  for (i = 0; i < len; i++) {
+    if (string[i] == '*' || string[i] == '?')
+      count++;                 /* Will add '.' */
+    if (string[i] == ',')
+      count += 2;              /* Will add '|' and '^' */
+  }
 
-  if (!identifier || !identifier_len)
+  regex = silc_calloc(len + count + 1, sizeof(*regex));
+  if (!regex)
     return NULL;
 
-  if (max_allowed_length && identifier_len > max_allowed_length)
-    return NULL;
+  count = 0;
+  regex[count++] = '(';
+  regex[count++] = '^';
 
-  status = silc_stringprep(identifier, identifier_len,
-                          identifier_encoding, SILC_IDENTIFIER_PREP, 0,
-                          &utf8s, &utf8s_len, SILC_STRING_UTF8);
-  if (status != SILC_STRINGPREP_OK) {
-    SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
-    return NULL;
+  for (i = 0; i < len; i++) {
+    if (string[i] == '*' || string[i] == '?') {
+      regex[count] = '.';
+      count++;
+    } else if (string[i] == ',') {
+      if (i + 2 == len)
+       continue;
+      regex[count++] = '|';
+      regex[count++] = '^';
+      continue;
+    }
+
+    regex[count++] = string[i];
   }
 
-  if (out_len)
-    *out_len = utf8s_len;
+  regex[count++] = ')';
+  regex[count] = '$';
 
-  return utf8s;
+  return regex;
 }
 
-/* Same as above but does not allocate memory, just checks the
-   validity of the string. */
+/* Combines two regex strings into one regex string so that they can be
+   used as one by the GNU regex library. The `string2' is combine into
+   the `string1'. */
 
-SilcBool silc_identifier_verify(const unsigned char *identifier,
-                           SilcUInt32 identifier_len,
-                           SilcStringEncoding identifier_encoding,
-                           SilcUInt32 max_allowed_length)
+char *silc_string_regex_combine(const char *string1, const char *string2)
 {
-  SilcStringprepStatus status;
+  char *tmp;
+  int len1, len2;
 
-  if (!identifier || !identifier_len)
-    return FALSE;
-
-  if (max_allowed_length && identifier_len > max_allowed_length)
-    return FALSE;
-
-  status = silc_stringprep(identifier, identifier_len,
-                          identifier_encoding, SILC_IDENTIFIER_PREP, 0,
-                          NULL, NULL, SILC_STRING_UTF8);
-  if (status != SILC_STRINGPREP_OK) {
-    SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
-    return FALSE;
-  }
-
-  return TRUE;
-}
-
-unsigned char *silc_channel_name_check(const unsigned char *identifier,
-                                      SilcUInt32 identifier_len,
-                                      SilcStringEncoding identifier_encoding,
-                                      SilcUInt32 max_allowed_length,
-                                      SilcUInt32 *out_len)
-{
-  unsigned char *utf8s;
-  SilcUInt32 utf8s_len;
-  SilcStringprepStatus status;
-
-  if (!identifier || !identifier_len)
-    return NULL;
-
-  if (max_allowed_length && identifier_len > max_allowed_length)
-    return NULL;
-
-  status = silc_stringprep(identifier, identifier_len,
-                          identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
-                          &utf8s, &utf8s_len, SILC_STRING_UTF8);
-  if (status != SILC_STRINGPREP_OK) {
-    SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
+  if (!string1 || !string2) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return NULL;
   }
 
-  if (out_len)
-    *out_len = utf8s_len;
+  len1 = strlen(string1);
+  len2 = strlen(string2);
+
+  tmp = silc_calloc(2 + len1 + len2, sizeof(*tmp));
+  strncat(tmp, string1, len1 - 2);
+  strncat(tmp, "|", 1);
+  strncat(tmp, string2 + 1, len2 - 1);
 
-  return utf8s;
+  return tmp;
 }
 
-/* Same as above but does not allocate memory, just checks the
-   validity of the string. */
+/* Do regex match to the two strings `string1' and `string2'. If the
+   `string2' matches the `string1' this returns TRUE. */
 
-SilcBool silc_channel_name_verify(const unsigned char *identifier,
-                             SilcUInt32 identifier_len,
-                             SilcStringEncoding identifier_encoding,
-                             SilcUInt32 max_allowed_length)
+int silc_string_match(const char *string1, const char *string2)
 {
-  SilcStringprepStatus status;
+  char *s1;
+  int ret = FALSE;
 
-  if (!identifier || !identifier_len)
-    return FALSE;
-
-  if (max_allowed_length && identifier_len > max_allowed_length)
-    return FALSE;
-
-  status = silc_stringprep(identifier, identifier_len,
-                          identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
-                          NULL, NULL, SILC_STRING_UTF8);
-  if (status != SILC_STRINGPREP_OK) {
-    SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
-    return FALSE;
+  if (!string1 || !string2) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return ret;
   }
 
-  return TRUE;
+  s1 = silc_string_regexify(string1);
+  ret = silc_string_regex_match(s1, string2);
+  silc_free(s1);
+
+  return ret;
 }