service_new: ready_confirm_fd
[oweals/gnunet.git] / src / util / strings.c
index 40968956e18c48ea5f53f628f09834a113a8d36f..6a6cad6fe8b05304ac48d61c71c3ebb5467d331f 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      This file is part of GNUnet.
-     (C) 2005, 2006 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2005-2013 GNUnet e.V.
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet 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, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -14,8 +14,8 @@
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
 
      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
-     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-     Boston, MA 02111-1307, USA.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
 */
 
 /**
 #if HAVE_ICONV
 #include <iconv.h>
 #endif
 #if HAVE_ICONV
 #include <iconv.h>
 #endif
-#include "gnunet_common.h"
+#include "gnunet_crypto_lib.h"
 #include "gnunet_strings_lib.h"
 #include "gnunet_strings_lib.h"
+#include <unicase.h>
+#include <unistr.h>
+#include <uniconv.h>
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 
 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
@@ -45,7 +48,7 @@
  *
  * Unlike using "snprintf" with "%s", this function
  * will add 0-terminators after each string.  The
  *
  * Unlike using "snprintf" with "%s", this function
  * will add 0-terminators after each string.  The
- * "GNUNET_string_buffer_tokenize" function can be
+ * #GNUNET_string_buffer_tokenize() function can be
  * used to parse the buffer back into individual
  * strings.
  *
  * used to parse the buffer back into individual
  * strings.
  *
@@ -76,7 +79,7 @@ GNUNET_STRINGS_buffer_fill (char *buffer, size_t size, unsigned int count, ...)
     if (buffer != NULL)
     {
       GNUNET_assert (needed + slen <= size);
     if (buffer != NULL)
     {
       GNUNET_assert (needed + slen <= size);
-      memcpy (&buffer[needed], s, slen);
+      GNUNET_memcpy (&buffer[needed], s, slen);
     }
     needed += slen;
     count--;
     }
     needed += slen;
     count--;
@@ -195,7 +198,7 @@ struct ConversionTable
  * @param input input string to parse
  * @param table table with the conversion of unit names to numbers
  * @param output where to store the result
  * @param input input string to parse
  * @param table table with the conversion of unit names to numbers
  * @param output where to store the result
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 convert_with_table (const char *input,
  */
 static int
 convert_with_table (const char *input,
@@ -213,21 +216,33 @@ convert_with_table (const char *input,
   in = GNUNET_strdup (input);
   for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
   {
   in = GNUNET_strdup (input);
   for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
   {
-    i = 0;
-    while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
-      i++;
-    if (table[i].name != NULL)
-      last *= table[i].value;
-    else
+    do
     {
     {
-      ret += last;
-      last = 0;
-      if (1 != SSCANF (tok, "%llu", &last))
+      i = 0;
+      while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
+        i++;
+      if (table[i].name != NULL)
       {
       {
-        GNUNET_free (in);
-        return GNUNET_SYSERR;   /* expected number */
+        last *= table[i].value;
+        break; /* next tok */
       }
       }
-    }
+      else
+      {
+        char *endptr;
+        ret += last;
+        errno = 0;
+        last = strtoull (tok, &endptr, 10);
+        if ((0 != errno) || (endptr == tok))
+        {
+          GNUNET_free (in);
+          return GNUNET_SYSERR;   /* expected number */
+        }
+        if ('\0' == endptr[0])
+          break; /* next tok */
+        else
+          tok = endptr; /* and re-check (handles times like "10s") */
+      }
+    } while (GNUNET_YES);
   }
   ret += last;
   *output = ret;
   }
   ret += last;
   *output = ret;
@@ -241,7 +256,7 @@ convert_with_table (const char *input,
  *
  * @param fancy_size human readable string (i.e. 1 MB)
  * @param size set to the size in bytes
  *
  * @param fancy_size human readable string (i.e. 1 MB)
  * @param size set to the size in bytes
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 int
 GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
  */
 int
 GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
@@ -277,7 +292,7 @@ GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
  *
  * @param fancy_time human readable string (i.e. 1 minute)
  * @param rtime set to the relative time
  *
  * @param fancy_time human readable string (i.e. 1 minute)
  * @param rtime set to the relative time
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 int
 GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time,
  */
 int
 GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time,
@@ -285,91 +300,150 @@ GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time,
 {
   static const struct ConversionTable table[] =
   {
 {
   static const struct ConversionTable table[] =
   {
-    { "ms", 1},
-    { "s", 1000},
-    { "\"", 1000},
-    { "min", 60 * 1000},
-    { "minutes", 60 * 1000},
-    { "'", 60 * 1000},
-    { "h", 60 * 60 * 1000},
-    { "d", 24 * 60 * 60 * 1000},
-    { "a", 31557600 /* year */ },
+    { "us", 1},
+    { "ms", 1000 },
+    { "s", 1000 * 1000LL },
+    { "\"", 1000  * 1000LL },
+    { "m", 60 * 1000  * 1000LL},
+    { "min", 60 * 1000  * 1000LL},
+    { "minute", 60 * 1000  * 1000LL},
+    { "minutes", 60 * 1000  * 1000LL},
+    { "'", 60 * 1000  * 1000LL},
+    { "h", 60 * 60 * 1000  * 1000LL},
+    { "hour", 60 * 60 * 1000  * 1000LL},
+    { "hours", 60 * 60 * 1000  * 1000LL},
+    { "d", 24 * 60 * 60 * 1000LL * 1000LL},
+    { "day", 24 * 60 * 60 * 1000LL * 1000LL},
+    { "days", 24 * 60 * 60 * 1000LL * 1000LL},
+    { "week", 7 * 24 * 60 * 60 * 1000LL * 1000LL},
+    { "weeks", 7 * 24 * 60 * 60 * 1000LL * 1000LL},
+    { "year", 31536000000000LL /* year */ },
+    { "years", 31536000000000LL /* year */ },
+    { "a", 31536000000000LL /* year */ },
     { NULL, 0}
   };
   int ret;
   unsigned long long val;
 
     { NULL, 0}
   };
   int ret;
   unsigned long long val;
 
+  if (0 == strcasecmp ("forever", fancy_time))
+  {
+    *rtime = GNUNET_TIME_UNIT_FOREVER_REL;
+    return GNUNET_OK;
+  }
   ret = convert_with_table (fancy_time,
                            table,
                            &val);
   ret = convert_with_table (fancy_time,
                            table,
                            &val);
-  rtime->rel_value = (uint64_t) val;
+  rtime->rel_value_us = (uint64_t) val;
   return ret;
 }
 
   return ret;
 }
 
+
+/**
+ * Convert a given fancy human-readable time to our internal
+ * representation. The human-readable time is expected to be
+ * in local time, whereas the returned value will be in UTC.
+ *
+ * @param fancy_time human readable string (i.e. %Y-%m-%d %H:%M:%S)
+ * @param atime set to the absolute time
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+int
+GNUNET_STRINGS_fancy_time_to_absolute (const char *fancy_time,
+                                       struct GNUNET_TIME_Absolute *atime)
+{
+  struct tm tv;
+  time_t t;
+
+  if (0 == strcasecmp ("end of time",
+                       fancy_time))
+  {
+    *atime = GNUNET_TIME_UNIT_FOREVER_ABS;
+    return GNUNET_OK;
+  }
+  memset (&tv, 0, sizeof (tv));
+  if ( (NULL == strptime (fancy_time, "%a %b %d %H:%M:%S %Y", &tv)) &&
+       (NULL == strptime (fancy_time, "%c", &tv)) &&
+       (NULL == strptime (fancy_time, "%Ec", &tv)) &&
+       (NULL == strptime (fancy_time, "%Y-%m-%d %H:%M:%S", &tv)) &&
+       (NULL == strptime (fancy_time, "%Y-%m-%d %H:%M", &tv)) &&
+       (NULL == strptime (fancy_time, "%x", &tv)) &&
+       (NULL == strptime (fancy_time, "%Ex", &tv)) &&
+       (NULL == strptime (fancy_time, "%Y-%m-%d", &tv)) &&
+       (NULL == strptime (fancy_time, "%Y-%m", &tv)) &&
+       (NULL == strptime (fancy_time, "%Y", &tv)) )
+    return GNUNET_SYSERR;
+  t = mktime (&tv);
+  atime->abs_value_us = (uint64_t) ((uint64_t) t * 1000LL * 1000LL);
+  return GNUNET_OK;
+}
+
+
 /**
  * Convert the len characters long character sequence
  * given in input that is in the given input charset
  * to a string in given output charset.
 /**
  * Convert the len characters long character sequence
  * given in input that is in the given input charset
  * to a string in given output charset.
+ *
+ * @param input input string
+ * @param len number of bytes in @a input
+ * @param input_charset character set used for @a input
+ * @param output_charset desired character set for the return value
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
-GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, const char *output_charset)
+GNUNET_STRINGS_conv (const char *input,
+                    size_t len,
+                    const char *input_charset,
+                    const char *output_charset)
 {
   char *ret;
 {
   char *ret;
-
-#if ENABLE_NLS && HAVE_ICONV
-  size_t tmpSize;
-  size_t finSize;
-  char *tmp;
-  char *itmp;
-  iconv_t cd;
-
-  cd = iconv_open (output_charset, input_charset);
-  if (cd == (iconv_t) - 1)
+  uint8_t *u8_string;
+  char *encoded_string;
+  size_t u8_string_length;
+  size_t encoded_string_length;
+
+  u8_string = u8_conv_from_encoding (input_charset,
+                                    iconveh_error,
+                                    input, len,
+                                    NULL, NULL,
+                                    &u8_string_length);
+  if (NULL == u8_string)
   {
   {
-    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
-    LOG (GNUNET_ERROR_TYPE_WARNING, _("Character sets requested were `%s'->`%s'\n"),
-         input_charset, output_charset);
-    ret = GNUNET_malloc (len + 1);
-    memcpy (ret, input, len);
-    ret[len] = '\0';
-    return ret;
+    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_from_encoding");
+    goto fail;
   }
   }
-  tmpSize = 3 * len + 4;
-  tmp = GNUNET_malloc (tmpSize);
-  itmp = tmp;
-  finSize = tmpSize;
-  if (iconv (cd,
-#if FREEBSD || DARWIN || WINDOWS
-             (const char **) &input,
-#else
-             (char **) &input,
-#endif
-             &len, &itmp, &finSize) == SIZE_MAX)
+  if (0 == strcmp (output_charset, "UTF-8"))
   {
   {
-    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv");
-    iconv_close (cd);
-    GNUNET_free (tmp);
-    ret = GNUNET_malloc (len + 1);
-    memcpy (ret, input, len);
-    ret[len] = '\0';
+    ret = GNUNET_malloc (u8_string_length + 1);
+    GNUNET_memcpy (ret, u8_string, u8_string_length);
+    ret[u8_string_length] = '\0';
+    free (u8_string);
     return ret;
   }
     return ret;
   }
-  ret = GNUNET_malloc (tmpSize - finSize + 1);
-  memcpy (ret, tmp, tmpSize - finSize);
-  ret[tmpSize - finSize] = '\0';
-  GNUNET_free (tmp);
-  if (0 != iconv_close (cd))
-    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
+  encoded_string = u8_conv_to_encoding (output_charset, iconveh_error,
+                                       u8_string, u8_string_length,
+                                       NULL, NULL,
+                                       &encoded_string_length);
+  free (u8_string);
+  if (NULL == encoded_string)
+  {
+    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_to_encoding");
+    goto fail;
+  }
+  ret = GNUNET_malloc (encoded_string_length + 1);
+  GNUNET_memcpy (ret, encoded_string, encoded_string_length);
+  ret[encoded_string_length] = '\0';
+  free (encoded_string);
   return ret;
   return ret;
-#else
+ fail:
+  LOG (GNUNET_ERROR_TYPE_WARNING,
+       _("Character sets requested were `%s'->`%s'\n"),
+       "UTF-8", output_charset);
   ret = GNUNET_malloc (len + 1);
   ret = GNUNET_malloc (len + 1);
-  memcpy (ret, input, len);
+  GNUNET_memcpy (ret, input, len);
   ret[len] = '\0';
   return ret;
   ret[len] = '\0';
   return ret;
-#endif
 }
 
 
 }
 
 
@@ -377,31 +451,86 @@ GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, c
  * Convert the len characters long character sequence
  * given in input that is in the given charset
  * to UTF-8.
  * Convert the len characters long character sequence
  * given in input that is in the given charset
  * to UTF-8.
+ *
+ * @param input the input string (not necessarily 0-terminated)
+ * @param len the number of bytes in the @a input
+ * @param charset character set to convert from
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
-GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
+GNUNET_STRINGS_to_utf8 (const char *input,
+                        size_t len,
+                        const char *charset)
 {
   return GNUNET_STRINGS_conv (input, len, charset, "UTF-8");
 }
 
 {
   return GNUNET_STRINGS_conv (input, len, charset, "UTF-8");
 }
 
+
 /**
  * Convert the len bytes-long UTF-8 string
  * given in input to the given charset.
 /**
  * Convert the len bytes-long UTF-8 string
  * given in input to the given charset.
-
+ *
+ * @param input the input string (not necessarily 0-terminated)
+ * @param len the number of bytes in the @a input
+ * @param charset character set to convert to
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
  * @return the converted string (0-terminated),
  *  if conversion fails, a copy of the orignal
  *  string is returned.
  */
 char *
-GNUNET_STRINGS_from_utf8 (const char *input, size_t len, const char *charset)
+GNUNET_STRINGS_from_utf8 (const char *input,
+                          size_t len,
+                          const char *charset)
 {
   return GNUNET_STRINGS_conv (input, len, "UTF-8", charset);
 }
 
 
 {
   return GNUNET_STRINGS_conv (input, len, "UTF-8", charset);
 }
 
 
+/**
+ * Convert the utf-8 input string to lowercase.
+ * Output needs to be allocated appropriately.
+ *
+ * @param input input string
+ * @param output output buffer
+ */
+void
+GNUNET_STRINGS_utf8_tolower (const char *input,
+                             char *output)
+{
+  uint8_t *tmp_in;
+  size_t len;
+
+  tmp_in = u8_tolower ((uint8_t*)input, strlen ((char *) input),
+                       NULL, UNINORM_NFD, NULL, &len);
+  GNUNET_memcpy(output, tmp_in, len);
+  output[len] = '\0';
+  free(tmp_in);
+}
+
+
+/**
+ * Convert the utf-8 input string to uppercase.
+ * Output needs to be allocated appropriately.
+ *
+ * @param input input string
+ * @param output output buffer
+ */
+void
+GNUNET_STRINGS_utf8_toupper(const char *input,
+                            char *output)
+{
+  uint8_t *tmp_in;
+  size_t len;
+
+  tmp_in = u8_toupper ((uint8_t*)input, strlen ((char *) input),
+                       NULL, UNINORM_NFD, NULL, &len);
+  GNUNET_memcpy (output, tmp_in, len);
+  output[len] = '\0';
+  free (tmp_in);
+}
+
 
 /**
  * Complete filename (a la shell) from abbrevition.
 
 /**
  * Complete filename (a la shell) from abbrevition.
@@ -414,10 +543,8 @@ char *
 GNUNET_STRINGS_filename_expand (const char *fil)
 {
   char *buffer;
 GNUNET_STRINGS_filename_expand (const char *fil)
 {
   char *buffer;
-
 #ifndef MINGW
   size_t len;
 #ifndef MINGW
   size_t len;
-  size_t n;
   char *fm;
   const char *fil_ptr;
 #else
   char *fm;
   const char *fil_ptr;
 #else
@@ -474,7 +601,8 @@ GNUNET_STRINGS_filename_expand (const char *fil)
     }
     if (fm == NULL)
     {
     }
     if (fm == NULL)
     {
-      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd");
+      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+                    "getcwd");
       buffer = getenv ("PWD");  /* alternative */
       if (buffer != NULL)
         fm = GNUNET_strdup (buffer);
       buffer = getenv ("PWD");  /* alternative */
       if (buffer != NULL)
         fm = GNUNET_strdup (buffer);
@@ -482,9 +610,9 @@ GNUNET_STRINGS_filename_expand (const char *fil)
     if (fm == NULL)
       fm = GNUNET_strdup ("./");        /* give up */
   }
     if (fm == NULL)
       fm = GNUNET_strdup ("./");        /* give up */
   }
-  n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
-  buffer = GNUNET_malloc (n);
-  GNUNET_snprintf (buffer, n, "%s%s%s", fm,
+  GNUNET_asprintf (&buffer,
+                   "%s%s%s",
+                   fm,
                    (fm[strlen (fm) - 1] ==
                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
   GNUNET_free (fm);
                    (fm[strlen (fm) - 1] ==
                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
   GNUNET_free (fm);
@@ -495,23 +623,29 @@ GNUNET_STRINGS_filename_expand (const char *fil)
   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
   {
     SetErrnoFromWinError (lRet);
   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
   {
     SetErrnoFromWinError (lRet);
-    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "plibc_conv_to_win_path");
+    LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+                  "plibc_conv_to_win_path");
     return NULL;
   }
   /* is the path relative? */
     return NULL;
   }
   /* is the path relative? */
-  if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
+  if ( (0 != strncmp (fn + 1, ":\\", 2)) &&
+       (0 != strncmp (fn, "\\\\", 2)) )
   {
     char szCurDir[MAX_PATH + 1];
 
   {
     char szCurDir[MAX_PATH + 1];
 
-    lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
+    lRet = GetCurrentDirectory (MAX_PATH + 1,
+                                szCurDir);
     if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
     {
       SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
     if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
     {
       SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
-      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetCurrentDirectory");
+      LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
+                    "GetCurrentDirectory");
       return NULL;
     }
       return NULL;
     }
-    buffer = GNUNET_malloc (MAX_PATH + 1);
-    GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
+    GNUNET_asprintf (&buffer,
+                     "%s\\%s",
+                     szCurDir,
+                     fn);
     GNUNET_free (fn);
     fn = buffer;
   }
     GNUNET_free (fn);
     fn = buffer;
   }
@@ -523,67 +657,118 @@ GNUNET_STRINGS_filename_expand (const char *fil)
 
 /**
  * Give relative time in human-readable fancy format.
 
 /**
  * Give relative time in human-readable fancy format.
+ * This is one of the very few calls in the entire API that is
+ * NOT reentrant!
  *
  * @param delta time in milli seconds
  *
  * @param delta time in milli seconds
+ * @param do_round are we allowed to round a bit?
  * @return time as human-readable string
  */
  * @return time as human-readable string
  */
-char *
-GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta)
+const char *
+GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta,
+                                       int do_round)
 {
 {
-  const char *unit = _( /* time unit */ "ms");
-  char *ret;
-  uint64_t dval = delta.rel_value;
-
-  if (delta.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
-    return GNUNET_strdup (_("eternity"));
-  if (dval > 5 * 1000)
+  static char buf[128];
+  const char *unit = _( /* time unit */ "µs");
+  uint64_t dval = delta.rel_value_us;
+
+  if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == delta.rel_value_us)
+    return _("forever");
+  if (0 == delta.rel_value_us)
+    return _("0 ms");
+  if ( ( (GNUNET_YES == do_round) &&
+        (dval > 5 * 1000) ) ||
+       (0 == (dval % 1000) ))
   {
     dval = dval / 1000;
   {
     dval = dval / 1000;
-    unit = _( /* time unit */ "s");
-    if (dval > 5 * 60)
+    unit = _( /* time unit */ "ms");
+    if ( ( (GNUNET_YES == do_round) &&
+          (dval > 5 * 1000) ) ||
+        (0 == (dval % 1000) ))
     {
     {
-      dval = dval / 60;
-      unit = _( /* time unit */ "m");
-      if (dval > 5 * 60)
+      dval = dval / 1000;
+      unit = _( /* time unit */ "s");
+      if ( ( (GNUNET_YES == do_round) &&
+            (dval > 5 * 60) ) ||
+          (0 == (dval % 60) ) )
       {
       {
-        dval = dval / 60;
-        unit = _( /* time unit */ "h");
-        if (dval > 5 * 24)
-        {
-          dval = dval / 24;
-          unit = _( /* time unit */ " days");
-        }
+       dval = dval / 60;
+       unit = _( /* time unit */ "m");
+       if ( ( (GNUNET_YES == do_round) &&
+              (dval > 5 * 60) ) ||
+            (0 == (dval % 60) ))
+       {
+         dval = dval / 60;
+         unit = _( /* time unit */ "h");
+         if ( ( (GNUNET_YES == do_round) &&
+                (dval > 5 * 24) ) ||
+              (0 == (dval % 24)) )
+         {
+           dval = dval / 24;
+           if (1 == dval)
+             unit = _( /* time unit */ "day");
+           else
+             unit = _( /* time unit */ "days");
+         }
+       }
       }
     }
   }
       }
     }
   }
-  GNUNET_asprintf (&ret, "%llu %s", dval, unit);
-  return ret;
+  GNUNET_snprintf (buf, sizeof (buf),
+                  "%llu %s", dval, unit);
+  return buf;
 }
 
 
 /**
 }
 
 
 /**
- * "man ctime_r", except for GNUnet time; also, unlike ctime, the
- * return value does not include the newline character.
+ * "asctime", except for GNUnet time.  Converts a GNUnet internal
+ * absolute time (which is in UTC) to a string in local time.
+ * Note that the returned value will be overwritten if this function
+ * is called again.
  *
  *
- * @param t time to convert
- * @return absolute time in human-readable format
+ * @param t the absolute time to convert
+ * @return timestamp in human-readable form in local time
  */
  */
-char *
+const char *
 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
 {
 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
 {
+  static char buf[255];
   time_t tt;
   time_t tt;
-  char *ret;
-
-  if (t.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
-    return GNUNET_strdup (_("end of time"));
-  tt = t.abs_value / 1000;
-#ifdef ctime_r
-  ret = ctime_r (&tt, GNUNET_malloc (32));
+  struct tm *tp;
+
+  if (t.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
+    return _("end of time");
+  tt = t.abs_value_us / 1000LL / 1000LL;
+  tp = localtime (&tt);
+  /* This is hacky, but i don't know a way to detect libc character encoding.
+   * Just expect utf8 from glibc these days.
+   * As for msvcrt, use the wide variant, which always returns utf16
+   * (otherwise we'd have to detect current codepage or use W32API character
+   * set conversion routines to convert to UTF8).
+   */
+#ifndef WINDOWS
+  strftime (buf, sizeof (buf), "%a %b %d %H:%M:%S %Y", tp);
 #else
 #else
-  ret = GNUNET_strdup (ctime (&tt));
+  {
+    static wchar_t wbuf[255];
+    uint8_t *conved;
+    size_t ssize;
+
+    wcsftime (wbuf, sizeof (wbuf) / sizeof (wchar_t),
+        L"%a %b %d %H:%M:%S %Y", tp);
+
+    ssize = sizeof (buf);
+    conved = u16_to_u8 (wbuf, sizeof (wbuf) / sizeof (wchar_t),
+        (uint8_t *) buf, &ssize);
+    if (conved != (uint8_t *) buf)
+    {
+      strncpy (buf, (char *) conved, sizeof (buf));
+      buf[255 - 1] = '\0';
+      free (conved);
+    }
+  }
 #endif
 #endif
-  ret[strlen (ret) - 1] = '\0';
-  return ret;
+  return buf;
 }
 
 
 }
 
 
@@ -611,7 +796,8 @@ GNUNET_STRINGS_get_short_name (const char *filename)
 
 
 /**
 
 
 /**
- * Get the numeric value corresponding to a character.
+ * Get the decoded value corresponding to a character according to Crockford
+ * Base32 encoding.
  *
  * @param a a character
  * @return corresponding numeric value
  *
  * @param a a character
  * @return corresponding numeric value
@@ -619,44 +805,80 @@ GNUNET_STRINGS_get_short_name (const char *filename)
 static unsigned int
 getValue__ (unsigned char a)
 {
 static unsigned int
 getValue__ (unsigned char a)
 {
+  unsigned int dec;
+
+  switch (a)
+  {
+  case 'O':
+  case 'o':
+    a = '0';
+    break;
+  case 'i':
+  case 'I':
+  case 'l':
+  case 'L':
+    a = '1';
+    break;
+    /* also consider U to be V */
+  case 'u':
+  case 'U':
+    a = 'V';
+    break;
+  default:
+    break;
+  }
   if ((a >= '0') && (a <= '9'))
     return a - '0';
   if ((a >= '0') && (a <= '9'))
     return a - '0';
-  if ((a >= 'A') && (a <= 'V'))
-    return (a - 'A' + 10);
+  if ((a >= 'a') && (a <= 'z'))
+    a = toupper (a);
+    /* return (a - 'a' + 10); */
+  dec = 0;
+  if ((a >= 'A') && (a <= 'Z'))
+  {
+    if ('I' < a)
+      dec++;
+    if ('L' < a)
+      dec++;
+    if ('O' < a)
+      dec++;
+    if ('U' < a)
+      dec++;
+    return (a - 'A' + 10 - dec);
+  }
   return -1;
 }
 
 
 /**
   return -1;
 }
 
 
 /**
- * Convert binary data to ASCII encoding.  The ASCII encoding is rather
- * GNUnet specific.  It was chosen such that it only uses characters
- * in [0-9A-V], can be produced without complex arithmetics and uses a
- * small number of characters.  
- * Does not append 0-terminator, but returns a pointer to the place where
- * it should be placed, if needed.
+ * Convert binary data to ASCII encoding using Crockford Base32 encoding.
+ * Returns a pointer to the byte after the last byte in the string, that
+ * is where the 0-terminator was placed if there was room.
  *
  * @param data data to encode
  * @param size size of data (in bytes)
  * @param out buffer to fill
  * @param out_size size of the buffer. Must be large enough to hold
  *
  * @param data data to encode
  * @param size size of data (in bytes)
  * @param out buffer to fill
  * @param out_size size of the buffer. Must be large enough to hold
- * ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes
- * @return pointer to the next byte in 'out' or NULL on error.
+ * (size * 8 + 4) / 5 bytes
+ * @return pointer to the next byte in @a out or NULL on error.
  */
 char *
  */
 char *
-GNUNET_STRINGS_data_to_string (const unsigned char *data, size_t size, char *out, size_t out_size)
+GNUNET_STRINGS_data_to_string (const void *data,
+                               size_t size,
+                               char *out,
+                               size_t out_size)
 {
   /**
 {
   /**
-   * 32 characters for encoding 
+   * 32 characters for encoding
    */
    */
-  static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+  static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
   unsigned int wpos;
   unsigned int rpos;
   unsigned int bits;
   unsigned int vbit;
   unsigned int wpos;
   unsigned int rpos;
   unsigned int bits;
   unsigned int vbit;
+  const unsigned char *udata;
 
 
-  GNUNET_assert (data != NULL);
-  GNUNET_assert (out != NULL);
-  if (out_size < (((size*8) + ((size*8) % 5)) % 5))
+  udata = data;
+  if (out_size < (size * 8 + 4) / 5)
   {
     GNUNET_break (0);
     return NULL;
   {
     GNUNET_break (0);
     return NULL;
@@ -669,7 +891,7 @@ GNUNET_STRINGS_data_to_string (const unsigned char *data, size_t size, char *out
   {
     if ((rpos < size) && (vbit < 5))
     {
   {
     if ((rpos < size) && (vbit < 5))
     {
-      bits = (bits << 8) | data[rpos++];   /* eat 8 more bits */
+      bits = (bits << 8) | udata[rpos++];   /* eat 8 more bits */
       vbit += 8;
     }
     if (vbit < 5)
       vbit += 8;
     }
     if (vbit < 5)
@@ -686,29 +908,59 @@ GNUNET_STRINGS_data_to_string (const unsigned char *data, size_t size, char *out
     out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
     vbit -= 5;
   }
     out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
     vbit -= 5;
   }
-  if (wpos != out_size)
+  GNUNET_assert (0 == vbit);
+  if (wpos < out_size)
+    out[wpos] = '\0';
+  return &out[wpos];
+}
+
+
+/**
+ * Return the base32crockford encoding of the given buffer.
+ *
+ * The returned string will be freshly allocated, and must be free'd
+ * with GNUNET_free().
+ *
+ * @param buffer with data
+ * @param size size of the buffer
+ * @return freshly allocated, null-terminated string
+ */
+char *
+GNUNET_STRINGS_data_to_string_alloc (const void *buf,
+                                     size_t size)
+{
+  char *str_buf;
+  size_t len = size * 8;
+  char *end;
+
+  if (len % 5 > 0)
+    len += 5 - len % 5;
+  len /= 5;
+  str_buf = GNUNET_malloc (len + 1);
+  end = GNUNET_STRINGS_data_to_string (buf, size, str_buf, len);
+  if (NULL == end)
   {
   {
-    GNUNET_break (0);
+    GNUNET_free (str_buf);
     return NULL;
   }
     return NULL;
   }
-  GNUNET_assert (vbit == 0);
-  return &out[wpos];
+  *end = '\0';
+  return str_buf;
 }
 
 
 /**
 }
 
 
 /**
- * Convert ASCII encoding back to data
- * out_size must match exactly the size of the data before it was encoded.
+ * Convert Crockford Base32hex encoding back to data.
+ * @a out_size must match exactly the size of the data before it was encoded.
  *
  * @param enc the encoding
  *
  * @param enc the encoding
- * @param enclen number of characters in 'enc' (without 0-terminator, which can be missing)
+ * @param enclen number of characters in @a enc (without 0-terminator, which can be missing)
  * @param out location where to store the decoded data
  * @param out location where to store the decoded data
- * @param out_size sizeof the output buffer
- * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
+ * @param out_size size of the output buffer @a out
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if result has the wrong encoding
  */
 int
 GNUNET_STRINGS_string_to_data (const char *enc, size_t enclen,
  */
 int
 GNUNET_STRINGS_string_to_data (const char *enc, size_t enclen,
-                              unsigned char *out, size_t out_size)
+                              void *out, size_t out_size)
 {
   unsigned int rpos;
   unsigned int wpos;
 {
   unsigned int rpos;
   unsigned int wpos;
@@ -716,41 +968,55 @@ GNUNET_STRINGS_string_to_data (const char *enc, size_t enclen,
   unsigned int vbit;
   int ret;
   int shift;
   unsigned int vbit;
   int ret;
   int shift;
-  int encoded_len = out_size * 8;
-  if (encoded_len % 5 > 0)
+  unsigned char *uout;
+  unsigned int encoded_len = out_size * 8;
+
+  if (0 == enclen)
+  {
+    if (0 == out_size)
+      return GNUNET_OK;
+    return GNUNET_SYSERR;
+  }
+  uout = out;
+  wpos = out_size;
+  rpos = enclen;
+  if ((encoded_len % 5) > 0)
   {
     vbit = encoded_len % 5; /* padding! */
     shift = 5 - vbit;
   {
     vbit = encoded_len % 5; /* padding! */
     shift = 5 - vbit;
+    bits = (ret = getValue__ (enc[--rpos])) >> shift;
   }
   else
   {
   }
   else
   {
-    vbit = 0;
+    vbit = 5;
     shift = 0;
     shift = 0;
+    bits = (ret = getValue__ (enc[--rpos]));
   }
   if ((encoded_len + shift) / 5 != enclen)
     return GNUNET_SYSERR;
   }
   if ((encoded_len + shift) / 5 != enclen)
     return GNUNET_SYSERR;
-
-  wpos = out_size;
-  rpos = enclen;
-  bits = (ret = getValue__ (enc[--rpos])) >> (5 - encoded_len % 5);
   if (-1 == ret)
     return GNUNET_SYSERR;
   while (wpos > 0)
   {
   if (-1 == ret)
     return GNUNET_SYSERR;
   while (wpos > 0)
   {
-    GNUNET_assert (rpos > 0);
+    if (0 == rpos)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
     bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
     if (-1 == ret)
       return GNUNET_SYSERR;
     vbit += 5;
     if (vbit >= 8)
     {
     bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
     if (-1 == ret)
       return GNUNET_SYSERR;
     vbit += 5;
     if (vbit >= 8)
     {
-      out[--wpos] = (unsigned char) bits;
+      uout[--wpos] = (unsigned char) bits;
       bits >>= 8;
       vbit -= 8;
     }
   }
       bits >>= 8;
       vbit -= 8;
     }
   }
-  GNUNET_assert (rpos == 0);
-  GNUNET_assert (vbit == 0);
+  if ( (0 != rpos) ||
+       (0 != vbit) )
+    return GNUNET_SYSERR;
   return GNUNET_OK;
 }
 
   return GNUNET_OK;
 }
 
@@ -817,7 +1083,7 @@ GNUNET_STRINGS_parse_uri (const char *path, char **scheme_part,
   if (scheme_part)
   {
     *scheme_part = GNUNET_malloc (post_scheme_part - path + 1);
   if (scheme_part)
   {
     *scheme_part = GNUNET_malloc (post_scheme_part - path + 1);
-    memcpy (*scheme_part, path, post_scheme_part - path);
+    GNUNET_memcpy (*scheme_part, path, post_scheme_part - path);
     (*scheme_part)[post_scheme_part - path] = '\0';
   }
   if (path_part)
     (*scheme_part)[post_scheme_part - path] = '\0';
   }
   if (path_part)
@@ -827,22 +1093,24 @@ GNUNET_STRINGS_parse_uri (const char *path, char **scheme_part,
 
 
 /**
 
 
 /**
- * Check whether 'filename' is absolute or not, and if it's an URI
+ * Check whether @a filename is absolute or not, and if it's an URI
  *
  * @param filename filename to check
  *
  * @param filename filename to check
- * @param can_be_uri GNUNET_YES to check for being URI, GNUNET_NO - to
+ * @param can_be_uri #GNUNET_YES to check for being URI, #GNUNET_NO - to
  *        assume it's not URI
  *        assume it's not URI
- * @param r_is_uri a pointer to an int that is set to GNUNET_YES if 'filename'
- *        is URI and to GNUNET_NO otherwise. Can be NULL. If 'can_be_uri' is
- *        not GNUNET_YES, *r_is_uri is set to GNUNET_NO.
+ * @param r_is_uri a pointer to an int that is set to #GNUNET_YES if @a filename
+ *        is URI and to #GNUNET_NO otherwise. Can be NULL. If @a can_be_uri is
+ *        not #GNUNET_YES, `* r_is_uri` is set to #GNUNET_NO.
  * @param r_uri_scheme a pointer to a char * that is set to a pointer to URI scheme.
  *        The string is allocated by the function, and should be freed with
  * @param r_uri_scheme a pointer to a char * that is set to a pointer to URI scheme.
  *        The string is allocated by the function, and should be freed with
- *        GNUNET_free (). Can be NULL.
- * @return GNUNET_YES if 'filename' is absolute, GNUNET_NO otherwise.
+ *        GNUNET_free(). Can be NULL.
+ * @return #GNUNET_YES if @a filename is absolute, #GNUNET_NO otherwise.
  */
 int
  */
 int
-GNUNET_STRINGS_path_is_absolute (const char *filename, int can_be_uri,
-    int *r_is_uri, char **r_uri_scheme)
+GNUNET_STRINGS_path_is_absolute (const char *filename,
+                                 int can_be_uri,
+                                 int *r_is_uri,
+                                 char **r_uri_scheme)
 {
 #if WINDOWS
   size_t len;
 {
 #if WINDOWS
   size_t len;
@@ -879,7 +1147,6 @@ GNUNET_STRINGS_path_is_absolute (const char *filename, int can_be_uri,
   }
   else
   {
   }
   else
   {
-    is_uri = GNUNET_NO;
     if (r_is_uri)
       *r_is_uri = GNUNET_NO;
   }
     if (r_is_uri)
       *r_is_uri = GNUNET_NO;
   }
@@ -900,177 +1167,816 @@ GNUNET_STRINGS_path_is_absolute (const char *filename, int can_be_uri,
 #define  S_ISLNK(m)    (((m)&_IFMT) == _IFLNK)
 #endif
 
 #define  S_ISLNK(m)    (((m)&_IFMT) == _IFLNK)
 #endif
 
+
 /**
 /**
- * Perform 'checks' on 'filename'
- * 
+ * Perform @a checks on @a filename.
+ *
  * @param filename file to check
  * @param checks checks to perform
  * @param filename file to check
  * @param checks checks to perform
- * @return GNUNET_YES if all checks pass, GNUNET_NO if at least one of them
- *         fails, GNUNET_SYSERR when a check can't be performed
+ * @return #GNUNET_YES if all checks pass, #GNUNET_NO if at least one of them
+ *         fails, #GNUNET_SYSERR when a check can't be performed
  */
 int
 GNUNET_STRINGS_check_filename (const char *filename,
                               enum GNUNET_STRINGS_FilenameCheck checks)
 {
   struct stat st;
  */
 int
 GNUNET_STRINGS_check_filename (const char *filename,
                               enum GNUNET_STRINGS_FilenameCheck checks)
 {
   struct stat st;
-  if (filename == NULL || filename[0] == '\0')
+  if ( (NULL == filename) || (filename[0] == '\0') )
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  if (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE)
+  if (0 != (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE))
     if (!GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL))
       return GNUNET_NO;
     if (!GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL))
       return GNUNET_NO;
-  if (checks & (GNUNET_STRINGS_CHECK_EXISTS
-      | GNUNET_STRINGS_CHECK_IS_DIRECTORY
-      | GNUNET_STRINGS_CHECK_IS_LINK))
+  if (0 != (checks & (GNUNET_STRINGS_CHECK_EXISTS
+                     | GNUNET_STRINGS_CHECK_IS_DIRECTORY
+                     | GNUNET_STRINGS_CHECK_IS_LINK)))
   {
   {
-    if (STAT (filename, &st))
+    if (0 != STAT (filename, &st))
     {
     {
-      if (checks & GNUNET_STRINGS_CHECK_EXISTS)
+      if (0 != (checks & GNUNET_STRINGS_CHECK_EXISTS))
         return GNUNET_NO;
       else
         return GNUNET_SYSERR;
     }
   }
         return GNUNET_NO;
       else
         return GNUNET_SYSERR;
     }
   }
-  if (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY)
+  if (0 != (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY))
     if (!S_ISDIR (st.st_mode))
       return GNUNET_NO;
     if (!S_ISDIR (st.st_mode))
       return GNUNET_NO;
-  if (checks & GNUNET_STRINGS_CHECK_IS_LINK)
+  if (0 != (checks & GNUNET_STRINGS_CHECK_IS_LINK))
     if (!S_ISLNK (st.st_mode))
       return GNUNET_NO;
   return GNUNET_YES;
 }
 
     if (!S_ISLNK (st.st_mode))
       return GNUNET_NO;
   return GNUNET_YES;
 }
 
-#define MAX_IPV6_ADDRLEN 47
-#define MAX_IPV4_ADDRLEN 21
-#define MAX_IP_ADDRLEN MAX_IPV6_ADDRLEN
-
 
 /**
  * Tries to convert 'zt_addr' string to an IPv6 address.
 
 /**
  * Tries to convert 'zt_addr' string to an IPv6 address.
- * 
+ * The string is expected to have the format "[ABCD::01]:80".
+ *
  * @param zt_addr 0-terminated string. May be mangled by the function.
  * @param zt_addr 0-terminated string. May be mangled by the function.
- * @param addrlen length of zt_addr (not counting 0-terminator).
+ * @param addrlen length of @a zt_addr (not counting 0-terminator).
  * @param r_buf a buffer to fill. Initially gets filled with zeroes,
  *        then its sin6_port, sin6_family and sin6_addr are set appropriately.
  * @param r_buf a buffer to fill. Initially gets filled with zeroes,
  *        then its sin6_port, sin6_family and sin6_addr are set appropriately.
- * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
- *         case the contents of r_buf are undefined.
+ * @return #GNUNET_OK if conversion succeded.
+ *         #GNUNET_SYSERR otherwise, in which
+ *         case the contents of @a r_buf are undefined.
  */
 int
  */
 int
-GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr, 
+GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr,
                                uint16_t addrlen,
                                struct sockaddr_in6 *r_buf)
 {
                                uint16_t addrlen,
                                struct sockaddr_in6 *r_buf)
 {
+  char zbuf[addrlen + 1];
   int ret;
   char *port_colon;
   unsigned int port;
 
   if (addrlen < 6)
     return GNUNET_SYSERR;
   int ret;
   char *port_colon;
   unsigned int port;
 
   if (addrlen < 6)
     return GNUNET_SYSERR;
-
-  port_colon = strrchr (zt_addr, ':');
-  if (port_colon == NULL)
+  GNUNET_memcpy (zbuf, zt_addr, addrlen);
+  if ('[' != zbuf[0])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("IPv6 address did not start with `['\n"));
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
+  }
+  zbuf[addrlen] = '\0';
+  port_colon = strrchr (zbuf, ':');
+  if (NULL == port_colon)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("IPv6 address did contain ':' to separate port number\n"));
+    return GNUNET_SYSERR;
+  }
+  if (']' != *(port_colon - 1))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("IPv6 address did contain ']' before ':' to separate port number\n"));
+    return GNUNET_SYSERR;
+  }
   ret = SSCANF (port_colon, ":%u", &port);
   ret = SSCANF (port_colon, ":%u", &port);
-  if (ret != 1 || port > 65535)
+  if ( (1 != ret) || (port > 65535) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("IPv6 address did contain a valid port number after the last ':'\n"));
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  port_colon[0] = '\0';
+  }
+  *(port_colon-1) = '\0';
   memset (r_buf, 0, sizeof (struct sockaddr_in6));
   memset (r_buf, 0, sizeof (struct sockaddr_in6));
-  ret = inet_pton (AF_INET6, zt_addr, &r_buf->sin6_addr);
+  ret = inet_pton (AF_INET6, &zbuf[1], &r_buf->sin6_addr);
   if (ret <= 0)
   if (ret <= 0)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Invalid IPv6 address `%s': %s\n"),
+               &zbuf[1],
+               STRERROR (errno));
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  r_buf->sin6_port = htonl (port);
+  }
+  r_buf->sin6_port = htons (port);
   r_buf->sin6_family = AF_INET6;
   r_buf->sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  r_buf->sin6_len = (u_char) sizeof (struct sockaddr_in6);
+#endif
   return GNUNET_OK;
 }
 
 
 /**
  * Tries to convert 'zt_addr' string to an IPv4 address.
   return GNUNET_OK;
 }
 
 
 /**
  * Tries to convert 'zt_addr' string to an IPv4 address.
- * 
+ * The string is expected to have the format "1.2.3.4:80".
+ *
  * @param zt_addr 0-terminated string. May be mangled by the function.
  * @param zt_addr 0-terminated string. May be mangled by the function.
- * @param addrlen length of zt_addr (not counting 0-terminator).
+ * @param addrlen length of @a zt_addr (not counting 0-terminator).
  * @param r_buf a buffer to fill.
  * @param r_buf a buffer to fill.
- * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which case
- *         the contents of r_buf are undefined.
+ * @return #GNUNET_OK if conversion succeded.
+ *         #GNUNET_SYSERR otherwise, in which case
+ *         the contents of @a r_buf are undefined.
  */
 int
 GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, uint16_t addrlen,
                                struct sockaddr_in *r_buf)
 {
  */
 int
 GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, uint16_t addrlen,
                                struct sockaddr_in *r_buf)
 {
-  unsigned int temps[5];
+  unsigned int temps[4];
   unsigned int port;
   unsigned int port;
-  int cnt;
+  unsigned int cnt;
 
   if (addrlen < 9)
     return GNUNET_SYSERR;
 
   if (addrlen < 9)
     return GNUNET_SYSERR;
-
   cnt = SSCANF (zt_addr, "%u.%u.%u.%u:%u", &temps[0], &temps[1], &temps[2], &temps[3], &port);
   cnt = SSCANF (zt_addr, "%u.%u.%u.%u:%u", &temps[0], &temps[1], &temps[2], &temps[3], &port);
-  if (cnt != 5)
+  if (5 != cnt)
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-
   for (cnt = 0; cnt < 4; cnt++)
     if (temps[cnt] > 0xFF)
       return GNUNET_SYSERR;
   if (port > 65535)
     return GNUNET_SYSERR;
   for (cnt = 0; cnt < 4; cnt++)
     if (temps[cnt] > 0xFF)
       return GNUNET_SYSERR;
   if (port > 65535)
     return GNUNET_SYSERR;
-
   r_buf->sin_family = AF_INET;
   r_buf->sin_family = AF_INET;
-  r_buf->sin_port = htonl (port);
+  r_buf->sin_port = htons (port);
   r_buf->sin_addr.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) +
   r_buf->sin_addr.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) +
-      (temps[2] << 8) + temps[3]);
+                                 (temps[2] << 8) + temps[3]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  r_buf->sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
   return GNUNET_OK;
 }
 
   return GNUNET_OK;
 }
 
+
 /**
 /**
- * Tries to convert 'addr' string to an IP (v4 or v6) address.
- * IPv6 address must have its address part enclosed in '()' parens
- * instead of '[]'.
+ * Tries to convert @a addr string to an IP (v4 or v6) address.
  * Will automatically decide whether to treat 'addr' as v4 or v6 address.
  * Will automatically decide whether to treat 'addr' as v4 or v6 address.
- * 
+ *
  * @param addr a string, may not be 0-terminated.
  * @param addr a string, may not be 0-terminated.
- * @param addrlen number of bytes in addr (if addr is 0-terminated,
+ * @param addrlen number of bytes in @a addr (if addr is 0-terminated,
  *        0-terminator should not be counted towards addrlen).
  * @param r_buf a buffer to fill.
  *        0-terminator should not be counted towards addrlen).
  * @param r_buf a buffer to fill.
- * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
+ * @return #GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
  *         case the contents of r_buf are undefined.
  */
 int
  *         case the contents of r_buf are undefined.
  */
 int
-GNUNET_STRINGS_to_address_ip (const char *addr, 
+GNUNET_STRINGS_to_address_ip (const char *addr,
                              uint16_t addrlen,
                              struct sockaddr_storage *r_buf)
 {
                              uint16_t addrlen,
                              struct sockaddr_storage *r_buf)
 {
-  uint16_t i;
-  char zt_addr[MAX_IP_ADDRLEN + 1];
-  uint16_t zt_len = addrlen <= MAX_IP_ADDRLEN ? addrlen : MAX_IP_ADDRLEN;
+  if (addr[0] == '[')
+    return GNUNET_STRINGS_to_address_ipv6 (addr,
+                                           addrlen,
+                                           (struct sockaddr_in6 *) r_buf);
+  return GNUNET_STRINGS_to_address_ipv4 (addr,
+                                         addrlen,
+                                         (struct sockaddr_in *) r_buf);
+}
+
 
 
-  if (addrlen < 1)
+/**
+ * Makes a copy of argv that consists of a single memory chunk that can be
+ * freed with a single call to GNUNET_free();
+ */
+static char *const *
+_make_continuous_arg_copy (int argc,
+                           char *const *argv)
+{
+  size_t argvsize = 0;
+  int i;
+  char **new_argv;
+  char *p;
+  for (i = 0; i < argc; i++)
+    argvsize += strlen (argv[i]) + 1 + sizeof (char *);
+  new_argv = GNUNET_malloc (argvsize + sizeof (char *));
+  p = (char *) &new_argv[argc + 1];
+  for (i = 0; i < argc; i++)
+  {
+    new_argv[i] = p;
+    strcpy (p, argv[i]);
+    p += strlen (argv[i]) + 1;
+  }
+  new_argv[argc] = NULL;
+  return (char *const *) new_argv;
+}
+
+
+/**
+ * Returns utf-8 encoded arguments.
+ * Does nothing (returns a copy of argc and argv) on any platform
+ * other than W32.
+ * Returned argv has u8argv[u8argc] == NULL.
+ * Returned argv is a single memory block, and can be freed with a single
+ *   GNUNET_free() call.
+ *
+ * @param argc argc (as given by main())
+ * @param argv argv (as given by main())
+ * @param u8argc a location to store new argc in (though it's th same as argc)
+ * @param u8argv a location to store new argv in
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+int
+GNUNET_STRINGS_get_utf8_args (int argc, char *const *argv, int *u8argc, char *const **u8argv)
+{
+#if WINDOWS
+  wchar_t *wcmd;
+  wchar_t **wargv;
+  int wargc;
+  int i;
+  char **split_u8argv;
+
+  wcmd = GetCommandLineW ();
+  if (NULL == wcmd)
+    return GNUNET_SYSERR;
+  wargv = CommandLineToArgvW (wcmd, &wargc);
+  if (NULL == wargv)
     return GNUNET_SYSERR;
 
     return GNUNET_SYSERR;
 
-  memset (zt_addr, 0, MAX_IP_ADDRLEN + 1);
-  strncpy (zt_addr, addr, zt_len);
+  split_u8argv = GNUNET_malloc (argc * sizeof (char *));
 
 
-  /* For URIs we use '(' and ')' instead of '[' and ']'. Do the substitution
-   * now, as GNUNET_STRINGS_to_address_ipv6() takes a proper []-enclosed IPv6
-   * address.
-   */
-  if (zt_addr[0] == '(')
+  for (i = 0; i < wargc; i++)
   {
   {
-    for (i = 0; i < zt_len; i++)
+    size_t strl;
+    /* Hopefully it will allocate us NUL-terminated strings... */
+    split_u8argv[i] = (char *) u16_to_u8 (wargv[i], wcslen (wargv[i]) + 1, NULL, &strl);
+    if (NULL == split_u8argv[i])
     {
     {
-      switch (zt_addr[i])
-      {
-      case '(':
-        zt_addr[i] = '[';
+      int j;
+      for (j = 0; j < i; j++)
+        free (split_u8argv[j]);
+      GNUNET_free (split_u8argv);
+      LocalFree (wargv);
+      return GNUNET_SYSERR;
+    }
+  }
+
+  *u8argv = _make_continuous_arg_copy (wargc, split_u8argv);
+  *u8argc = wargc;
+
+  for (i = 0; i < wargc; i++)
+    free (split_u8argv[i]);
+  free (split_u8argv);
+  return GNUNET_OK;
+#else
+  char *const *new_argv = (char *const *) _make_continuous_arg_copy (argc, argv);
+  *u8argv = new_argv;
+  *u8argc = argc;
+  return GNUNET_OK;
+#endif
+}
+
+
+/**
+ * Parse the given port policy.  The format is
+ * "[!]SPORT[-DPORT]".
+ *
+ * @param port_policy string to parse
+ * @param pp policy to fill in
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
+ *         @a port_policy is malformed
+ */
+static int
+parse_port_policy (const char *port_policy,
+                   struct GNUNET_STRINGS_PortPolicy *pp)
+{
+  const char *pos;
+  int s;
+  int e;
+  char eol[2];
+
+  pos = port_policy;
+  if ('!' == *pos)
+  {
+    pp->negate_portrange = GNUNET_YES;
+    pos++;
+  }
+  if (2 == sscanf (pos,
+                   "%u-%u%1s",
+                   &s, &e, eol))
+  {
+    if ( (0 == s) ||
+         (s > 0xFFFF) ||
+         (e < s) ||
+         (e > 0xFFFF) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Port not in range\n"));
+      return GNUNET_SYSERR;
+    }
+    pp->start_port = (uint16_t) s;
+    pp->end_port = (uint16_t) e;
+    return GNUNET_OK;
+  }
+  if (1 == sscanf (pos,
+                   "%u%1s",
+                   &s,
+                   eol))
+  {
+    if ( (0 == s) ||
+         (s > 0xFFFF) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Port not in range\n"));
+      return GNUNET_SYSERR;
+    }
+
+    pp->start_port = (uint16_t) s;
+    pp->end_port = (uint16_t) s;
+    return GNUNET_OK;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              _("Malformed port policy `%s'\n"),
+              port_policy);
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Parse an IPv4 network policy. The argument specifies a list of
+ * subnets. The format is
+ * <tt>(network[/netmask][:SPORT[-DPORT]];)*</tt> (no whitespace, must
+ * be terminated with a semicolon). The network must be given in
+ * dotted-decimal notation. The netmask can be given in CIDR notation
+ * (/16) or in dotted-decimal (/255.255.0.0).
+ *
+ * @param routeListX a string specifying the IPv4 subnets
+ * @return the converted list, terminated with all zeros;
+ *         NULL if the synatx is flawed
+ */
+struct GNUNET_STRINGS_IPv4NetworkPolicy *
+GNUNET_STRINGS_parse_ipv4_policy (const char *routeListX)
+{
+  unsigned int count;
+  unsigned int i;
+  unsigned int j;
+  unsigned int len;
+  int cnt;
+  unsigned int pos;
+  unsigned int temps[8];
+  int slash;
+  struct GNUNET_STRINGS_IPv4NetworkPolicy *result;
+  int colon;
+  int end;
+  char *routeList;
+
+  if (NULL == routeListX)
+    return NULL;
+  len = strlen (routeListX);
+  if (0 == len)
+    return NULL;
+  routeList = GNUNET_strdup (routeListX);
+  count = 0;
+  for (i = 0; i < len; i++)
+    if (routeList[i] == ';')
+      count++;
+  result = GNUNET_malloc (sizeof (struct GNUNET_STRINGS_IPv4NetworkPolicy) * (count + 1));
+  i = 0;
+  pos = 0;
+  while (i < count)
+  {
+    for (colon = pos; ':' != routeList[colon]; colon++)
+      if ( (';' == routeList[colon]) ||
+           ('\0' == routeList[colon]) )
         break;
         break;
-      case ')':
-        zt_addr[i] = ']';
+    for (end = colon; ';' != routeList[end]; end++)
+      if ('\0' == routeList[end])
         break;
         break;
-      default:
+    if ('\0' == routeList[end])
+      break;
+    routeList[end] = '\0';
+    if (':' == routeList[colon])
+    {
+      routeList[colon] = '\0';
+      if (GNUNET_OK != parse_port_policy (&routeList[colon + 1],
+                                          &result[i].pp))
         break;
         break;
+    }
+    cnt =
+        SSCANF (&routeList[pos],
+                "%u.%u.%u.%u/%u.%u.%u.%u",
+                &temps[0],
+                &temps[1],
+                &temps[2],
+                &temps[3],
+                &temps[4],
+                &temps[5],
+                &temps[6],
+                &temps[7]);
+    if (8 == cnt)
+    {
+      for (j = 0; j < 8; j++)
+        if (temps[j] > 0xFF)
+        {
+          LOG (GNUNET_ERROR_TYPE_WARNING,
+               _("Invalid format for IP: `%s'\n"),
+               &routeList[pos]);
+          GNUNET_free (result);
+          GNUNET_free (routeList);
+          return NULL;
+        }
+      result[i].network.s_addr =
+          htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
+                 temps[3]);
+      result[i].netmask.s_addr =
+          htonl ((temps[4] << 24) + (temps[5] << 16) + (temps[6] << 8) +
+                 temps[7]);
+      pos = end + 1;
+      i++;
+      continue;
+    }
+    /* try second notation */
+    cnt =
+        SSCANF (&routeList[pos],
+                "%u.%u.%u.%u/%u",
+                &temps[0],
+                &temps[1],
+                &temps[2],
+                &temps[3],
+                &slash);
+    if (5 == cnt)
+    {
+      for (j = 0; j < 4; j++)
+        if (temps[j] > 0xFF)
+        {
+          LOG (GNUNET_ERROR_TYPE_WARNING,
+               _("Invalid format for IP: `%s'\n"),
+               &routeList[pos]);
+          GNUNET_free (result);
+          GNUNET_free (routeList);
+          return NULL;
+        }
+      result[i].network.s_addr =
+          htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
+                 temps[3]);
+      if ((slash <= 32) && (slash >= 0))
+      {
+        result[i].netmask.s_addr = 0;
+        while (slash > 0)
+        {
+          result[i].netmask.s_addr =
+              (result[i].netmask.s_addr >> 1) + 0x80000000;
+          slash--;
+        }
+        result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
+        pos = end + 1;
+        i++;
+        continue;
+      }
+      else
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             _("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."),
+             slash);
+        GNUNET_free (result);
+          GNUNET_free (routeList);
+        return NULL;            /* error */
       }
     }
       }
     }
