refactoring datastore API to use MQ API, also fixing misc. bugs in new mysql backend
[oweals/gnunet.git] / src / fs / fs_namespace.c
index 9e198adc2cbd05b1ae108782fa2e13288bd4546c..69876b8b55a8ae4dbf41afadf06e66f8eeefd636 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, 2010 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2003-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
 
      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.
 */
 
 /**
  * @file fs/fs_namespace.c
 */
 
 /**
  * @file fs/fs_namespace.c
- * @brief create and destroy namespaces
+ * @brief publishing to namespaces, and tracking updateable entries
+ *        for our namespaces
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @author Christian Grothoff
  */
 #include "platform.h"
 #include "gnunet_signatures.h"
 #include "gnunet_util_lib.h"
 #include "gnunet_fs_service.h"
 #include "gnunet_signatures.h"
 #include "gnunet_util_lib.h"
 #include "gnunet_fs_service.h"
-#include "fs.h"
-
-/**
- * Return the name of the directory in which we store
- * our local namespaces (or rather, their public keys).
- *
- * @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;
-}
+#include "fs_api.h"
+#include "fs_publish_ublock.h"
 
 
 /**
 
 
 /**
- * Context for advertising a namespace.
+ * Information about an (updateable) node in the
+ * namespace.
  */
  */
-struct AdvertisementContext
+struct NamespaceUpdateNode
 {
   /**
 {
   /**
-   * Function to call with the result.
+   * Identifier for this node.
    */
    */
-  GNUNET_FS_PublishContinuation cont;
+  char *id;
 
   /**
 
   /**
-   * Closure for cont.
+   * Identifier of children of this node.
    */
    */
-  void *cont_cls;
+  char *update;
 
   /**
 
   /**
-   * Datastore handle.
+   * Metadata for this entry.
    */
    */
-  struct GNUNET_DATASTORE_Handle *dsh;
+  struct GNUNET_CONTAINER_MetaData *md;
+
+  /**
+   * URI of this entry in the namespace.
+   */
+  struct GNUNET_FS_Uri *uri;
 
   /**
 
   /**
-   * Our KSK URI.
-   */ 
-  struct GNUNET_FS_Uri *ksk_uri;
+   * Namespace update generation ID.  Used to ensure
+   * freshness of the tree_id.
+   */
+  unsigned int nug;
 
   /**
 
   /**
-   * Plaintext.
+   * TREE this entry belongs to (if nug is current).
    */
    */
-  char *pt;
+  unsigned int tree_id;
+
+};
+
+
+/**
+ * Handle to update information for a namespace.
+ */
+struct GNUNET_FS_UpdateInformationGraph
+{
 
   /**
 
   /**
-   * NBlock to sign and store.
+   * Handle to the FS service context.
    */
    */
-  struct NBlock *nb;
+  struct GNUNET_FS_Handle *h;
 
   /**
 
   /**
-   * The namespace.
+   * Array with information about nodes in the namespace.
    */
    */
-  struct GNUNET_FS_Namespace *ns;
+  struct NamespaceUpdateNode **update_nodes;
 
   /**
 
   /**
-   * Expiration time.
+   * Private key for the namespace.
    */
    */
-  struct GNUNET_TIME_Absolute expiration;
+  struct GNUNET_CRYPTO_EcdsaPrivateKey ns;
 
   /**
 
   /**
-   * Number of bytes of plaintext.
-   */ 
-  size_t pt_size;
+   * Hash map mapping identifiers of update nodes
+   * to the update nodes (initialized on-demand).
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *update_map;
 
   /**
 
   /**
-   * Anonymity level.
+   * Size of the update nodes array.
    */
    */
-  uint32_t anonymity;
+  unsigned int update_node_count;
 
   /**
 
   /**
-   * Content priority.
+   * Reference counter.
    */
    */
-  uint32_t priority;
+  unsigned int rc;
 
   /**
 
   /**
-   * Current keyword offset.
+   * Generator for unique nug numbers.
    */
    */
-  unsigned int pos;
+  unsigned int nug_gen;
 };
 
 
 /**
 };
 
 
 /**
- * Continuation called to notify client about result of the
- * operation.
+ * Return the name of the directory in which we store
+ * the update information graph for the given local namespace.
  *
  *
- * @param cls closure (our struct AdvertismentContext)
- * @param success GNUNET_SYSERR on failure
- * @param msg NULL on success, otherwise an error message
+ * @param h file-sharing handle
+ * @param ns namespace handle
+ * @return NULL on error, otherwise the name of the directory
  */
  */
