-help seaspider some more
[oweals/gnunet.git] / src / fs / fs_namespace.c
index fb45d99e90cacf14ad521be5a52df5fc7601d0a0..bfd7594ef7a69c0aa5abde45e8724356de9d8b92 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet
 /*
      This file is part of GNUnet
-     (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
+     (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Christian Grothoff (and other contributing authors)
 
      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
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @author Christian Grothoff
  */
 #include "platform.h"
+#include "gnunet_constants.h"
+#include "gnunet_signatures.h"
+#include "gnunet_util_lib.h"
 #include "gnunet_fs_service.h"
 #include "gnunet_fs_service.h"
+#include "fs_api.h"
 
 
 /**
 
 
 /**
- * Publish an advertismement for a namespace.  
+ * Maximum legal size for an sblock.
+ */
+#define MAX_SBLOCK_SIZE (60 * 1024)
+
+
+/**
+ * Return the name of the directory in which we store
+ * our local namespaces (or rather, their public keys).
  *
  *
- * @param h handle to the file sharing subsystem
- * @param namespace handle for the namespace that should be advertised
- * @param meta meta-data for the namespace advertisement
- * @param anonymity for the namespace advertismement
- * @param priority for the namespace advertisement
- * @param expiration for the namespace advertisement
- * @param advertisementURI the keyword (!) URI to advertise the
- *        namespace under (we will create a GNUNET_EC_KNBlock)
- * @param rootEntry name of the root entry in the namespace (for
- *        the namespace advertisement)
+ * @param h global fs handle
+ * @return NULL on error, otherwise the name of the directory
+ */
+static char *
+get_namespace_directory (struct GNUNET_FS_Handle *h)
+{
+  char *dn;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "IDENTITY_DIR",
+                                               &dn))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Configuration fails to specify `%s' in section `%s'\n"),
+                "IDENTITY_DIR", "fs");
+    return NULL;
+  }
+  return dn;
+}
+
+
+/**
+ * Return the name of the directory in which we store
+ * the update information graph for the given local namespace.
+ *
+ * @param ns namespace handle
+ * @return NULL on error, otherwise the name of the directory
+ */
+static char *
+get_update_information_directory (struct GNUNET_FS_Namespace *ns)
+{
+  char *dn;
+  char *ret;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (ns->h->cfg, "FS", "UPDATE_DIR",
+                                               &dn))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Configuration fails to specify `%s' in section `%s'\n"),
+                "UPDATE_DIR", "fs");
+    return NULL;
+  }
+  GNUNET_asprintf (&ret, "%s%s%s", dn, DIR_SEPARATOR_STR, ns->name);
+  GNUNET_free (dn);
+  return ret;
+}
+
+
+/**
+ * Write the namespace update node graph to a file.
  *
  *
- * @return uri of the advertisement
+ * @param ns namespace to dump
  */
  */
-struct GNUNET_FS_Uri *
-GNUNET_FS_namespace_advertise (struct GNUNET_FS_Handle *h,
-                              struct GNUNET_FS_Namespace *namespace,
-                              const struct GNUNET_MetaData *meta,
-                              unsigned int anonymity,
-                              unsigned int priority,
-                              struct GNUNET_TIME_Absolute expiration,
-                              const struct GNUNET_FS_Uri *advertisementURI,
-                              const char *rootEntry)
+static void
+write_update_information_graph (struct GNUNET_FS_Namespace *ns)
 {
 {
-  return NULL;
+  char *fn;
+  struct GNUNET_BIO_WriteHandle *wh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+
+  fn = get_update_information_directory (ns);
+  wh = GNUNET_BIO_write_open (fn);
+  if (wh == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to open `%s' for writing: %s\n"), STRERROR (errno));
+    GNUNET_free (fn);
+    return;
+  }
+  if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, ns->update_node_count))
+    goto END;
+  for (i = 0; i < ns->update_node_count; i++)
+  {
+    n = ns->update_nodes[i];
+    uris = GNUNET_FS_uri_to_string (n->uri);
+    if ((GNUNET_OK != GNUNET_BIO_write_string (wh, n->id)) ||
+        (GNUNET_OK != GNUNET_BIO_write_meta_data (wh, n->md)) ||
+        (GNUNET_OK != GNUNET_BIO_write_string (wh, n->update)) ||
+        (GNUNET_OK != GNUNET_BIO_write_string (wh, uris)))
+    {
+      GNUNET_free (uris);
+      break;
+    }
+    GNUNET_free (uris);
+  }
+END:
+  if (GNUNET_OK != GNUNET_BIO_write_close (wh))
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to write `%s': %s\n"),
+                STRERROR (errno));
+  GNUNET_free (fn);
+}
+
+
+/**
+ * Read the namespace update node graph from a file.
+ *
+ * @param ns namespace to read
+ */
+static void
+read_update_information_graph (struct GNUNET_FS_Namespace *ns)
+{
+  char *fn;
+  struct GNUNET_BIO_ReadHandle *rh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+  uint32_t count;
+  char *emsg;
+
+  fn = get_update_information_directory (ns);
+  if (GNUNET_YES != GNUNET_DISK_file_test (fn))
+  {
+    GNUNET_free (fn);
+    return;
+  }
+  rh = GNUNET_BIO_read_open (fn);
+  if (rh == NULL)
+  {
+    GNUNET_free (fn);
+    return;
+  }
+  if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &count))
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (count > 1024 * 1024)
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (count == 0)
+  {
+    GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL));
+    GNUNET_free (fn);
+    return;
+  }
+  ns->update_nodes =
+      GNUNET_malloc (count * sizeof (struct NamespaceUpdateNode *));
+
+  for (i = 0; i < count; i++)
+  {
+    n = GNUNET_malloc (sizeof (struct NamespaceUpdateNode));
+    if ((GNUNET_OK != GNUNET_BIO_read_string (rh, "identifier", &n->id, 1024))
+        || (GNUNET_OK != GNUNET_BIO_read_meta_data (rh, "meta", &n->md)) ||
+        (GNUNET_OK !=
+         GNUNET_BIO_read_string (rh, "update-id", &n->update, 1024)) ||
+        (GNUNET_OK != GNUNET_BIO_read_string (rh, "uri", &uris, 1024 * 2)))
+    {
+      GNUNET_break (0);
+      GNUNET_free_non_null (n->id);
+      GNUNET_free_non_null (n->update);
+      if (n->md != NULL)
+        GNUNET_CONTAINER_meta_data_destroy (n->md);
+      GNUNET_free (n);
+      break;
+    }
+    n->uri = GNUNET_FS_uri_parse (uris, &emsg);
+    GNUNET_free (uris);
+    if (n->uri == NULL)
+    {
+      GNUNET_break (0);
+      GNUNET_free (emsg);
+      GNUNET_free (n->id);
+      GNUNET_free_non_null (n->update);
+      GNUNET_CONTAINER_meta_data_destroy (n->md);
+      GNUNET_free (n);
+      break;
+    }
+    ns->update_nodes[i] = n;
+  }
+  ns->update_node_count = i;
+END:
+  if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to write `%s': %s\n"), emsg);
+    GNUNET_free (emsg);
+  }
+  GNUNET_free (fn);
 }
 
 
 }
 
 
+
+
 /**
  * Create a namespace with the given name; if one already
  * exists, return a handle to the existing namespace.
 /**
  * Create a namespace with the given name; if one already
  * exists, return a handle to the existing namespace.
@@ -66,10 +237,45 @@ GNUNET_FS_namespace_advertise (struct GNUNET_FS_Handle *h,
  * @return handle to the namespace, NULL on error
  */
 struct GNUNET_FS_Namespace *
  * @return handle to the namespace, NULL on error
  */
 struct GNUNET_FS_Namespace *
-GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h,
-                           const char *name)
+GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h, const char *name)
+{
+  char *dn;
+  char *fn;
+  struct GNUNET_FS_Namespace *ret;
+
+  dn = get_namespace_directory (h);
+  GNUNET_asprintf (&fn, "%s%s%s", dn, DIR_SEPARATOR_STR, name);
+  GNUNET_free (dn);
+  ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Namespace));
+  ret->h = h;
+  ret->rc = 1;
+  ret->key = GNUNET_CRYPTO_rsa_key_create_from_file (fn);
+  if (ret->key == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to create or read private key for namespace `%s'\n"),
+                name);
+    GNUNET_free (ret);
+    GNUNET_free (fn);
+    return NULL;
+  }
+  ret->name = GNUNET_strdup (name);
+  ret->filename = fn;
+  return ret;
+}
+
+
+/**
+ * Duplicate a namespace handle.
+ *
+ * @param ns namespace handle
+ * @return duplicated handle to the namespace
+ */
+struct GNUNET_FS_Namespace *
+GNUNET_FS_namespace_dup (struct GNUNET_FS_Namespace *ns)
 {
 {
-  return NULL;
+  ns->rc++;
+  return ns;
 }
 
 
 }
 
 