-    return GNUNET_STRINGS_to_address_ipv6 (zt_addr, zt_len, (struct sockaddr_in6 *) r_buf);
+    /* try third notation */
+    slash = 32;
+    cnt =
+        SSCANF (&routeList[pos],
+                "%u.%u.%u.%u",
+                &temps[0],
+                &temps[1],
+                &temps[2],
+                &temps[3]);
+    if (4 == cnt)
+    {
+      for (j = 0; j < 4; j++)
+        if (temps[j] > 0xFF)
+        {
+          LOG (GNUNET_ERROR_TYPE_WARNING,
+               _("Invalid format for IP: `%s'\n"),
+               &routeList[pos]);
+          GNUNET_free (result);
+          GNUNET_free (routeList);
+          return NULL;
+        }
+      result[i].network.s_addr =
+          htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) +
+                 temps[3]);
+      result[i].netmask.s_addr = 0;
+      while (slash > 0)
+      {
+        result[i].netmask.s_addr = (result[i].netmask.s_addr >> 1) + 0x80000000;
+        slash--;
+      }
+      result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
+      pos = end + 1;
+      i++;
+      continue;
+    }
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Invalid format for IP: `%s'\n"),
+         &routeList[pos]);
+    GNUNET_free (result);
+    GNUNET_free (routeList);
+    return NULL;                /* error */
+  }
+  if (pos < strlen (routeList))
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Invalid format: `%s'\n"),
+         &routeListX[pos]);
+    GNUNET_free (result);
+    GNUNET_free (routeList);
+    return NULL;                /* oops */
   }
   }