-static void
-advertisement_cont (void *cls,
-                   int success,
-                   const char *msg)
+static char *
+get_update_information_directory (struct GNUNET_FS_Handle *h,
+                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
 {
 {
-  struct AdvertisementContext *ac = cls;
-  const char *keyword;
-  GNUNET_HashCode key;
-  GNUNET_HashCode query;
-  struct GNUNET_CRYPTO_AesSessionKey skey;
-  struct GNUNET_CRYPTO_AesInitializationVector iv;
-  struct GNUNET_CRYPTO_RsaPrivateKey *pk;
-  
-  if (GNUNET_OK != success)
-    {
-      /* error! */
-      GNUNET_DATASTORE_disconnect (ac->dsh, GNUNET_NO);
-      ac->cont (ac->cont_cls, NULL, msg);
-      GNUNET_FS_uri_destroy (ac->ksk_uri);
-      GNUNET_free (ac->pt);
-      GNUNET_free (ac->nb);
-      GNUNET_FS_namespace_delete (ac->ns, GNUNET_NO);
-      GNUNET_free (ac);
-      return;
-    }
-  if (ac->pos == ac->ksk_uri->data.ksk.keywordCount)
-    {
-      /* done! */
-      GNUNET_DATASTORE_disconnect (ac->dsh, GNUNET_NO);
-      ac->cont (ac->cont_cls, ac->ksk_uri, NULL);
-      GNUNET_FS_uri_destroy (ac->ksk_uri);
-      GNUNET_free (ac->pt);
-      GNUNET_free (ac->nb);
-      GNUNET_FS_namespace_delete (ac->ns, GNUNET_NO);
-      GNUNET_free (ac);
-      return;
-    }
-  keyword = ac->ksk_uri->data.ksk.keywords[ac->pos++];
-  /* first character of keyword indicates if it is
-     mandatory or not -- ignore for hashing */
-  GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
-  GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
-  GNUNET_CRYPTO_aes_encrypt (ac->pt,
-                            ac->pt_size,
-                            &skey,
-                            &iv,
-                            &ac->nb[1]);
-  GNUNET_break (GNUNET_OK == 
-               GNUNET_CRYPTO_rsa_sign (ac->ns->key,
-                                       &ac->nb->ns_purpose,
-                                       &ac->nb->ns_signature));
-  pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
-  GNUNET_CRYPTO_rsa_key_get_public (pk, &ac->nb->keyspace);
-  GNUNET_CRYPTO_hash (&ac->nb->keyspace,
-                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                     &query);
-  GNUNET_break (GNUNET_OK == 
-               GNUNET_CRYPTO_rsa_sign (pk,
-                                       &ac->nb->ksk_purpose,
-                                       &ac->nb->ksk_signature));
-  GNUNET_CRYPTO_rsa_key_free (pk);
-  GNUNET_DATASTORE_put (ac->dsh,
-                       0 /* no reservation */, 
-                       &query,
-                       ac->pt_size + sizeof (struct NBlock),
-                       ac->nb,
-                       GNUNET_BLOCK_TYPE_NBLOCK,
-                       ac->priority,
-                       ac->anonymity,
-                       ac->expiration,
-                       -2, 1,
-                       GNUNET_CONSTANTS_SERVICE_TIMEOUT, 
-                       &advertisement_cont,
-                       ac);
+  char *dn;
+  char *ret;
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub;
+  struct GNUNET_HashCode hc;
+  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (h->cfg, "FS", "UPDATE_DIR",
+                                               &dn))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "fs", "UPDATE_DIR");
+    return NULL;
+  }
+  GNUNET_CRYPTO_ecdsa_key_get_public (ns, &pub);
+  GNUNET_CRYPTO_hash (&pub, sizeof (pub), &hc);
+  GNUNET_CRYPTO_hash_to_enc (&hc,
+                            &enc);
+  GNUNET_asprintf (&ret, "%s%s%s",
+                  dn,
+                  DIR_SEPARATOR_STR,
+                  (const char *) enc.encoding);
+  GNUNET_free (dn);
+  return ret;
 }
 
 
 /**
 }
 
 
 /**
- * Publish an advertismement for a namespace.  
+ * Release memory occupied by UIG datastructure.
  *
  *
- * @param h handle to the file sharing subsystem
- * @param ksk_uri keywords to use for advertisment
- * @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 rootEntry name of the root of the namespace
- * @param cont continuation
- * @param cont_cls closure for cont
+ * @param uig data structure to free
  */
  */