@@ -84,11 +290,98 @@ GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h,
  *
  * @return GNUNET_OK on success, GNUNET_SYSERR on error
  */
  *
  * @return GNUNET_OK on success, GNUNET_SYSERR on error
  */
-int 
-GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace,
-                           int freeze)
+int
+GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace, int freeze)
+{
+  unsigned int i;
+  struct NamespaceUpdateNode *nsn;
+
+  namespace->rc--;
+  if (freeze)
+  {
+    if (0 != UNLINK (namespace->filename))
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink",
+                                namespace->filename);
+  }
+  if (0 != namespace->rc)
+    return GNUNET_OK;
+  GNUNET_CRYPTO_rsa_key_free (namespace->key);
+  GNUNET_free (namespace->filename);
+  GNUNET_free (namespace->name);
+  for (i = 0; i < namespace->update_node_count; i++)
+  {
+    nsn = namespace->update_nodes[i];
+    GNUNET_CONTAINER_meta_data_destroy (nsn->md);
+    GNUNET_FS_uri_destroy (nsn->uri);
+    GNUNET_free (nsn->id);
+    GNUNET_free (nsn->update);
+    GNUNET_free (nsn);
+  }
+  GNUNET_array_grow (namespace->update_nodes, namespace->update_node_count,
+                    0);
+  if (namespace->update_map != NULL)
+    GNUNET_CONTAINER_multihashmap_destroy (namespace->update_map);
+  GNUNET_free (namespace);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Context for the 'process_namespace' callback.
+ * Specifies a function to call on each namespace.
+ */
+struct ProcessNamespaceContext
+{
+  /**
+   * Function to call.
+   */
+  GNUNET_FS_NamespaceInfoProcessor cb;
+
+  /**
+   * Closure for 'cb'.
+   */
+  void *cb_cls;
+};
+
+
+/**
+ * Function called with a filename of a namespace. Reads the key and
+ * calls the callback.
+ *
+ * @param cls closure (struct ProcessNamespaceContext)
+ * @param filename complete filename (absolute path)
+ * @return GNUNET_OK to continue to iterate,
+ *  GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+process_namespace (void *cls, const char *filename)
 {
 {
-  return GNUNET_SYSERR;
+  struct ProcessNamespaceContext *pnc = cls;
+  struct GNUNET_CRYPTO_RsaPrivateKey *key;
+  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk;
+  GNUNET_HashCode id;
+  const char *name;
+  const char *t;
+
+  key = GNUNET_CRYPTO_rsa_key_create_from_file (filename);
+  if (key == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _
+                ("Failed to read namespace private key file `%s', deleting it!\n"),
+                filename);
+    if (0 != UNLINK (filename))
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
+    return GNUNET_OK;
+  }
+  GNUNET_CRYPTO_rsa_key_get_public (key, &pk);
+  GNUNET_CRYPTO_rsa_key_free (key);
+  GNUNET_CRYPTO_hash (&pk, sizeof (pk), &id);
+  name = filename;
+  while (NULL != (t = strstr (name, DIR_SEPARATOR_STR)))
+    name = t + 1;
+  pnc->cb (pnc->cb_cls, name, &id);
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -101,629 +394,561 @@ GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace,
  * @param cb function to call on each known namespace
  * @param cb_cls closure for cb
  */
  * @param cb function to call on each known namespace
  * @param cb_cls closure for cb
  */
-void 
+void
 GNUNET_FS_namespace_list (struct GNUNET_FS_Handle *h,
 GNUNET_FS_namespace_list (struct GNUNET_FS_Handle *h,
-                         GNUNET_FS_NamespaceInfoProcessor cb,
-                         void *cb_cls)
+                          GNUNET_FS_NamespaceInfoProcessor cb, void *cb_cls)
 {
 {
+  char *dn;
+  struct ProcessNamespaceContext ctx;
+
+  dn = get_namespace_directory (h);
+  if (dn == NULL)
+    return;
+  ctx.cb = cb;
+  ctx.cb_cls = cb_cls;
+  GNUNET_DISK_directory_scan (dn, &process_namespace, &ctx);
+  GNUNET_free (dn);
 }
 
 }
 
-/* end of fs_namespace.c */
-
-#if 0
-/*
-     This file is part of GNUnet
-     (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
-
-     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
-     option) any later version.
-
-     GNUnet 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 GNU
-     General Public License for more details.
-
-     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.
-*/
 
 /**
 
 /**
- * @file applications/fs/ecrs/namespace.c
- * @brief creation, deletion and advertising of namespaces
- * @author Christian Grothoff
+ * Context for the SKS publication.
  */
  */