-  return GNUNET_STRINGS_to_address_ipv4 (zt_addr, zt_len, (struct sockaddr_in *) r_buf);
+  GNUNET_free (routeList);
+  return result;                /* ok */
 }
 
 }
 
+
+/**
+ * Parse an IPv6 network policy. The argument specifies a list of
+ * subnets. The format is <tt>(network[/netmask[:SPORT[-DPORT]]];)*</tt>
+ * (no whitespace, must be terminated with a semicolon). The network
+ * must be given in colon-hex notation.  The netmask must be given in
+ * CIDR notation (/16) or can be omitted to specify a single host.
+ * Note that the netmask is mandatory if ports are specified.
+ *
+ * @param routeListX a string specifying the policy
+ * @return the converted list, 0-terminated, NULL if the synatx is flawed
+ */
+struct GNUNET_STRINGS_IPv6NetworkPolicy *
+GNUNET_STRINGS_parse_ipv6_policy (const char *routeListX)
+{
+  unsigned int count;
+  unsigned int i;
+  unsigned int len;
+  unsigned int pos;
+  int start;
+  int slash;
+  int ret;
+  char *routeList;
+  struct GNUNET_STRINGS_IPv6NetworkPolicy *result;
+  unsigned int bits;
+  unsigned int off;
+  int save;
+  int colon;
+
+  if (NULL == routeListX)
+    return NULL;
+  len = strlen (routeListX);
+  if (0 == len)
+    return NULL;
+  routeList = GNUNET_strdup (routeListX);
+  count = 0;
+  for (i = 0; i < len; i++)
+    if (';' == routeList[i])
+      count++;
+  if (';' != routeList[len - 1])
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Invalid network notation (does not end with ';': `%s')\n"),
+         routeList);
+    GNUNET_free (routeList);
+    return NULL;
+  }
+
+  result = GNUNET_malloc (sizeof (struct GNUNET_STRINGS_IPv6NetworkPolicy) * (count + 1));
+  i = 0;
+  pos = 0;
+  while (i < count)
+  {
+    start = pos;
+    while (';' != routeList[pos])
+      pos++;
+    slash = pos;
+    while ((slash >= start) && (routeList[slash] != '/'))
+      slash--;
+
+    if (slash < start)
+    {
+      memset (&result[i].netmask,
+              0xFF,
+              sizeof (struct in6_addr));
+      slash = pos;
+    }
+    else
+    {
+      routeList[pos] = '\0';
+      for (colon = pos; ':' != routeList[colon]; colon--)
+        if ('/' == routeList[colon])
+          break;
+      if (':' == routeList[colon])
+      {
+        routeList[colon] = '\0';
+        if (GNUNET_OK != parse_port_policy (&routeList[colon + 1],
+                                            &result[i].pp))
+        {
+          GNUNET_free (result);
+          GNUNET_free (routeList);
+          return NULL;
+        }
+      }
+      ret = inet_pton (AF_INET6, &routeList[slash + 1], &result[i].netmask);
+      if (ret <= 0)
+      {
+        save = errno;
+        if ((1 != SSCANF (&routeList[slash + 1], "%u", &bits)) || (bits > 128))
+        {
+          if (0 == ret)
+            LOG (GNUNET_ERROR_TYPE_WARNING,
+                 _("Wrong format `%s' for netmask\n"),
+                 &routeList[slash + 1]);
+          else
+          {
+            errno = save;
+            LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_pton");
+          }
+          GNUNET_free (result);
+          GNUNET_free (routeList);
+          return NULL;
+        }
+        off = 0;
+        while (bits > 8)
+        {
+          result[i].netmask.s6_addr[off++] = 0xFF;
+          bits -= 8;
+        }
+        while (bits > 0)
+        {
+          result[i].netmask.s6_addr[off] =
+              (result[i].netmask.s6_addr[off] >> 1) + 0x80;
+          bits--;
+        }
+      }
+    }
+    routeList[slash] = '\0';
+    ret = inet_pton (AF_INET6, &routeList[start], &result[i].network);
+    if (ret <= 0)
+    {
+      if (0 == ret)
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             _("Wrong format `%s' for network\n"),
+             &routeList[slash + 1]);
+      else
+        LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+                      "inet_pton");
+      GNUNET_free (result);
+      GNUNET_free (routeList);
+      return NULL;
+    }
+    pos++;
+    i++;
+  }
+  GNUNET_free (routeList);
+  return result;
+}
+
+
+
+/** ******************** Base64 encoding ***********/
+
+#define FILLCHAR '='
+static char *cvt =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
+
+
+/**
+ * Encode into Base64.
+ *
+ * @param data the data to encode
+ * @param len the length of the input
+ * @param output where to write the output (*output should be NULL,
+ *   is allocated)
+ * @return the size of the output
+ */
+size_t
+GNUNET_STRINGS_base64_encode (const char *data,
+                              size_t len,
+                              char **output)
+{
+  size_t i;
+  char c;
+  size_t ret;
+  char *opt;
+
+  ret = 0;
+  opt = GNUNET_malloc (2 + (len * 4 / 3) + 8);
+  *output = opt;
+  for (i = 0; i < len; ++i)
+  {
+    c = (data[i] >> 2) & 0x3f;
+    opt[ret++] = cvt[(int) c];
+    c = (data[i] << 4) & 0x3f;
+    if (++i < len)
+      c |= (data[i] >> 4) & 0x0f;
+    opt[ret++] = cvt[(int) c];
+    if (i < len)
+    {
+      c = (data[i] << 2) & 0x3f;
+      if (++i < len)
+        c |= (data[i] >> 6) & 0x03;
+      opt[ret++] = cvt[(int) c];
+    }
+    else
+    {
+      ++i;
+      opt[ret++] = FILLCHAR;
+    }
+    if (i < len)
+    {
+      c = data[i] & 0x3f;
+      opt[ret++] = cvt[(int) c];
+    }
+    else
+    {
+      opt[ret++] = FILLCHAR;
+    }
+  }
+  opt[ret++] = FILLCHAR;
+  return ret;
+}
+
+#define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\
+                   :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\
+                   :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\
+          :((a) == '+') ? 62\
+          :((a) == '/') ? 63 : -1)
+
+
+/**
+ * Decode from Base64.
+ *
+ * @param data the data to encode
+ * @param len the length of the input
+ * @param output where to write the output (*output should be NULL,
+ *   is allocated)
+ * @return the size of the output
+ */
+size_t
+GNUNET_STRINGS_base64_decode (const char *data,
+                              size_t len, char **output)
+{
+  size_t i;
+  char c;
+  char c1;
+  size_t ret = 0;
+
+#define CHECK_CRLF  while (data[i] == '\r' || data[i] == '\n') {\
+                       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, "ignoring CR/LF\n"); \
+                       i++; \
+                       if (i >= len) goto END;  \
+               }
+
+  *output = GNUNET_malloc ((len * 3 / 4) + 8);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "base64_decode decoding len=%d\n",
+              (int) len);
+  for (i = 0; i < len; ++i)
+  {
+    CHECK_CRLF;
+    if (FILLCHAR == data[i])
+      break;
+    c = (char) cvtfind (data[i]);
+    ++i;
+    CHECK_CRLF;
+    c1 = (char) cvtfind (data[i]);
+    c = (c << 2) | ((c1 >> 4) & 0x3);
+    (*output)[ret++] = c;
+    if (++i < len)
+    {
+      CHECK_CRLF;
+      c = data[i];
+      if (FILLCHAR == c)
+        break;
+      c = (char) cvtfind (c);
+      c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
+      (*output)[ret++] = c1;
+    }
+    if (++i < len)
+    {
+      CHECK_CRLF;
+      c1 = data[i];
+      if (FILLCHAR == c1)
+        break;
+
+      c1 = (char) cvtfind (c1);
+      c = ((c << 6) & 0xc0) | c1;
+      (*output)[ret++] = c;
+    }
+  }
+END:
+  return ret;
+}
+
+
+
+
+
 /* end of strings.c */
 /* end of strings.c */