-void
-GNUNET_FS_namespace_advertise (struct GNUNET_FS_Handle *h,
-                              struct GNUNET_FS_Uri *ksk_uri,
-                              struct GNUNET_FS_Namespace *namespace,
-                              const struct GNUNET_CONTAINER_MetaData *meta,
-                              uint32_t anonymity,
-                              uint32_t priority,
-                              struct GNUNET_TIME_Absolute expiration,
-                              const char *rootEntry,
-                              GNUNET_FS_PublishContinuation cont,
-                              void *cont_cls)
+static void
+free_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
 {
 {
-  size_t reslen;
-  size_t size;
-  ssize_t mdsize;
-  struct NBlock *nb;
-  char *mdst;
-  struct GNUNET_DATASTORE_Handle *dsh;
-  struct AdvertisementContext *ctx;
-  char *pt;
+  unsigned int i;
+  struct NamespaceUpdateNode *nsn;
+
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->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 (uig->update_nodes, uig->update_node_count,
+                    0);
+  if (NULL != uig->update_map)
+    GNUNET_CONTAINER_multihashmap_destroy (uig->update_map);
+  GNUNET_free (uig);
+}
 
 
-  /* create advertisements */
-  mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
-  if (-1 == mdsize)
-    {
-      cont (cont_cls, NULL, _("Failed to serialize meta data"));
-      return;
-    }
-  reslen = strlen (rootEntry) + 1;
-  size = mdsize + sizeof (struct NBlock) + reslen;
-  if (size > MAX_NBLOCK_SIZE)
+
+/**
+ * Write a namespace's update node graph to a file.
+ *
+ * @param uig update information graph to dump
+ */
+static void
+write_update_information_graph (struct GNUNET_FS_UpdateInformationGraph *uig)
+{
+  char *fn;
+  struct GNUNET_BIO_WriteHandle *wh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+
+  fn = get_update_information_directory (uig->h,
+                                        &uig->ns);
+  wh = GNUNET_BIO_write_open (fn);
+  if (NULL == wh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to open `%s' for writing: %s\n"),
+                fn,
+                STRERROR (errno));
+    GNUNET_free (fn);
+    return;
+  }
+  if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, uig->update_node_count))
+    goto END;
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    n = uig->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)))
     {
     {
-      size = MAX_NBLOCK_SIZE;
-      mdsize = size - sizeof (struct NBlock) - reslen;
+      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"),
+                fn,
+                STRERROR (errno));
+  GNUNET_free (fn);
+}
 
 
-  pt = GNUNET_malloc (mdsize + reslen);
-  memcpy (pt, rootEntry, reslen);
-  mdst = &pt[reslen];
-  mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
-                                                &mdst,
-                                                mdsize,
-                                                GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
-  if (mdsize == -1)
+
+/**
+ * Read the namespace update node graph from a file.
+ *
+ * @param h FS handle to use
+ * @param ns namespace to read
+ * @return update graph, never NULL
+ */
+static struct GNUNET_FS_UpdateInformationGraph *
+read_update_information_graph (struct GNUNET_FS_Handle *h,
+                              const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns)
+{
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+  char *fn;
+  struct GNUNET_BIO_ReadHandle *rh;
+  unsigned int i;
+  struct NamespaceUpdateNode *n;
+  char *uris;
+  uint32_t count;
+  char *emsg;
+
+  uig = GNUNET_new (struct GNUNET_FS_UpdateInformationGraph);
+  uig->h = h;
+  uig->ns = *ns;
+  fn = get_update_information_directory (h, ns);
+  if (GNUNET_YES != GNUNET_DISK_file_test (fn))
+  {
+    GNUNET_free (fn);
+    return uig;
+  }
+  rh = GNUNET_BIO_read_open (fn);
+  if (NULL == rh)
+  {
+    GNUNET_free (fn);
+    return uig;
+  }
+  if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, &count))
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (count > 1024 * 1024)
+  {
+    GNUNET_break (0);
+    goto END;
+  }
+  if (0 == count)
+    goto END;
+  uig->update_nodes =
+    GNUNET_malloc (count * sizeof (struct NamespaceUpdateNode *));
+
+  for (i = 0; i < count; i++)
+  {
+    n = GNUNET_new (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_break (0);
-      GNUNET_free (pt);
-      cont (cont_cls, NULL, _("Failed to serialize meta data"));
-      return;
+      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;
     }
     }
-  size = mdsize + sizeof (struct NBlock) + reslen;  
-  nb = GNUNET_malloc (size);
-  GNUNET_CRYPTO_rsa_key_get_public (namespace->key, 
-                                   &nb->subspace);
-  nb->ns_purpose.size = htonl (mdsize + reslen + 
-                           sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
-                           sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
-  nb->ns_purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_NBLOCK);
-  nb->ksk_purpose.size = htonl (size - sizeof (struct GNUNET_CRYPTO_RsaSignature));
-  nb->ksk_purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_NBLOCK_KSIG);
-  dsh = GNUNET_DATASTORE_connect (h->cfg, h->sched);
-  if (NULL == dsh)
+    n->uri = GNUNET_FS_uri_parse (uris, &emsg);
+    GNUNET_free (uris);
+    if (n->uri == NULL)
     {
     {
-      GNUNET_free (nb);
-      GNUNET_free (pt);
-      cont (cont_cls, NULL, _("Failed to connect to datastore service"));
-      return;
-    }  
-  ctx = GNUNET_malloc (sizeof (struct AdvertisementContext));
-  ctx->cont = cont;
-  ctx->cont_cls = cont_cls;
-  ctx->dsh = dsh;
-  ctx->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
-  ctx->nb = nb;
-  ctx->pt = pt;
-  ctx->pt_size = mdsize + reslen;
-  ctx->ns = namespace;
-  ctx->ns->rc++;
-  ctx->anonymity = anonymity;
-  ctx->priority = priority;
-  ctx->expiration = expiration;
-  advertisement_cont (ctx, GNUNET_OK, 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;
+    }
+    uig->update_nodes[i] = n;
+  }
+  uig->update_node_count = i;
+ END:
+  if (GNUNET_OK != GNUNET_BIO_read_close (rh, &emsg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to read `%s': %s\n"),
+               fn, emsg);
+    GNUNET_free (emsg);
+  }
+  GNUNET_free (fn);
+  return uig;
 }
 
 
 /**
 }
 
 
 /**
- * Create a namespace with the given name; if one already
- * exists, return a handle to the existing namespace.
+ * Context for the SKS publication.
+ */
+struct GNUNET_FS_PublishSksContext
+{
+
+  /**
+   * 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_CRYPTO_EcdsaPrivateKey ns;
+
+  /**
+   * Handle to the datastore.
+   */
+  struct GNUNET_DATASTORE_Handle *dsh;
+
+  /**
+   * Handle to FS.
+   */
+  struct GNUNET_FS_Handle *h;
+
+  /**
+   * Function to call once we're done.
+   */
+  GNUNET_FS_PublishContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Handle for our UBlock operation request.
+   */
+  struct GNUNET_FS_PublishUblockContext *uc;
+};
+
+
+/**
+ * Function called by the UBlock construction with
+ * the result from the PUT (UBlock) request.
  *
  *
- * @param h handle to the file sharing subsystem
- * @param name name to use for the namespace
- * @return handle to the namespace, NULL on error
+ * @param cls closure of type "struct GNUNET_FS_PublishSksContext*"
+ * @param msg error message (or NULL)
  */
  */