+struct GNUNET_FS_PublishSksContext
+{
 
 
-#include "platform.h"
-#include "gnunet_directories.h"
-#include "gnunet_protocols.h"
-#include "gnunet_ecrs_lib.h"
-#include "gnunet_fs_lib.h"
-#include "ecrs_core.h"
-#include "ecrs.h"
+  /**
+   * URI of the new entry in the namespace.
+   */
+  struct GNUNET_FS_Uri *uri;
+
+  /**
+   * Namespace update node to add to namespace on success (or to be
+   * deleted if publishing failed).
+   */
+  struct NamespaceUpdateNode *nsn;
+
+  /**
+   * Namespace we're publishing to.
+   */
+  struct GNUNET_FS_Namespace *namespace;
+
+  /**
+   * Handle to the datastore.
+   */
+  struct GNUNET_DATASTORE_Handle *dsh;
+
+  /**
+   * Function to call once we're done.
+   */
+  GNUNET_FS_PublishContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Handle for our datastore request.
+   */
+  struct GNUNET_DATASTORE_QueueEntry *dqe;
+};
 
 
-#define PSEUDODIR "data/namespace/keys/"
-#define INITVALUE "GNUnet!!"
-#define MAX_SBLOCK_SIZE 32000
 
 
-static char *
-getPseudonymFileName (struct GNUNET_GE_Context *ectx,
-                      struct GNUNET_GC_Configuration *cfg,
-                      const GNUNET_HashCode * pid)
+/**
+ * Function called by the datastore API with
+ * the result from the PUT (SBlock) request.
+ *
+ * @param cls closure of type "struct GNUNET_FS_PublishSksContext*"
+ * @param success GNUNET_OK on success
+ * @param min_expiration minimum expiration time required for content to be stored
+ * @param msg error message (or NULL)
+ */
+static void
+sb_put_cont (void *cls, int success, 
+            struct GNUNET_TIME_Absolute min_expiration,
+            const char *msg)
 {
 {
-  char *gnHome;
-  char *fileName;
-  GNUNET_EncName enc;
-
-  GNUNET_GC_get_configuration_value_filename (cfg,
-                                              "GNUNET",
-                                              "GNUNET_HOME",
-                                              GNUNET_DEFAULT_HOME_DIRECTORY,
-                                              &fileName);
-  gnHome = GNUNET_expand_file_name (ectx, fileName);
-  GNUNET_free (fileName);
-  fileName =
-    GNUNET_malloc (strlen (gnHome) + strlen (PSEUDODIR) +
-                   sizeof (GNUNET_EncName) + 2);
-  strcpy (fileName, gnHome);
-  GNUNET_free (gnHome);
-  strcat (fileName, DIR_SEPARATOR_STR);
-  strcat (fileName, PSEUDODIR);
-  GNUNET_disk_directory_create (ectx, fileName);
-  if (pid != NULL)
+  struct GNUNET_FS_PublishSksContext *psc = cls;
+  GNUNET_HashCode hc;
+
+  psc->dqe = NULL;
+  if (GNUNET_OK != success)
+  {
+    if (NULL != psc->cont)
+      psc->cont (psc->cont_cls, NULL, msg);
+    GNUNET_FS_publish_sks_cancel (psc);
+    return;
+  }
+  if (NULL != psc->nsn)
+  {
+    /* FIXME: this can be done much more
+     * efficiently by simply appending to the
+     * file and overwriting the 4-byte header */
+    if (psc->namespace->update_nodes == NULL)
+      read_update_information_graph (psc->namespace);
+    GNUNET_array_append (psc->namespace->update_nodes,
+                        psc->namespace->update_node_count, psc->nsn);
+    if (psc->namespace->update_map != NULL)
     {
     {
-      GNUNET_hash_to_enc (pid, &enc);
-      strcat (fileName, (char *) &enc);
+      GNUNET_CRYPTO_hash (psc->nsn->id, strlen (psc->nsn->id), &hc);
+      GNUNET_CONTAINER_multihashmap_put (psc->namespace->update_map, &hc,
+                                        psc->nsn,
+                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
     }
     }
-  return fileName;
+    psc->nsn = NULL;
+    write_update_information_graph (psc->namespace);
+  }
+  if (NULL != psc->cont)
+    psc->cont (psc->cont_cls, psc->uri, NULL);
+  GNUNET_FS_publish_sks_cancel (psc);
 }
 
 
 /**
 }
 
 
 /**
- * Check if the given namespace exists (locally).
+ * Publish an SBlock on GNUnet.
  *
  *
- * @return GNUNET_OK if the namespace exists, GNUNET_SYSERR if not
+ * @param h handle to the file sharing subsystem
+ * @param namespace namespace to publish in
+ * @param identifier identifier to use
+ * @param update update identifier to use
+ * @param meta metadata to use
+ * @param uri URI to refer to in the SBlock
+ * @param bo block options
+ * @param options publication options
+ * @param cont continuation
+ * @param cont_cls closure for cont
+ * @return NULL on error ('cont' will still be called)
  */
  */
-int
-GNUNET_ECRS_namespace_test_exists (struct GNUNET_GE_Context *ectx,
-                                   struct GNUNET_GC_Configuration *cfg,
-                                   const GNUNET_HashCode * pid)
+struct GNUNET_FS_PublishSksContext *
+GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
+                       struct GNUNET_FS_Namespace *namespace,
+                       const char *identifier, const char *update,
+                       const struct GNUNET_CONTAINER_MetaData *meta,
+                       const struct GNUNET_FS_Uri *uri,
+                       const struct GNUNET_FS_BlockOptions *bo,
+                       enum GNUNET_FS_PublishOptions options,
+                       GNUNET_FS_PublishContinuation cont, void *cont_cls)
 {
 {
-  char *fileName;
-  int ret;
-
-  fileName = getPseudonymFileName (ectx, cfg, pid);
-  ret = GNUNET_disk_file_test (ectx, fileName);
-  GNUNET_free (fileName);
-  return ret;
+  struct GNUNET_FS_PublishSksContext *psc;
+  struct GNUNET_CRYPTO_AesSessionKey sk;
+  struct GNUNET_CRYPTO_AesInitializationVector iv;
+  struct GNUNET_FS_Uri *sks_uri;
+  char *uris;
+  size_t size;
+  size_t slen;
+  size_t nidlen;
+  size_t idlen;
+  ssize_t mdsize;
+  struct SBlock *sb;
+  struct SBlock *sb_enc;
+  char *dest;
+  struct GNUNET_CONTAINER_MetaData *mmeta;
+  GNUNET_HashCode key;          /* hash of thisId = key */
+  GNUNET_HashCode id;           /* hash of hc = identifier */
+  GNUNET_HashCode query;        /* id ^ nsid = DB query */
+
+  if (NULL == meta)
+    mmeta = GNUNET_CONTAINER_meta_data_create ();
+  else
+    mmeta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+  uris = GNUNET_FS_uri_to_string (uri);
+  slen = strlen (uris) + 1;
+  idlen = strlen (identifier);
+  if (update != NULL)
+    nidlen = strlen (update) + 1;
+  else
+    nidlen = 1;
+  mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (mmeta);
+  size = sizeof (struct SBlock) + slen + nidlen + mdsize;
+  if (size > MAX_SBLOCK_SIZE)
+  {
+    size = MAX_SBLOCK_SIZE;
+    mdsize = size - (sizeof (struct SBlock) + slen + nidlen);
+  }
+  sb = GNUNET_malloc (sizeof (struct SBlock) + size);
+  dest = (char *) &sb[1];
+  if (update != NULL)
+    memcpy (dest, update, nidlen);
+  else
+    memset (dest, 0, 1);
+  dest += nidlen;
+  memcpy (dest, uris, slen);
+  GNUNET_free (uris);
+  dest += slen;
+  mdsize =
+      GNUNET_CONTAINER_meta_data_serialize (mmeta, &dest, mdsize,
+                                            GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
+  GNUNET_CONTAINER_meta_data_destroy (mmeta);
+  if (mdsize == -1)
+  {
+    GNUNET_break (0);
+    GNUNET_free (sb);
+    if (NULL != cont)
+      cont (cont_cls, NULL, _("Internal error."));
+    return NULL;
+  }
+  size = sizeof (struct SBlock) + mdsize + slen + nidlen;
+  sb_enc = GNUNET_malloc (size);
+  GNUNET_CRYPTO_hash (identifier, idlen, &key);
+  GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &id);
+  sks_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
+  sks_uri->type = sks;
+  GNUNET_CRYPTO_rsa_key_get_public (namespace->key, &sb_enc->subspace);
+  GNUNET_CRYPTO_hash (&sb_enc->subspace,
+                      sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                      &sks_uri->data.sks.namespace);
+  sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
+  GNUNET_CRYPTO_hash_xor (&id, &sks_uri->data.sks.namespace,
+                          &sb_enc->identifier);
+  GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv);
+  GNUNET_CRYPTO_aes_encrypt (&sb[1], size - sizeof (struct SBlock), &sk, &iv,
+                             &sb_enc[1]);
+  sb_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_SBLOCK);
+  sb_enc->purpose.size =
+      htonl (slen + mdsize + nidlen + sizeof (struct SBlock) -
+             sizeof (struct GNUNET_CRYPTO_RsaSignature));
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_rsa_sign (namespace->key, &sb_enc->purpose,
+                                         &sb_enc->signature));
+  psc = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishSksContext));
+  psc->uri = sks_uri;
+  psc->cont = cont;
+  psc->namespace = GNUNET_FS_namespace_dup (namespace);
+  psc->cont_cls = cont_cls;
+  if (0 != (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
+  {
+    GNUNET_free (sb_enc);
+    GNUNET_free (sb);
+    sb_put_cont (psc, GNUNET_OK, GNUNET_TIME_UNIT_ZERO_ABS, NULL);
+    return NULL;
+  }
+  psc->dsh = GNUNET_DATASTORE_connect (h->cfg);
+  if (NULL == psc->dsh)
+  {
+    GNUNET_free (sb_enc);
+    GNUNET_free (sb);
+    sb_put_cont (psc, GNUNET_NO, GNUNET_TIME_UNIT_ZERO_ABS, _("Failed to connect to datastore."));
+    return NULL;
+  }
+  GNUNET_CRYPTO_hash_xor (&sks_uri->data.sks.namespace, &id, &query);
+  if (NULL != update)
+  {
+    psc->nsn = GNUNET_malloc (sizeof (struct NamespaceUpdateNode));
+    psc->nsn->id = GNUNET_strdup (identifier);
+    psc->nsn->update = GNUNET_strdup (update);
+    psc->nsn->md = GNUNET_CONTAINER_meta_data_duplicate (meta);
+    psc->nsn->uri = GNUNET_FS_uri_dup (uri);
+  }
+  psc->dqe = GNUNET_DATASTORE_put (psc->dsh, 0, &sb_enc->identifier, size, sb_enc,
+                                  GNUNET_BLOCK_TYPE_FS_SBLOCK, bo->content_priority,
+                                  bo->anonymity_level, bo->replication_level,
+                                  bo->expiration_time, -2, 1,
+                                  GNUNET_CONSTANTS_SERVICE_TIMEOUT, &sb_put_cont, psc);
+  GNUNET_free (sb);
+  GNUNET_free (sb_enc);
+  return psc;
 }
 
 }
 
+
 /**
 /**
- * Delete a local namespace.
+ * Abort the SKS publishing operation.
  *
  *
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @param psc context of the operation to abort.
  */
  */
-int
-GNUNET_ECRS_namespace_delete (struct GNUNET_GE_Context *ectx,
-                              struct GNUNET_GC_Configuration *cfg,
-                              const GNUNET_HashCode * pid)
+void
+GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc)
 {
 {
-  char *fileName;
-
-  fileName = getPseudonymFileName (ectx, cfg, pid);
-  if (GNUNET_YES != GNUNET_disk_file_test (ectx, fileName))
-    {
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;     /* no such namespace */
-    }
-  if (0 != UNLINK (fileName))
-    {
-      GNUNET_GE_LOG_STRERROR_FILE (ectx,
-                                   GNUNET_GE_WARNING | GNUNET_GE_USER |
-                                   GNUNET_GE_BULK, "unlink", fileName);
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;
-    }
-  GNUNET_free (fileName);
-  return GNUNET_OK;
+  if (NULL != psc->dqe)
+  {
+    GNUNET_DATASTORE_cancel (psc->dqe);
+    psc->dqe = NULL;
+  }
+  if (NULL != psc->dsh)
+  {
+    GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
+    psc->dsh = NULL;
+  }
+  GNUNET_FS_namespace_delete (psc->namespace, GNUNET_NO);
+  GNUNET_FS_uri_destroy (psc->uri);
+  if (NULL != psc->nsn)
+  {
+    GNUNET_CONTAINER_meta_data_destroy (psc->nsn->md);
+    GNUNET_FS_uri_destroy (psc->nsn->uri);
+    GNUNET_free (psc->nsn->id);
+    GNUNET_free (psc->nsn->update);
+    GNUNET_free (psc->nsn);
+  }
+  GNUNET_free (psc);
 }
 
 }
 
+
 /**
 /**
- * Write the private key of the namespace to a file.
+ * Closure for 'process_update_node'.
  */
  */