-struct GNUNET_FS_Namespace *
-GNUNET_FS_namespace_create (struct GNUNET_FS_Handle *h,
-                           const char *name)
+static void
+sks_publish_cont (void *cls,
+                 const char *msg)
 {
 {
-  char *dn;
-  char *fn;
-  struct GNUNET_FS_Namespace *ret;
+  struct GNUNET_FS_PublishSksContext *psc = cls;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+
+  psc->uc = NULL;
+  if (NULL != msg)
+  {
+    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 */
+    uig = read_update_information_graph (psc->h,
+                                        &psc->ns);
+    GNUNET_array_append (uig->update_nodes,
+                        uig->update_node_count,
+                        psc->nsn);
+    psc->nsn = NULL;
+    write_update_information_graph (uig);
+    free_update_information_graph (uig);
+  }
+  if (NULL != psc->cont)
+    psc->cont (psc->cont_cls, psc->uri, NULL);
+  GNUNET_FS_publish_sks_cancel (psc);
+}
 
 
-  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->rc = 1;
-  ret->key = GNUNET_CRYPTO_rsa_key_create_from_file (fn);
-  if (ret->key == NULL)
+
+/**
+ * Publish an SBlock on GNUnet.
+ *
+ * @param h handle to the file sharing subsystem
+ * @param ns 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)
+ */
+struct GNUNET_FS_PublishSksContext *
+GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
+                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
+                       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)
+{
+  struct GNUNET_FS_PublishSksContext *psc;
+  struct GNUNET_FS_Uri *sks_uri;
+
+  sks_uri = GNUNET_new (struct GNUNET_FS_Uri);
+  sks_uri->type = GNUNET_FS_URI_SKS;
+  sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
+  GNUNET_CRYPTO_ecdsa_key_get_public (ns,
+                                   &sks_uri->data.sks.ns);
+
+  psc = GNUNET_new (struct GNUNET_FS_PublishSksContext);
+  psc->h = h;
+  psc->uri = sks_uri;
+  psc->cont = cont;
+  psc->cont_cls = cont_cls;
+  psc->ns = *ns;
+  if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
+  {
+    psc->dsh = GNUNET_DATASTORE_connect (h->cfg);
+    if (NULL == psc->dsh)
     {
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 _("Failed to create or read private key for namespace `%s'\n"),
-                 name);
-      GNUNET_free (ret);
-      GNUNET_free (fn);
+      sks_publish_cont (psc,
+                       _("Failed to connect to datastore."));
       return NULL;
     }
       return NULL;
     }