-static int
-write_namespace_key (struct GNUNET_GC_Configuration *cfg,
-                     const struct GNUNET_RSA_PrivateKey *key)
+struct ProcessUpdateClosure
 {
 {
-  GNUNET_RSA_PrivateKeyEncoded *namespace_priv_key_encoded;
-  char *fileName;
-  GNUNET_RSA_PublicKey pubk;
-  GNUNET_HashCode pid;
-
-  GNUNET_RSA_get_public_key (key, &pubk);
-  GNUNET_hash (&pubk, sizeof (GNUNET_RSA_PublicKey), &pid);
-  fileName = getPseudonymFileName (NULL, cfg, &pid);
-  if (GNUNET_YES == GNUNET_disk_file_test (NULL, fileName))
-    {
-      GNUNET_GE_BREAK (NULL, 0);        /* hash collision!? */
-      GNUNET_free (fileName);
-      return GNUNET_SYSERR;
-    }
-  namespace_priv_key_encoded = GNUNET_RSA_encode_key (key);
-  GNUNET_disk_file_write (NULL, fileName,
-                          (const char *) namespace_priv_key_encoded,
-                          ntohs (namespace_priv_key_encoded->len), "600");
-  GNUNET_free (fileName);
-  GNUNET_free (namespace_priv_key_encoded);
-  return GNUNET_OK;
-}
+  /**
+   * Function to call for each node.
+   */
+  GNUNET_FS_IdentifierProcessor ip;
+
+  /**
+   * Closure for 'ip'.
+   */
+  void *ip_cls;
+};
+
 
 /**
 
 /**
- * Create a new namespace (and publish an advertismement).
- * This publishes both an GNUNET_EC_NBlock in the namespace itself
- * as well as KNBlocks under all keywords specified in
- * the advertisementURI.
+ * Call the iterator in the closure for each node.
  *
  *
- * @param anonymity_level for the namespace advertismement
- * @param priority for the namespace advertisement
- * @param expiration for the namespace advertisement
- * @param advertisementURI the keyword (!) URI to advertise the
- *        namespace under (GNUNET_EC_KNBlock)
- * @param meta meta-data for the namespace advertisement
- *        (will be used to derive a name)
- * @param rootEntry name of the root entry in the namespace (for
- *        the namespace advertisement)
- * @param rootURI set to the URI of the namespace, NULL if
- *        no advertisement was created
- *
- * @return URI on success, NULL on error
+ * @param cls closure (of type 'struct ProcessUpdateClosure *')
+ * @param key current key code
+ * @param value value in the hash map (of type 'struct NamespaceUpdateNode *')
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
  */
  */