-  ret->name = GNUNET_strdup (name);
-  ret->filename = fn;
-  return ret;
+  }
+  if (NULL != update)
+  {
+    psc->nsn = GNUNET_new (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->uc = GNUNET_FS_publish_ublock_ (h,
+                                      psc->dsh,
+                                      identifier,
+                                      update,
+                                      ns,
+                                      meta,
+                                      uri,
+                                      bo,
+                                      options,
+                                      &sks_publish_cont,
+                                      psc);
+  return psc;
 }
 
 
 /**
 }
 
 
 /**
- * Delete a namespace handle.  Can be used for a clean shutdown (free
- * memory) or also to freeze the namespace to prevent further
- * insertions by anyone.
+ * Abort the SKS publishing operation.
  *
  *
- * @param namespace handle to the namespace that should be deleted / freed
- * @param freeze prevents future insertions; creating a namespace
- *        with the same name again will create a fresh namespace instead
+ * @param psc context of the operation to abort.
+ */
+void
+GNUNET_FS_publish_sks_cancel (struct GNUNET_FS_PublishSksContext *psc)
+{
+  if (NULL != psc->uc)
+  {
+    GNUNET_FS_publish_ublock_cancel_ (psc->uc);
+    psc->uc = NULL;
+  }
+  if (NULL != psc->dsh)
+  {
+    GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
+    psc->dsh = NULL;
+  }
+  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);
+}
+
+
+/**
+ * Closure for 'process_update_node'.
+ */
+struct ProcessUpdateClosure
+{
+  /**
+   * Function to call for each node.
+   */
+  GNUNET_FS_IdentifierProcessor ip;
+
+  /**
+   * Closure for 'ip'.
+   */
+  void *ip_cls;
+};
+
+
+/**
+ * Call the iterator in the closure for each node.
  *
  *
- * @return GNUNET_OK on success, GNUNET_SYSERR 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.
  */
  */
-int 
-GNUNET_FS_namespace_delete (struct GNUNET_FS_Namespace *namespace,
-                           int freeze)
+static int
+process_update_node (void *cls,
+                    const struct GNUNET_HashCode *key,
+                    void *value)
 {
 {
-  namespace->rc--;
-  if (freeze)
-    {
-      if (0 != UNLINK (namespace->filename))
-       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                                 "unlink",
-                                 namespace->filename);      
-    }
-  if (0 == namespace->rc)
-    {
-      GNUNET_CRYPTO_rsa_key_free (namespace->key);
-      GNUNET_free (namespace->filename);
-      GNUNET_free (namespace->name);
-      GNUNET_free (namespace);
-    }
-  return GNUNET_OK;
+  struct ProcessUpdateClosure *pc = cls;
+  struct NamespaceUpdateNode *nsn = value;
+
+  pc->ip (pc->ip_cls,
+         nsn->id,
+         nsn->uri,
+         nsn->md,
+         nsn->update);
+  return GNUNET_YES;
 }
 
 
 /**
 }
 
 
 /**
- * Context for the 'process_namespace' callback.
- * Specifies a function to call on each namespace.
+ * Closure for 'find_trees'.
  */
  */
-struct ProcessNamespaceContext
+struct FindTreeClosure
 {
   /**
 {
   /**
-   * Function to call.
+   * UIG we are operating on.
    */
    */
-  GNUNET_FS_NamespaceInfoProcessor cb;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
 
   /**
 
   /**
-   * Closure for 'cb'.
+   * Array with 'head's of TREEs.
    */
    */
-  void *cb_cls;
+  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;
 };
 
 
 /**
 };
 
 
 /**
- * Function called with a filename of a namespace. Reads the key and
- * calls the callback.
+ * 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 cls closure (struct ProcessNamespaceContext)
- * @param filename complete filename (absolute path)
- * @return GNUNET_OK to continue to iterate,
- *  GNUNET_SYSERR to abort iteration with 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.
  */
 static int
  */
 static int