-struct GNUNET_ECRS_URI *
-GNUNET_ECRS_namespace_create (struct GNUNET_GE_Context *ectx,
-                              struct GNUNET_GC_Configuration *cfg,
-                              const struct GNUNET_MetaData *meta,
-                              unsigned int anonymityLevel,
-                              unsigned int priority,
-                              GNUNET_CronTime expiration,
-                              const struct GNUNET_ECRS_URI *advertisementURI,
-                              const char *rootEntry)
+static int
+process_update_node (void *cls, const GNUNET_HashCode * key, void *value)
 {
 {
-  struct GNUNET_ECRS_URI *rootURI;
-  struct GNUNET_RSA_PrivateKey *namespace_priv_key;
-  GNUNET_HashCode hc;
-  struct GNUNET_ClientServerConnection *sock;
-  GNUNET_DatastoreValue *value;
-  GNUNET_DatastoreValue *knvalue;
-  unsigned int size;
-  unsigned int mdsize;
-  struct GNUNET_RSA_PrivateKey *pk;
-  GNUNET_EC_SBlock *sb;
-  GNUNET_EC_KSBlock *ksb;
-  char **keywords;
-  const char *keyword;
-  unsigned int keywordCount;
-  int i;
-  char *cpy;
-  char *rtgt;
-
-  if ((advertisementURI != NULL)
-      && (!GNUNET_ECRS_uri_test_ksk (advertisementURI)))
-    {
-      GNUNET_GE_BREAK (ectx, 0);
-      return NULL;
-    }
-  namespace_priv_key = GNUNET_RSA_create_key ();
-  if (GNUNET_OK != write_namespace_key (cfg, namespace_priv_key))
-    {
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-
-  /* create advertisements */
-  mdsize = GNUNET_meta_data_get_serialized_size (meta, GNUNET_SERIALIZE_PART);
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (rootEntry) + 2;
-  if (size > MAX_SBLOCK_SIZE)
-    {
-      size = MAX_SBLOCK_SIZE;
-      mdsize = size - sizeof (GNUNET_EC_SBlock) - strlen (rootEntry) - 2;
-    }
-  value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  memset (value, 0, sizeof (GNUNET_DatastoreValue) + size);
-  sb = (GNUNET_EC_SBlock *) & value[1];
-  sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  GNUNET_RSA_get_public_key (namespace_priv_key, &sb->subspace);
-  rtgt = (char *) &sb[1];
-  memcpy (rtgt, rootEntry, strlen (rootEntry) + 1);
-  mdsize = GNUNET_meta_data_serialize (ectx,
-                                       meta,
-                                       &rtgt[strlen (rootEntry) + 2],
-                                       mdsize, GNUNET_SERIALIZE_PART);
-  if (mdsize == -1)
-    {
-      GNUNET_GE_BREAK (ectx, 0);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      GNUNET_free (value);
-      return NULL;
-    }
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (rootEntry) + 2;
-  GNUNET_GE_ASSERT (ectx,
-                    GNUNET_OK == GNUNET_RSA_sign (namespace_priv_key,
-                                                  size
-                                                  -
-                                                  sizeof
-                                                  (GNUNET_RSA_Signature) -
-                                                  sizeof
-                                                  (GNUNET_RSA_PublicKey) -
-                                                  sizeof (unsigned int),
-                                                  &sb->identifier,
-                                                  &sb->signature));
-  value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  value->priority = htonl (priority);
-  value->anonymity_level = htonl (anonymityLevel);
-  value->expiration_time = GNUNET_htonll (expiration);
-  sock = GNUNET_client_connection_create (ectx, cfg);
-  if (sock == NULL)
-    {
-      GNUNET_free (value);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-  if (GNUNET_OK != GNUNET_FS_insert (sock, value))
-    {
-      GNUNET_free (value);
-      GNUNET_client_connection_destroy (sock);
-      GNUNET_RSA_free_key (namespace_priv_key);
-      return NULL;
-    }
-
-
-  /* publish KNBlocks */
-  size += sizeof (GNUNET_EC_KSBlock) - sizeof (GNUNET_EC_SBlock);
-  knvalue = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  *knvalue = *value;
-  knvalue->type = htonl (GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED);
-  knvalue->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  ksb = (GNUNET_EC_KSBlock *) & knvalue[1];
-  ksb->type = htonl (GNUNET_ECRS_BLOCKTYPE_KEYWORD_SIGNED);
-  memcpy (&ksb->sblock,
-          sb, sizeof (GNUNET_EC_SBlock) + mdsize + strlen (rootEntry) + 2);
+  struct ProcessUpdateClosure *pc = cls;
+  struct NamespaceUpdateNode *nsn = value;
 
 
-  if (advertisementURI != NULL)
-    {
-      keywords = advertisementURI->data.ksk.keywords;
-      keywordCount = advertisementURI->data.ksk.keywordCount;
-      cpy =
-        GNUNET_malloc (size - sizeof (GNUNET_EC_KBlock) -
-                       sizeof (unsigned int));
-      memcpy (cpy,
-              &ksb->sblock,
-              size - sizeof (GNUNET_EC_KBlock) - sizeof (unsigned int));
-      for (i = 0; i < keywordCount; i++)
-        {
-          keyword = keywords[i];
-          /* first character of keyword indicates
-             mandatory or not -- ignore for hashing! */
-          GNUNET_hash (&keyword[1], strlen (&keyword[1]), &hc);
-          pk = GNUNET_RSA_create_key_from_hash (&hc);
-          GNUNET_RSA_get_public_key (pk, &ksb->kblock.keyspace);
-          GNUNET_GE_ASSERT (ectx,
-                            size - sizeof (GNUNET_EC_KBlock) -
-                            sizeof (unsigned int) ==
-                            sizeof (GNUNET_EC_SBlock) + mdsize +
-                            strlen (rootEntry) + 2);
-          GNUNET_ECRS_encryptInPlace (&hc, &ksb->sblock,
-                                      size - sizeof (GNUNET_EC_KBlock) -
-                                      sizeof (unsigned int));
-
-          GNUNET_GE_ASSERT (ectx,
-                            GNUNET_OK == GNUNET_RSA_sign (pk,
-                                                          size -
-                                                          sizeof
-                                                          (GNUNET_EC_KBlock) -
-                                                          sizeof (unsigned
-                                                                  int),
-                                                          &ksb->sblock,
-                                                          &ksb->
-                                                          kblock.signature));
-          /* extra check: verify sig */
-          GNUNET_RSA_free_key (pk);
-          if (GNUNET_OK != GNUNET_FS_insert (sock, knvalue))
-            {
-              GNUNET_GE_BREAK (ectx, 0);
-              GNUNET_free (cpy);
-              GNUNET_free (knvalue);
-              GNUNET_free (value);
-              GNUNET_client_connection_destroy (sock);
-              GNUNET_RSA_free_key (namespace_priv_key);
-              return NULL;
-            }
-          /* restore nblock to avoid re-encryption! */
-          memcpy (&ksb->sblock,
-                  cpy,
-                  size - sizeof (GNUNET_EC_KBlock) - sizeof (unsigned int));
-        }
-      GNUNET_free (cpy);
-    }
-  rootURI = GNUNET_malloc (sizeof (URI));
-  rootURI->type = sks;
-  GNUNET_hash (&sb->subspace,
-               sizeof (GNUNET_RSA_PublicKey), &rootURI->data.sks.namespace);
-  rootURI->data.sks.identifier = GNUNET_strdup (rootEntry);
-  GNUNET_free (knvalue);
-  GNUNET_free (value);
-  GNUNET_client_connection_destroy (sock);
-  GNUNET_RSA_free_key (namespace_priv_key);
-
-  return rootURI;
+  pc->ip (pc->ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update);
+  return GNUNET_YES;
 }
 
 }
 
-static struct GNUNET_RSA_PrivateKey *
-read_namespace_key (struct GNUNET_GC_Configuration *cfg,
-                    const GNUNET_HashCode * pid)
+
+/**
+ * Closure for 'find_trees'.
+ */
+struct FindTreeClosure
 {
 {
-  char *fileName;
-  GNUNET_RSA_PrivateKeyEncoded *hke;
-  struct GNUNET_RSA_PrivateKey *hk;
-  char *dst;
-  unsigned long long len;
-
-  fileName = getPseudonymFileName (NULL, cfg, pid);
-  if (GNUNET_OK != GNUNET_disk_file_size (NULL, fileName, &len, GNUNET_YES))
-    {
-      GNUNET_free (fileName);
-      return NULL;
-    }
-  if (len < 2)
-    {
-      GNUNET_GE_LOG (NULL, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("File `%s' does not contain a pseudonym.\n"),
-                     fileName);
-      GNUNET_free (fileName);
-      return NULL;
-    }
-  dst = GNUNET_malloc (len);
-  len = GNUNET_disk_file_read (NULL, fileName, len, dst);
-  hke = (GNUNET_RSA_PrivateKeyEncoded *) dst;
-  if (ntohs (hke->len) != len)
-    {
-      GNUNET_GE_LOG (NULL, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of pseudonym `%s' is invalid.\n"), fileName);
-      GNUNET_free (fileName);
-      GNUNET_free (hke);
-      return NULL;
-    }
-  GNUNET_free (fileName);
-  hk = GNUNET_RSA_decode_key (hke);
-  GNUNET_free (hke);
-  return hk;
-}
+  /**
+   * Namespace we are operating on.
+   */
+  struct GNUNET_FS_Namespace *namespace;
+
+  /**
+   * Array with 'head's of TREEs.
+   */
+  struct NamespaceUpdateNode **tree_array;
+
+  /**
+   * Size of 'tree_array'
+   */
+  unsigned int tree_array_size;
+
+  /**
+   * Current generational ID used.
+   */
+  unsigned int nug;
+
+  /**
+   * Identifier for the current TREE, or UINT_MAX for none yet.
+   */
+  unsigned int id;
+};
 
 
 /**
 
 
 /**
- * Add an entry into a namespace.
+ * Find all nodes reachable from the current node (including the
+ * current node itself).  If they are in no tree, add them to the
+ * current one.   If they are the head of another tree, merge the
+ * trees.  If they are in the middle of another tree, let them be.
+ * We can tell that a node is already in an tree by checking if
+ * its 'nug' field is set to the current 'nug' value.  It is the
+ * head of an tree if it is in the 'tree_array' under its respective
+ * 'tree_id'.
+ *
+ * In short, we're trying to find the smallest number of tree to
+ * cover a directed graph.
  *
  *
- * @param dstU to which URI should the namespace entry refer?
- * @param md what meta-data should be associated with the
- *        entry?
- * @param thisId name of this entry in the namespace (keyword/identifier)
- * @param nextId name of the update for this entry (to be published in
- *               the future; maybe NULL)
- * @param pid unique identifier of the namespace/pseudonym
- * @return URI on success, NULL on error
+ * @param cls closure (of type 'struct FindTreeClosure')
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
  */
  */
-struct GNUNET_ECRS_URI *
-GNUNET_ECRS_namespace_add_content (struct GNUNET_GE_Context *ectx,
-                                   struct GNUNET_GC_Configuration *cfg,
-                                   const GNUNET_HashCode * pid,
-                                   unsigned int anonymityLevel,
-                                   unsigned int priority,
-                                   GNUNET_CronTime expiration,
-                                   const char *thisId,
-                                   const char *nextId,
-                                   const struct GNUNET_ECRS_URI *dstU,
-                                   const struct GNUNET_MetaData *md)
+static int
+find_trees (void *cls, const GNUNET_HashCode * key, void *value)
 {
 {
-  struct GNUNET_ECRS_URI *uri;
-  struct GNUNET_ClientServerConnection *sock;
-  GNUNET_DatastoreValue *value;
-  unsigned int size;
-  unsigned int mdsize;
-  struct GNUNET_RSA_PrivateKey *hk;
-  GNUNET_EC_SBlock *sb;
-  char *dstURI;
-  char *destPos;
-  GNUNET_HashCode hc;           /* hash of thisId = key */
-  GNUNET_HashCode hc2;          /* hash of hc = identifier */
-  int ret;
-  unsigned int nidlen;
-
-  hk = read_namespace_key (cfg, pid);
-  if (hk == NULL)
-    return NULL;
-
-  /* THEN: construct GNUNET_EC_SBlock */
-  dstURI = GNUNET_ECRS_uri_to_string (dstU);
-  mdsize = GNUNET_meta_data_get_serialized_size (md, GNUNET_SERIALIZE_PART);
-  if (nextId == NULL)
-    nextId = "";
-  nidlen = strlen (nextId) + 1;
-  size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen;
-  if (size > MAX_SBLOCK_SIZE)
-    {
-      size = MAX_SBLOCK_SIZE;
-      mdsize =
-        size - (sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen);
-    }
-  value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
-  sb = (GNUNET_EC_SBlock *) & value[1];
-  sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  destPos = (char *) &sb[1];
-  memcpy (destPos, nextId, nidlen);
-  destPos += nidlen;
-  memcpy (destPos, dstURI, strlen (dstURI) + 1);
-  destPos += strlen (dstURI) + 1;
-  mdsize = GNUNET_meta_data_serialize (ectx,
-                                       md,
-                                       destPos,
-                                       mdsize, GNUNET_SERIALIZE_PART);
-  if (mdsize == -1)
-    {
-      GNUNET_GE_BREAK (ectx, 0);
-      GNUNET_free (dstURI);
-      GNUNET_RSA_free_key (hk);
-      GNUNET_free (value);
-      return NULL;
-    }
-  size = sizeof (GNUNET_EC_SBlock) + mdsize + strlen (dstURI) + 1 + nidlen;
-  value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
-  value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
-  value->priority = htonl (priority);
-  value->anonymity_level = htonl (anonymityLevel);
-  value->expiration_time = GNUNET_htonll (expiration);
-  GNUNET_hash (thisId, strlen (thisId), &hc);
-  GNUNET_hash (&hc, sizeof (GNUNET_HashCode), &hc2);
-  uri = GNUNET_malloc (sizeof (URI));
-  uri->type = sks;
-  GNUNET_RSA_get_public_key (hk, &sb->subspace);
-  GNUNET_hash (&sb->subspace,
-               sizeof (GNUNET_RSA_PublicKey), &uri->data.sks.namespace);
-  GNUNET_GE_BREAK (ectx, 0 == memcmp (&uri->data.sks.namespace,
-                                      pid, sizeof (GNUNET_HashCode)));
-  uri->data.sks.identifier = GNUNET_strdup (thisId);
-  GNUNET_hash_xor (&hc2, &uri->data.sks.namespace, &sb->identifier);
-  GNUNET_ECRS_encryptInPlace (&hc, &sb[1], size - sizeof (GNUNET_EC_SBlock));
-  GNUNET_GE_ASSERT (ectx,
-                    GNUNET_OK == GNUNET_RSA_sign (hk,
-                                                  size
-                                                  -
-                                                  sizeof
-                                                  (GNUNET_RSA_Signature) -
-                                                  sizeof
-                                                  (GNUNET_RSA_PublicKey) -
-                                                  sizeof (unsigned int),
-                                                  &sb->identifier,
-                                                  &sb->signature));
-  GNUNET_RSA_free_key (hk);
-  sock = GNUNET_client_connection_create (ectx, cfg);
-  ret = GNUNET_FS_insert (sock, value);
-  if (ret != GNUNET_OK)
-    {
-      GNUNET_free (uri);
-      uri = NULL;
-    }
-  GNUNET_client_connection_destroy (sock);
-  GNUNET_free (value);
-  GNUNET_free (dstURI);
+  struct FindTreeClosure *fc = cls;
+  struct NamespaceUpdateNode *nsn = value;
+  GNUNET_HashCode hc;
 
 
-  return uri;
+  if (nsn->nug == fc->nug)
+  {
+    if (nsn->tree_id == UINT_MAX)
+      return GNUNET_YES;        /* circular */
+    GNUNET_assert (nsn->tree_id < fc->tree_array_size);
+    if (fc->tree_array[nsn->tree_id] != nsn)
+      return GNUNET_YES;        /* part of "another" (directed) TREE,
+                                 * and not root of it, end trace */
+    if (nsn->tree_id == fc->id)
+      return GNUNET_YES;        /* that's our own root (can this be?) */
+    /* merge existing TREE, we have a root for both */
+    fc->tree_array[nsn->tree_id] = NULL;
+    if (fc->id == UINT_MAX)
+      fc->id = nsn->tree_id;    /* take over ID */
+  }
+  else
+  {
+    nsn->nug = fc->nug;
+    nsn->tree_id = UINT_MAX;    /* mark as undef */
+    /* trace */
+    GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
+    GNUNET_CONTAINER_multihashmap_get_multiple (fc->namespace->update_map, &hc,
+                                                &find_trees, fc);
+  }
+  return GNUNET_YES;
 }
 
 }
 
-struct lNCLS
-{
-  struct GNUNET_GE_Context *ectx;
-  struct GNUNET_GC_Configuration *cfg;
-  GNUNET_ECRS_NamespaceInfoProcessor cb;
-  void *cls;
-  int cnt;
-};
 
 
-static int
-processFile_ (void *cls, const char *fileName)
+/**
+ * List all of the identifiers in the namespace for which we could
+ * produce an update.  Namespace updates form a graph where each node
+ * has a name.  Each node can have any number of URI/meta-data entries
+ * which can each be linked to other nodes.  Cycles are possible.
+ *
+ * Calling this function with "next_id" NULL will cause the library to
+ * call "ip" with a root for each strongly connected component of the
+ * graph (a root being a node from which all other nodes in the Tree
+ * are reachable).
+ *
+ * Calling this function with "next_id" being the name of a node will
+ * cause the library to call "ip" with all children of the node.  Note
+ * that cycles within the final tree are possible (including self-loops).
+ * I know, odd definition of a tree, but the GUI will display an actual
+ * tree (GtkTreeView), so that's what counts for the term here.
+ *
+ * @param namespace namespace to inspect for updateable content
+ * @param next_id ID to look for; use NULL to look for tree roots
+ * @param ip function to call on each updateable identifier
+ * @param ip_cls closure for ip
+ */
+void
+GNUNET_FS_namespace_list_updateable (struct GNUNET_FS_Namespace *namespace,
+                                     const char *next_id,
+                                     GNUNET_FS_IdentifierProcessor ip,
+                                     void *ip_cls)
 {
 {
-  struct lNCLS *c = cls;
-  struct GNUNET_RSA_PrivateKey *hk;
-  GNUNET_RSA_PrivateKeyEncoded *hke;
-  char *dst;
-  unsigned long long len;
-  GNUNET_HashCode namespace;
-  GNUNET_RSA_PublicKey pk;
-  const char *name;
-
-  if (GNUNET_OK !=
-      GNUNET_disk_file_size (c->ectx, fileName, &len, GNUNET_YES))
-    return GNUNET_OK;
-  if (len < 2)
+  unsigned int i;
+  unsigned int nug;
+  GNUNET_HashCode hc;
+  struct NamespaceUpdateNode *nsn;
+  struct ProcessUpdateClosure pc;
+  struct FindTreeClosure fc;
+
+  if (namespace->update_nodes == NULL)
+    read_update_information_graph (namespace);
+  if (namespace->update_nodes == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "No updateable nodes found for ID `%s'\n", next_id);
+    return;                     /* no nodes */
+  }
+  if (namespace->update_map == NULL)
+  {
+    /* need to construct */
+    namespace->update_map =
+        GNUNET_CONTAINER_multihashmap_create (2 +
+                                              3 * namespace->update_node_count /
+                                              4);
+    for (i = 0; i < namespace->update_node_count; i++)
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      return GNUNET_OK;
+      nsn = namespace->update_nodes[i];
+      GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
+      GNUNET_CONTAINER_multihashmap_put (namespace->update_map, &hc, nsn,
+                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
     }
     }
-  dst = GNUNET_malloc (len);
-  len = GNUNET_disk_file_read (c->ectx, fileName, len, dst);
-  hke = (GNUNET_RSA_PrivateKeyEncoded *) dst;
-  if (ntohs (hke->len) != len)
+  }
+  if (next_id != NULL)
+  {
+    GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc);
+    pc.ip = ip;
+    pc.ip_cls = ip_cls;
+    GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, &hc,
+                                                &process_update_node, &pc);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calculating TREEs to find roots of update trees\n");
+  /* Find heads of TREEs in update graph */
+  nug = ++namespace->nug_gen;
+  fc.tree_array = NULL;
+  fc.tree_array_size = 0;
+
+  for (i = 0; i < namespace->update_node_count; i++)
+  {
+    nsn = namespace->update_nodes[i];
+    if (nsn->nug == nug)
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      GNUNET_free (hke);
-      return GNUNET_OK;
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TREE of node `%s' is %u\n", nsn->id,
+                  nsn->nug);
+      continue;                 /* already placed in TREE */
     }
     }
-  hk = GNUNET_RSA_decode_key (hke);
-  GNUNET_free (hke);
-  if (hk == NULL)
+    GNUNET_CRYPTO_hash (nsn->update, strlen (nsn->update), &hc);
+    nsn->nug = nug;
+    nsn->tree_id = UINT_MAX;
+    fc.id = UINT_MAX;
+    fc.nug = nug;
+    fc.namespace = namespace;
+    GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, &hc,
+                                                &find_trees, &fc);
+    if (fc.id == UINT_MAX)
     {
     {
-      GNUNET_GE_LOG (c->ectx,
-                     GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
-                     _("Format of file `%s' is invalid, trying to remove.\n"),
-                     fileName);
-      UNLINK (fileName);
-      GNUNET_GE_BREAK (c->ectx, 0);
-      return GNUNET_SYSERR;
+      /* start new TREE */
+      for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++)
+      {
+        if (fc.tree_array[fc.id] == NULL)
+        {
+          fc.tree_array[fc.id] = nsn;
+          nsn->tree_id = fc.id;
+          break;
+        }
+      }
+      if (fc.id == fc.tree_array_size)
+      {
+        GNUNET_array_append (fc.tree_array, fc.tree_array_size, nsn);
+        nsn->tree_id = fc.id;
+      }
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Starting new TREE %u with node `%s'\n", nsn->tree_id,
+                  nsn->id);
+      /* put all nodes with same identifier into this TREE */
+      GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
+      fc.id = nsn->tree_id;
+      fc.nug = nug;
+      fc.namespace = namespace;
+      GNUNET_CONTAINER_multihashmap_get_multiple (namespace->update_map, &hc,
+                                                  &find_trees, &fc);
     }
     }
-  GNUNET_RSA_get_public_key (hk, &pk);
-  GNUNET_RSA_free_key (hk);
-  GNUNET_hash (&pk, sizeof (GNUNET_RSA_PublicKey), &namespace);
-  if (NULL != c->cb)
+    else
     {
     {
-      name = fileName;
-      while (NULL != strstr (name, DIR_SEPARATOR_STR))
-        name = 1 + strstr (name, DIR_SEPARATOR_STR);
-      if (GNUNET_OK == c->cb (&namespace, name, c->cls))
-        c->cnt++;
-      else
-        c->cnt = GNUNET_SYSERR;
+      /* make head of TREE "id" */
+      fc.tree_array[fc.id] = nsn;
+      nsn->tree_id = fc.id;
     }
     }
-  else
-    c->cnt++;
-  return GNUNET_OK;
-}
-
-/**
- * Build a list of all available namespaces
- *
- * @param list where to store the names (is allocated, caller frees)
- * @return GNUNET_SYSERR on error, otherwise the number of pseudonyms in list
- */
-int
-GNUNET_ECRS_get_namespaces (struct GNUNET_GE_Context *ectx,
-                            struct GNUNET_GC_Configuration *cfg,
-                            GNUNET_ECRS_NamespaceInfoProcessor cb, void *cls)
-{
-  char *dirName;
-  struct lNCLS myCLS;
-
-  myCLS.cls = cls;
-  myCLS.cb = cb;
-  myCLS.cnt = 0;
-  myCLS.ectx = ectx;
-  myCLS.cfg = cfg;
-  dirName = getPseudonymFileName (ectx, cfg, NULL);
-  GNUNET_disk_directory_scan (ectx, dirName, &processFile_, &myCLS);
-  GNUNET_free (dirName);
-  return myCLS.cnt;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TREE of node `%s' is %u\n", nsn->id,
+                fc.id);
+  }
+  for (i = 0; i < fc.tree_array_size; i++)
+  {
+    nsn = fc.tree_array[i];
+    if (NULL != nsn)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Root of TREE %u is node `%s'\n", i,
+                  nsn->id);
+      ip (ip_cls, nsn->id, nsn->uri, nsn->md, nsn->update);
+    }
+  }
+  GNUNET_array_grow (fc.tree_array, fc.tree_array_size, 0);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Done processing TREEs\n");
 }
 
 
 }
 
 
-
-/* end of namespace.c */
-#endif
+/* end of fs_namespace.c */