-process_namespace (void *cls, 
-                  const char *filename)
+find_trees (void *cls,
+           const struct GNUNET_HashCode *key,
+           void *value)
 {
 {
-  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;
+  struct FindTreeClosure *fc = cls;
+  struct NamespaceUpdateNode *nsn = value;
+  struct GNUNET_HashCode hc;
+
+  if (nsn->nug == fc->nug)
+  {
+    if (UINT_MAX == nsn->tree_id)
+      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 (UINT_MAX == fc->id)
+      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->uig->update_map, &hc,
+                                                &find_trees, fc);
+  }
+  return GNUNET_YES;
 }
 
 
 /**
 }
 
 
 /**
- * Build a list of all available local (!) namespaces The returned
- * names are only the nicknames since we only iterate over the local
- * namespaces.
+ * 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.
  *
  *
- * @param h handle to the file sharing subsystem
- * @param cb function to call on each known namespace
- * @param cb_cls closure for cb
+ * 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 h fs handle to use
+ * @param ns 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 (struct GNUNET_FS_Handle *h,
-                         GNUNET_FS_NamespaceInfoProcessor cb,
-                         void *cb_cls)
+void
+GNUNET_FS_namespace_list_updateable (struct GNUNET_FS_Handle *h,
+                                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
+                                     const char *next_id,
+                                     GNUNET_FS_IdentifierProcessor ip,
+                                     void *ip_cls)
 {
 {
-  char *dn;
-  struct ProcessNamespaceContext ctx;
-  
-  dn = get_namespace_directory (h);
-  if (dn == NULL)
+  unsigned int i;
+  unsigned int nug;
+  struct GNUNET_HashCode hc;
+  struct NamespaceUpdateNode *nsn;
+  struct ProcessUpdateClosure pc;
+  struct FindTreeClosure fc;
+  struct GNUNET_FS_UpdateInformationGraph *uig;
+
+  uig = read_update_information_graph (h, ns);
+  if (NULL == uig->update_nodes)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "No updateable nodes found for ID `%s'\n", next_id);
+    free_update_information_graph (uig);
+    return;                     /* no nodes */
+  }
+  uig->update_map =
+    GNUNET_CONTAINER_multihashmap_create (2 +
+                                         3 * uig->update_node_count /
+                                         4,
+                                         GNUNET_NO);
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->update_nodes[i];
+    GNUNET_CRYPTO_hash (nsn->id, strlen (nsn->id), &hc);
+    GNUNET_CONTAINER_multihashmap_put (uig->update_map, &hc, nsn,
+                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  }
+  if (NULL != next_id)
+  {
+    GNUNET_CRYPTO_hash (next_id, strlen (next_id), &hc);
+    pc.ip = ip;
+    pc.ip_cls = ip_cls;
+    GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                &process_update_node, &pc);
+    free_update_information_graph (uig);
     return;
     return;
-  ctx.cb = cb;
-  ctx.cb_cls = cb_cls;
-  GNUNET_DISK_directory_scan (dn,
-                             &process_namespace,
-                             &ctx);
-  GNUNET_free (dn);
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calculating TREEs to find roots of update trees\n");
+  /* Find heads of TREEs in update graph */
+  nug = ++uig->nug_gen;
+  fc.tree_array = NULL;
+  fc.tree_array_size = 0;
+
+  for (i = 0; i < uig->update_node_count; i++)
+  {
+    nsn = uig->update_nodes[i];
+    if (nsn->nug == nug)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TREE of node `%s' is %u\n", nsn->id,
+                  nsn->nug);
+      continue;                 /* already placed in TREE */
+    }
+    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.uig = uig;
+    GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                &find_trees, &fc);
+    if (UINT_MAX == fc.id)
+    {
+      /* start new TREE */
+      for (fc.id = 0; fc.id < fc.tree_array_size; fc.id++)
+      {
+        if (NULL == fc.tree_array[fc.id])
+        {
+          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.uig = uig;
+      GNUNET_CONTAINER_multihashmap_get_multiple (uig->update_map, &hc,
+                                                  &find_trees, &fc);
+    }
+    else
+    {
+      /* make head of TREE "id" */
+      fc.tree_array[fc.id] = nsn;
+      nsn->tree_id = fc.id;
+    }
+    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");
+  free_update_information_graph (uig);
 }
 
 }
 
-/* end of fs_namespace.c */
 
 
+/* end of fs_namespace.c */