Refactoring gnunet time
[oweals/gnunet.git] / src / fs / fs_search.c
index 90a28052f6232472c711fefff8e2f4398892e572..16e1dd1d58c4b2153d890c162889f72b3aea6da6 100644 (file)
@@ -4,7 +4,7 @@
 
      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
  *
  * TODO:
  * @author Christian Grothoff
  *
  * TODO:
- * - handle SKS updates searches nicely (can wait)
- * - handle availability probes (can wait)
- * - make operations persistent (can wait)
- * - handle namespace advertisements (can wait)
  * - add support for pushing "already seen" information
  *   to FS service for bloomfilter (can wait)
  */
  * - add support for pushing "already seen" information
  *   to FS service for bloomfilter (can wait)
  */
 
 #define DEBUG_SEARCH GNUNET_NO
 
 
 #define DEBUG_SEARCH GNUNET_NO
 
-
-
 /**
 /**
- * Fill in all of the generic fields for 
- * a search event.
+ * Fill in all of the generic fields for a search event and
+ * call the callback.
  *
  * @param pi structure to fill in
  * @param sc overall search context
  *
  * @param pi structure to fill in
  * @param sc overall search context
+ * @return value returned by the callback
  */
  */
-static void
-make_search_status (struct GNUNET_FS_ProgressInfo *pi,
-                   struct GNUNET_FS_SearchContext *sc)
-{
+void *
+GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
+                              struct GNUNET_FS_SearchContext *sc)
+{  
+  void *ret;
   pi->value.search.sc = sc;
   pi->value.search.cctx
     = sc->client_info;
   pi->value.search.pctx
   pi->value.search.sc = sc;
   pi->value.search.cctx
     = sc->client_info;
   pi->value.search.pctx
-    = (sc->parent == NULL) ? NULL : sc->parent->client_info;
+    = (sc->psearch_result == NULL) ? NULL : sc->psearch_result->client_info;
   pi->value.search.query 
     = sc->uri;
   pi->value.search.duration = GNUNET_TIME_absolute_get_duration (sc->start_time);
   pi->value.search.anonymity = sc->anonymity;
   pi->value.search.query 
     = sc->uri;
   pi->value.search.duration = GNUNET_TIME_absolute_get_duration (sc->start_time);
   pi->value.search.anonymity = sc->anonymity;
+  ret =  sc->h->upcb (sc->h->upcb_cls,
+                     pi);
+  return ret;
 }
 
 
 }
 
 
@@ -71,7 +70,7 @@ make_search_status (struct GNUNET_FS_ProgressInfo *pi,
  * 
  * @param cls points to the URI we check against
  * @param key not used
  * 
  * @param cls points to the URI we check against
  * @param key not used
- * @param value a "struct SearchResult" who's URI we
+ * @param value a "struct GNUNET_FS_SearchResult" who's URI we
  *        should compare with
  * @return GNUNET_SYSERR if the result is present,
  *         GNUNET_OK otherwise
  *        should compare with
  * @return GNUNET_SYSERR if the result is present,
  *         GNUNET_OK otherwise
@@ -82,7 +81,7 @@ test_result_present (void *cls,
                     void *value)
 {
   const struct GNUNET_FS_Uri *uri = cls;
                     void *value)
 {
   const struct GNUNET_FS_Uri *uri = cls;
-  struct SearchResult *sr = value;
+  struct GNUNET_FS_SearchResult *sr = value;
 
   if (GNUNET_FS_uri_test_equal (uri,
                                sr->uri))
 
   if (GNUNET_FS_uri_test_equal (uri,
                                sr->uri))
@@ -100,16 +99,16 @@ test_result_present (void *cls,
  */
 static void
 notify_client_chk_result (struct GNUNET_FS_SearchContext *sc, 
  */
 static void
 notify_client_chk_result (struct GNUNET_FS_SearchContext *sc, 
-                         struct SearchResult *sr)
+                         struct GNUNET_FS_SearchResult *sr)
 {                        
   struct GNUNET_FS_ProgressInfo pi;
 
   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
 {                        
   struct GNUNET_FS_ProgressInfo pi;
 
   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
-  make_search_status (&pi, sc);
   pi.value.search.specifics.result.meta = sr->meta;
   pi.value.search.specifics.result.uri = sr->uri;
   pi.value.search.specifics.result.meta = sr->meta;
   pi.value.search.specifics.result.uri = sr->uri;
-  sr->client_info = sc->h->upcb (sc->h->upcb_cls,
-                                &pi);
+  pi.value.search.specifics.result.result = sr;
+  pi.value.search.specifics.result.applicability_rank = sr->optional_support;
+  sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
 }
 
 
 }
 
 
@@ -122,12 +121,11 @@ notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
  */
 static void
 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc, 
  */
 static void
 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc, 
-                         struct SearchResult *sr)
+                         struct GNUNET_FS_SearchResult *sr)
 {                        
   struct GNUNET_FS_ProgressInfo pi;
 
   pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
 {                        
   struct GNUNET_FS_ProgressInfo pi;
 
   pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
-  make_search_status (&pi, sc);
   pi.value.search.specifics.update.cctx = sr->client_info;
   pi.value.search.specifics.update.meta = sr->meta;
   pi.value.search.specifics.update.uri = sr->uri;
   pi.value.search.specifics.update.cctx = sr->client_info;
   pi.value.search.specifics.update.meta = sr->meta;
   pi.value.search.specifics.update.uri = sr->uri;
@@ -137,8 +135,7 @@ notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
     = sr->availability_trials;
   pi.value.search.specifics.update.applicability_rank 
     = sr->optional_support;
     = sr->availability_trials;
   pi.value.search.specifics.update.applicability_rank 
     = sr->optional_support;
-  sr->client_info = sc->h->upcb (sc->h->upcb_cls,
-                                &pi);
+  sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
 }
 
 
 }
 
 
@@ -156,17 +153,17 @@ struct GetResultContext
    * Where to store a pointer to the search
    * result struct if we found a match.
    */
    * Where to store a pointer to the search
    * result struct if we found a match.
    */
-  struct SearchResult *sr;
+  struct GNUNET_FS_SearchResult *sr;
 };
 
 
 /**
 };
 
 
 /**
- * Check if the given result is identical
- * to the given URI and if so return it.
+ * Check if the given result is identical to the given URI and if so
+ * return it.
  * 
  * @param cls a "struct GetResultContext"
  * @param key not used
  * 
  * @param cls a "struct GetResultContext"
  * @param key not used
- * @param value a "struct SearchResult" who's URI we
+ * @param value a "struct GNUNET_FS_SearchResult" who's URI we
  *        should compare with
  * @return GNUNET_OK
  */
  *        should compare with
  * @return GNUNET_OK
  */
@@ -176,7 +173,7 @@ get_result_present (void *cls,
                     void *value)
 {
   struct GetResultContext *grc = cls;
                     void *value)
 {
   struct GetResultContext *grc = cls;
-  struct SearchResult *sr = value;
+  struct GNUNET_FS_SearchResult *sr = value;
 
   if (GNUNET_FS_uri_test_equal (grc->uri,
                                sr->uri))
 
   if (GNUNET_FS_uri_test_equal (grc->uri,
                                sr->uri))
@@ -186,9 +183,209 @@ get_result_present (void *cls,
 
 
 /**
 
 
 /**
- * We have received a KSK result.  Check
- * how it fits in with the overall query
- * and notify the client accordingly.
+ * Signal result of last probe to client and then schedule next
+ * probe.
+ */
+static void
+signal_probe_result (struct GNUNET_FS_SearchResult *sr)
+{
+  struct GNUNET_FS_ProgressInfo pi;
+
+  pi.status = GNUNET_FS_STATUS_SEARCH_START;
+  pi.value.search.specifics.update.cctx = sr->client_info;
+  pi.value.search.specifics.update.meta = sr->meta;
+  pi.value.search.specifics.update.uri = sr->uri;
+  pi.value.search.specifics.update.availability_rank = sr->availability_success;
+  pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
+  pi.value.search.specifics.update.applicability_rank = sr->optional_support;
+  sr->sc->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
+  GNUNET_FS_search_start_probe_ (sr);
+}
+
+
+/**
+ * Handle the case where we have failed to receive a response for our probe.
+ *
+ * @param cls our 'struct GNUNET_FS_SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_failure_handler (void *cls,
+                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_FS_SearchResult *sr = cls;
+  sr->availability_trials++;
+  GNUNET_FS_search_result_sync_ (sr);
+  signal_probe_result (sr);
+}
+
+
+/**
+ * Handle the case where we have gotten a response for our probe.
+ *
+ * @param cls our 'struct GNUNET_FS_SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_success_handler (void *cls,
+                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct GNUNET_FS_SearchResult *sr = cls;
+  sr->availability_trials++;
+  sr->availability_success++;
+  GNUNET_FS_search_result_sync_ (sr);
+  signal_probe_result (sr);
+}
+
+
+/**
+ * Notification of FS that a search probe has made progress.
+ * This function is used INSTEAD of the client's event handler
+ * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
+ *
+ * @param cls closure, always NULL (!), actual closure
+ *        is in the client-context of the info struct
+ * @param info details about the event, specifying the event type
+ *        and various bits about the event
+ * @return client-context (for the next progress call
+ *         for this operation; should be set to NULL for
+ *         SUSPEND and STOPPED events).  The value returned
+ *         will be passed to future callbacks in the respective
+ *         field in the GNUNET_FS_ProgressInfo struct.
+ */
+void*
+GNUNET_FS_search_probe_progress_ (void *cls,
+                                 const struct GNUNET_FS_ProgressInfo *info)
+{
+  struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
+  struct GNUNET_TIME_Relative dur;
+
+  switch (info->status)
+    {
+    case GNUNET_FS_STATUS_DOWNLOAD_START:
+      /* ignore */
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
+      /* probes should never be resumed */
+      GNUNET_assert (0);
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
+      /* probes should never be suspended */
+      GNUNET_break (0);
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
+      /* ignore */
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
+      if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+                                  sr->probe_cancel_task);
+         sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+       }     
+      sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+                                                           sr->remaining_probe_time,
+                                                           &probe_failure_handler,
+                                                           sr);
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
+      if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+                                  sr->probe_cancel_task);
+         sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+       }     
+      sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+                                                           sr->remaining_probe_time,
+                                                           &probe_success_handler,
+                                                           sr);
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
+      if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+                                  sr->probe_cancel_task);
+         sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+       }     
+      sr = NULL;
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
+      GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
+      sr->probe_active_time = GNUNET_TIME_absolute_get ();
+      sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+                                                           sr->remaining_probe_time,
+                                                           &probe_failure_handler,
+                                                           sr);
+      break;
+    case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
+      if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+       {
+         GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+                                  sr->probe_cancel_task);
+         sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+       }
+      dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
+      sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
+                                                               dur);
+      GNUNET_FS_search_result_sync_ (sr);
+      break;
+    default:
+      GNUNET_break (0);
+      return NULL;
+    }
+  return sr;
+}
+
+
+/**
+ * Start download probes for the given search result.
+ *
+ * @param sr the search result
+ */
+void
+GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
+{
+  uint64_t off;
+  uint64_t len;
+  
+  if (sr->probe_ctx != NULL)
+    return;
+  if (sr->download != NULL)
+    return;
+  if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
+    return;
+  if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
+    return;
+  len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
+  if (len == 0)
+    return;
+  if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
+    return;
+  off = len / DBLOCK_SIZE;
+  if (off > 0)
+    off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
+  off *= DBLOCK_SIZE;
+  if (len - off < DBLOCK_SIZE)
+    len = len - off;
+  else
+    len = DBLOCK_SIZE;
+  sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
+                                                           2 * (1 + sr->availability_trials));
+  sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
+                                           sr->uri,
+                                           sr->meta,
+                                           NULL, NULL,
+                                           off, len, 
+                                           sr->sc->anonymity,
+                                           GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
+                                           GNUNET_FS_DOWNLOAD_IS_PROBE,
+                                           sr, NULL);    
+}
+
+
+/**
+ * We have received a KSK result.  Check how it fits in with the
+ * overall query and notify the client accordingly.
  *
  * @param sc context for the overall query
  * @param ent entry for the specific keyword
  *
  * @param sc context for the overall query
  * @param ent entry for the specific keyword
@@ -203,19 +400,12 @@ process_ksk_result (struct GNUNET_FS_SearchContext *sc,
                    const struct GNUNET_CONTAINER_MetaData *meta)
 {
   GNUNET_HashCode key;
                    const struct GNUNET_CONTAINER_MetaData *meta)
 {
   GNUNET_HashCode key;
-  struct SearchResult *sr;
+  struct GNUNET_FS_SearchResult *sr;
   struct GetResultContext grc;
   int is_new;
 
   /* check if new */
   struct GetResultContext grc;
   int is_new;
 
   /* check if new */
-  if (! GNUNET_FS_uri_test_chk (uri))
-    {
-      GNUNET_break_op (0);
-      return;
-    }
-  GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
-                         &uri->data.chk.chk.query,
-                         &key);
+  GNUNET_FS_uri_to_key (uri, &key);
   if (GNUNET_SYSERR ==
       GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
                                                  &key,
   if (GNUNET_SYSERR ==
       GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
                                                  &key,
@@ -233,10 +423,12 @@ process_ksk_result (struct GNUNET_FS_SearchContext *sc,
   is_new = (NULL == sr) || (sr->mandatory_missing > 0);
   if (NULL == sr)
     {
   is_new = (NULL == sr) || (sr->mandatory_missing > 0);
   if (NULL == sr)
     {
-      sr = GNUNET_malloc (sizeof (struct SearchResult));
+      sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
+      sr->sc = sc;
       sr->uri = GNUNET_FS_uri_dup (uri);
       sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
       sr->mandatory_missing = sc->mandatory_count;
       sr->uri = GNUNET_FS_uri_dup (uri);
       sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
       sr->mandatory_missing = sc->mandatory_count;
+      sr->key = key;
       GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
                                         &key,
                                         sr,
       GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
                                         &key,
                                         sr,
@@ -244,7 +436,7 @@ process_ksk_result (struct GNUNET_FS_SearchContext *sc,
     }
   else
     {
     }
   else
     {
-      /* FIXME: consider combining the meta data */
+      GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
     }
   /* check if mandatory satisfied */
   if (ent->mandatory)
     }
   /* check if mandatory satisfied */
   if (ent->mandatory)
@@ -257,7 +449,8 @@ process_ksk_result (struct GNUNET_FS_SearchContext *sc,
     notify_client_chk_result (sc, sr);
   else
     notify_client_chk_update (sc, sr);
     notify_client_chk_result (sc, sr);
   else
     notify_client_chk_update (sc, sr);
-  /* FIXME: consider starting probes for "sr" */
+  GNUNET_FS_search_result_sync_ (sr);
+  GNUNET_FS_search_start_probe_ (sr);
 }
 
 
 }
 
 
@@ -268,22 +461,23 @@ process_ksk_result (struct GNUNET_FS_SearchContext *sc,
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
+ * @param options options for the search
  * @param cctx client context
  * @param cctx client context
- * @param parent parent search (for namespace update searches)
+ * @param psearch parent search result (for namespace update searches)
  * @return context that can be used to control the search
  */
 static struct GNUNET_FS_SearchContext *
 search_start (struct GNUNET_FS_Handle *h,
              const struct GNUNET_FS_Uri *uri,
              uint32_t anonymity,
  * @return context that can be used to control the search
  */
 static struct GNUNET_FS_SearchContext *
 search_start (struct GNUNET_FS_Handle *h,
              const struct GNUNET_FS_Uri *uri,
              uint32_t anonymity,
+             enum GNUNET_FS_SearchOptions options,
              void *cctx,
              void *cctx,
-             struct GNUNET_FS_SearchContext *parent);
+             struct GNUNET_FS_SearchResult *psearch);
 
 
 /**
 
 
 /**
- * We have received an SKS result.  Start
- * searching for updates and notify the
- * client if it is a new result.
+ * We have received an SKS result.  Start searching for updates and
+ * notify the client if it is a new result.
  *
  * @param sc context for the overall query
  * @param id_update identifier for updates, NULL for none
  *
  * @param sc context for the overall query
  * @param id_update identifier for updates, NULL for none
@@ -298,14 +492,10 @@ process_sks_result (struct GNUNET_FS_SearchContext *sc,
 {
   struct GNUNET_FS_Uri uu;
   GNUNET_HashCode key;
 {
   struct GNUNET_FS_Uri uu;
   GNUNET_HashCode key;
-  struct SearchResult *sr;
+  struct GNUNET_FS_SearchResult *sr;
 
   /* check if new */
 
   /* check if new */
-  if (! GNUNET_FS_uri_test_ksk (uri))
-    {
-      GNUNET_break_op (0);
-      return;
-    }
+  GNUNET_FS_uri_to_key (uri, &key);
   GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
                          &uri->data.chk.chk.query,
                          &key);
   GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
                          &uri->data.chk.chk.query,
                          &key);
@@ -315,15 +505,17 @@ process_sks_result (struct GNUNET_FS_SearchContext *sc,
                                                  &test_result_present,
                                                  (void*) uri))
     return; /* duplicate result */
                                                  &test_result_present,
                                                  (void*) uri))
     return; /* duplicate result */
-  sr = GNUNET_malloc (sizeof (struct SearchResult));
+  sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
+  sr->sc = sc;
   sr->uri = GNUNET_FS_uri_dup (uri);
   sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
   sr->uri = GNUNET_FS_uri_dup (uri);
   sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
+  sr->key = key;
   GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
                                     &key,
                                     sr,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
   GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
                                     &key,
                                     sr,
                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
-  /* FIXME: consider starting probes for "sr" */
-
+  GNUNET_FS_search_result_sync_ (sr);
+  GNUNET_FS_search_start_probe_ (sr);
   /* notify client */
   notify_client_chk_result (sc, sr);
   /* search for updates */
   /* notify client */
   notify_client_chk_result (sc, sr);
   /* search for updates */
@@ -332,14 +524,13 @@ process_sks_result (struct GNUNET_FS_SearchContext *sc,
   uu.type = sks;
   uu.data.sks.namespace = sc->uri->data.sks.namespace;
   uu.data.sks.identifier = GNUNET_strdup (id_update);
   uu.type = sks;
   uu.data.sks.namespace = sc->uri->data.sks.namespace;
   uu.data.sks.identifier = GNUNET_strdup (id_update);
-  /* FIXME: should attach update search
-     to the individual result, not
-     the entire SKS search! */
-  search_start (sc->h,
-               &uu,
-               sc->anonymity,
-               NULL,
-               sc);
+  (void) search_start (sc->h,
+                      &uu,
+                      sc->anonymity,
+                      sc->options,
+                      NULL,
+                      sr);
+  GNUNET_free (uu.data.sks.identifier);
 }
 
 
 }
 
 
@@ -383,11 +574,15 @@ process_kblock (struct GNUNET_FS_SearchContext *sc,
     }
   /* decrypt */
   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
     }
   /* decrypt */
   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
-  GNUNET_CRYPTO_aes_decrypt (&kb[1],
-                            size - sizeof (struct KBlock),
-                            &skey,
-                            &iv,
-                            pt);
+  if (-1 == GNUNET_CRYPTO_aes_decrypt (&kb[1],
+                                      size - sizeof (struct KBlock),
+                                      &skey,
+                                      &iv,
+                                      pt))
+    {
+      GNUNET_break (0);
+      return;
+    }
   /* parse */
   eos = memchr (pt, 0, sizeof (pt));
   if (NULL == eos)
   /* parse */
   eos = memchr (pt, 0, sizeof (pt));
   if (NULL == eos)
@@ -396,8 +591,11 @@ process_kblock (struct GNUNET_FS_SearchContext *sc,
       return;
     }
   j = eos - pt + 1;
       return;
     }
   j = eos - pt + 1;
-  meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
-                                                sizeof (pt) - j);
+  if (sizeof (pt) == j)
+    meta = GNUNET_CONTAINER_meta_data_create ();
+  else
+    meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
+                                                  sizeof (pt) - j);
   if (meta == NULL)
     {
       GNUNET_break_op (0);       /* kblock malformed */
   if (meta == NULL)
     {
       GNUNET_break_op (0);       /* kblock malformed */
@@ -420,6 +618,101 @@ process_kblock (struct GNUNET_FS_SearchContext *sc,
 }
 
 
 }
 
 
+/**
+ * Process a keyword-search result with a namespace advertisment.
+ *
+ * @param sc our search context
+ * @param nb the nblock
+ * @param size size of nb
+ */
+static void
+process_nblock (struct GNUNET_FS_SearchContext *sc,
+               const struct NBlock *nb,
+               size_t size)
+{
+  unsigned int i;
+  size_t j;
+  GNUNET_HashCode q;
+  char pt[size - sizeof (struct NBlock)];
+  struct GNUNET_CRYPTO_AesSessionKey skey;
+  struct GNUNET_CRYPTO_AesInitializationVector iv;
+  const char *eos;
+  struct GNUNET_CONTAINER_MetaData *meta;
+  struct GNUNET_FS_Uri *uri;
+  char *uris;
+  
+  GNUNET_CRYPTO_hash (&nb->keyspace,
+                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &q);
+  /* find key */
+  for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
+    if (0 == memcmp (&q,
+                    &sc->requests[i].query,
+                    sizeof (GNUNET_HashCode)))
+      break;
+  if (i == sc->uri->data.ksk.keywordCount)
+    {
+      /* oops, does not match any of our keywords!? */
+      GNUNET_break (0);
+      return;
+    }
+  /* decrypt */
+  GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
+  if (-1 == GNUNET_CRYPTO_aes_decrypt (&nb[1],
+                                      size - sizeof (struct NBlock),
+                                      &skey,
+                                      &iv,
+                                      pt))
+    {
+      GNUNET_break (0);
+      return;
+    }
+  /* parse */
+  eos = memchr (pt, 0, sizeof (pt));
+  if (NULL == eos)
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+  j = eos - pt + 1;
+  if (sizeof (pt) == j)
+    meta = GNUNET_CONTAINER_meta_data_create ();
+  else
+    meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
+                                                  sizeof (pt) - j);
+  if (meta == NULL)
+    {
+      GNUNET_break_op (0);       /* nblock malformed */
+      return;
+    }
+
+  uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
+  uri->type = sks;
+  uri->data.sks.identifier = GNUNET_strdup (pt);
+  GNUNET_CRYPTO_hash (&nb->subspace,
+                     sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+                     &uri->data.sks.namespace);
+  uris = GNUNET_FS_uri_to_string (uri);
+  GNUNET_CONTAINER_meta_data_insert (meta,
+                                    "<gnunet>",
+                                    EXTRACTOR_METATYPE_URI,
+                                    EXTRACTOR_METAFORMAT_UTF8,
+                                    "text/plain",
+                                    uris,
+                                    strlen (uris)+1);
+  GNUNET_free (uris);
+  GNUNET_PSEUDONYM_add (sc->h->cfg,
+                       &uri->data.sks.namespace,
+                       meta);
+  /* process */
+  process_ksk_result (sc, &sc->requests[i], uri, meta);
+
+  /* clean up */
+  GNUNET_CONTAINER_meta_data_destroy (meta);
+  GNUNET_FS_uri_destroy (uri);
+}
+
+
 /**
  * Process a namespace-search result.
  *
 /**
  * Process a namespace-search result.
  *
@@ -451,11 +744,15 @@ process_sblock (struct GNUNET_FS_SearchContext *sc,
                      strlen (identifier), 
                      &key);
   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
                      strlen (identifier), 
                      &key);
   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
-  GNUNET_CRYPTO_aes_encrypt (&sb[1],
-                            len,
-                            &skey,
-                            &iv,
-                            pt);
+  if (-1 == GNUNET_CRYPTO_aes_decrypt (&sb[1],
+                                      len,
+                                      &skey,
+                                      &iv,
+                                      pt))
+    {
+      GNUNET_break (0);
+      return;
+    }
   /* parse */
   off = GNUNET_STRINGS_buffer_tokenize (pt,
                                        len, 
   /* parse */
   off = GNUNET_STRINGS_buffer_tokenize (pt,
                                        len, 
@@ -477,6 +774,9 @@ process_sblock (struct GNUNET_FS_SearchContext *sc,
   uri = GNUNET_FS_uri_parse (uris, &emsg);
   if (uri == NULL)
     {
   uri = GNUNET_FS_uri_parse (uris, &emsg);
   if (uri == NULL)
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "Failed to parse URI `%s': %s\n",
+                 uris, emsg);
       GNUNET_break_op (0);     /* sblock malformed */
       GNUNET_free_non_null (emsg);
       GNUNET_CONTAINER_meta_data_destroy (meta);
       GNUNET_break_op (0);     /* sblock malformed */
       GNUNET_free_non_null (emsg);
       GNUNET_CONTAINER_meta_data_destroy (meta);
@@ -501,16 +801,20 @@ process_sblock (struct GNUNET_FS_SearchContext *sc,
  */
 static void
 process_result (struct GNUNET_FS_SearchContext *sc,
  */
 static void
 process_result (struct GNUNET_FS_SearchContext *sc,
-               uint32_t type,
+               enum GNUNET_BLOCK_Type type,
                struct GNUNET_TIME_Absolute expiration,
                const void *data,
                size_t size)
 {
                struct GNUNET_TIME_Absolute expiration,
                const void *data,
                size_t size)
 {
-  if (GNUNET_TIME_absolute_get_duration (expiration).value > 0)
-    return; /* result expired */
+  if (GNUNET_TIME_absolute_get_duration (expiration).rel_value > 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Result received has already expired.\n");
+      return; /* result expired */
+    }
   switch (type)
     {
   switch (type)
     {
-    case GNUNET_DATASTORE_BLOCKTYPE_KBLOCK:
+    case GNUNET_BLOCK_TYPE_FS_KBLOCK:
       if (! GNUNET_FS_uri_test_ksk (sc->uri))
        {
          GNUNET_break (0);
       if (! GNUNET_FS_uri_test_ksk (sc->uri))
        {
          GNUNET_break (0);
@@ -523,8 +827,8 @@ process_result (struct GNUNET_FS_SearchContext *sc,
        }
       process_kblock (sc, data, size);
       break;
        }
       process_kblock (sc, data, size);
       break;
-    case GNUNET_DATASTORE_BLOCKTYPE_SBLOCK:
-      if (! GNUNET_FS_uri_test_ksk (sc->uri))
+    case GNUNET_BLOCK_TYPE_FS_SBLOCK:
+      if (! GNUNET_FS_uri_test_sks (sc->uri))
        {
          GNUNET_break (0);
          return;
        {
          GNUNET_break (0);
          return;
@@ -536,10 +840,23 @@ process_result (struct GNUNET_FS_SearchContext *sc,
        }
       process_sblock (sc, data, size);
       break;
        }
       process_sblock (sc, data, size);
       break;
-    case GNUNET_DATASTORE_BLOCKTYPE_ANY:
-    case GNUNET_DATASTORE_BLOCKTYPE_DBLOCK:
-    case GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND:
-    case GNUNET_DATASTORE_BLOCKTYPE_IBLOCK:
+    case GNUNET_BLOCK_TYPE_FS_NBLOCK:
+      if (! GNUNET_FS_uri_test_ksk (sc->uri))
+       {
+         GNUNET_break (0);
+         return;
+       }
+      if (sizeof (struct NBlock) > size)
+       {
+         GNUNET_break_op (0);
+         return;
+       }
+      process_nblock (sc, data, size);
+      break;
+    case GNUNET_BLOCK_TYPE_ANY:
+    case GNUNET_BLOCK_TYPE_FS_DBLOCK:
+    case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
+    case GNUNET_BLOCK_TYPE_FS_IBLOCK:
       GNUNET_break (0);
       break;
     default:
       GNUNET_break (0);
       break;
     default:
@@ -585,6 +902,9 @@ receive_results (void *cls,
       return;
     }
   msize = ntohs (msg->size);
       return;
     }
   msize = ntohs (msg->size);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Receiving %u bytes of result from fs service\n",
+             msize);
   cm = (const struct PutMessage*) msg;
   process_result (sc, 
                  ntohl (cm->type),
   cm = (const struct PutMessage*) msg;
   process_result (sc, 
                  ntohl (cm->type),
@@ -618,6 +938,7 @@ transmit_search_request (void *cls,
   struct SearchMessage *sm;
   unsigned int i;
   const char *identifier;
   struct SearchMessage *sm;
   unsigned int i;
   const char *identifier;
+  GNUNET_HashCode key;
   GNUNET_HashCode idh;
 
   if (NULL == buf)
   GNUNET_HashCode idh;
 
   if (NULL == buf)
@@ -635,28 +956,48 @@ transmit_search_request (void *cls,
        {
          sm[i].header.size = htons (sizeof (struct SearchMessage));
          sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
        {
          sm[i].header.size = htons (sizeof (struct SearchMessage));
          sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
+         if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
+           sm[i].options = htonl (1);
+         else
+           sm[i].options = htonl (0);            
+         sm[i].type = htonl (GNUNET_BLOCK_TYPE_ANY);
          sm[i].anonymity_level = htonl (sc->anonymity);
          sm[i].query = sc->requests[i].query;
          sm[i].anonymity_level = htonl (sc->anonymity);
          sm[i].query = sc->requests[i].query;
+         /* FIXME: should transmit hash codes of all already-known results here! 
+            (and if they do not fit, add another message with the same 
+            header and additional already-seen results!) */
        }
     }
   else
     {
        }
     }
   else
     {
+      GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
       msize = sizeof (struct SearchMessage);
       GNUNET_assert (size >= msize);
       sm = buf;
       memset (sm, 0, msize);
       sm->header.size = htons (sizeof (struct SearchMessage));
       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
       msize = sizeof (struct SearchMessage);
       GNUNET_assert (size >= msize);
       sm = buf;
       memset (sm, 0, msize);
       sm->header.size = htons (sizeof (struct SearchMessage));
       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
+      if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
+       sm->options = htonl (1);
+      else
+       sm->options = htonl (0);      
+      sm->type = htonl (GNUNET_BLOCK_TYPE_FS_SBLOCK);
       sm->anonymity_level = htonl (sc->anonymity);
       sm->target = sc->uri->data.sks.namespace;
       identifier = sc->uri->data.sks.identifier;
       GNUNET_CRYPTO_hash (identifier,
                          strlen (identifier),
       sm->anonymity_level = htonl (sc->anonymity);
       sm->target = sc->uri->data.sks.namespace;
       identifier = sc->uri->data.sks.identifier;
       GNUNET_CRYPTO_hash (identifier,
                          strlen (identifier),
+                         &key);
+      GNUNET_CRYPTO_hash (&key,
+                         sizeof (GNUNET_HashCode),
                          &idh);
       GNUNET_CRYPTO_hash_xor (&idh,
                              &sm->target,
                              &sm->query);
                          &idh);
       GNUNET_CRYPTO_hash_xor (&idh,
                              &sm->target,
                              &sm->query);
-    }
+      /* FIXME: should transmit hash codes of all already-known results here!
+        (and if they do not fit, add another message with the same 
+        header and additional already-seen results!) */      
+   }
   GNUNET_CLIENT_receive (sc->client,
                         &receive_results,
                         sc,
   GNUNET_CLIENT_receive (sc->client,
                         &receive_results,
                         sc,
@@ -715,7 +1056,7 @@ try_reconnect (struct GNUNET_FS_SearchContext *sc)
 {
   if (NULL != sc->client)
     {
 {
   if (NULL != sc->client)
     {
-      GNUNET_CLIENT_disconnect (sc->client);
+      GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
       sc->client = NULL;
     }
   sc->task
       sc->client = NULL;
     }
   sc->task
@@ -733,93 +1074,257 @@ try_reconnect (struct GNUNET_FS_SearchContext *sc)
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
+ * @param options options for the search
  * @param cctx initial value for the client context
  * @param cctx initial value for the client context
- * @param parent parent search (for namespace update searches)
+ * @param psearch parent search result (for namespace update searches)
  * @return context that can be used to control the search
  */
 static struct GNUNET_FS_SearchContext *
 search_start (struct GNUNET_FS_Handle *h,
              const struct GNUNET_FS_Uri *uri,
              uint32_t anonymity,
  * @return context that can be used to control the search
  */
 static struct GNUNET_FS_SearchContext *
 search_start (struct GNUNET_FS_Handle *h,
              const struct GNUNET_FS_Uri *uri,
              uint32_t anonymity,
+             enum GNUNET_FS_SearchOptions options,
              void *cctx,
              void *cctx,
-             struct GNUNET_FS_SearchContext *parent)
+             struct GNUNET_FS_SearchResult *psearch)
 {
   struct GNUNET_FS_SearchContext *sc;
 {
   struct GNUNET_FS_SearchContext *sc;
-  struct GNUNET_CLIENT_Connection *client;
   struct GNUNET_FS_ProgressInfo pi;
   struct GNUNET_FS_ProgressInfo pi;
-  size_t size;
+
+  sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
+  sc->h = h;
+  sc->options = options;
+  sc->uri = GNUNET_FS_uri_dup (uri);
+  sc->anonymity = anonymity;
+  sc->start_time = GNUNET_TIME_absolute_get ();
+  if (psearch != NULL)
+    {
+      sc->psearch_result = psearch;  
+      psearch->update_search = sc;
+    }
+  sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
+  sc->client_info = cctx;
+  if (GNUNET_OK !=
+      GNUNET_FS_search_start_searching_ (sc))
+    {
+      GNUNET_FS_uri_destroy (sc->uri);
+      GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
+      GNUNET_free (sc);      
+      return NULL;
+    }
+  GNUNET_FS_search_sync_ (sc);
+  pi.status = GNUNET_FS_STATUS_SEARCH_START;
+  sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
+  return sc;
+}
+
+
+/**
+ * Build the request and actually initiate the search using the
+ * GNUnet FS service.
+ *
+ * @param sc search context
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
+{
   unsigned int i;
   const char *keyword;
   GNUNET_HashCode hc;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;  
   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
   unsigned int i;
   const char *keyword;
   GNUNET_HashCode hc;
   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;  
   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
+  size_t size;
 
 
-  if (GNUNET_FS_uri_test_ksk (uri))
+  GNUNET_assert (NULL == sc->client);
+  if (GNUNET_FS_uri_test_ksk (sc->uri))
     {
     {
-      size = sizeof (struct SearchMessage) * uri->data.ksk.keywordCount;
+      size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
     }
   else
     {
     }
   else
     {
-      GNUNET_assert (GNUNET_FS_uri_test_sks (uri));
+      GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
       size = sizeof (struct SearchMessage);
     }
   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Too many keywords specified for a single search."));
       size = sizeof (struct SearchMessage);
     }
   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Too many keywords specified for a single search."));
-      return NULL;
+      return GNUNET_SYSERR;
     }
     }
-  client = GNUNET_CLIENT_connect (h->sched,
-                                 "fs",
-                                 h->cfg);
-  if (NULL == client)
-    return NULL;
-  sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
-  sc->h = h;
-  sc->uri = GNUNET_FS_uri_dup (uri);
-  sc->anonymity = anonymity;
-  sc->start_time = GNUNET_TIME_absolute_get ();
-  sc->client = client;  
-  sc->parent = parent;
-  sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
-  sc->client_info = cctx;
-
-  sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
-                               sc->uri->data.ksk.keywordCount);
-  for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
+  if (GNUNET_FS_uri_test_ksk (sc->uri))
     {
     {
-      keyword = &sc->uri->data.ksk.keywords[i][1];
-      GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
-      pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
-      GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
-      GNUNET_CRYPTO_rsa_key_free (pk);
-      GNUNET_CRYPTO_hash (&pub,
-                         sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
-                         &sc->requests[i].query);
-      sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
-      if (sc->requests[i].mandatory)
-       sc->mandatory_count++;
-      sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
-      GNUNET_CRYPTO_hash (keyword,
-                         strlen (keyword),
-                         &sc->requests[i].key);
-    }
-  if (NULL != parent)
-    {
-      // FIXME: need to track children
-      // in parent in case parent is stopped!
+      GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
+      sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
+                                   sc->uri->data.ksk.keywordCount);
+      for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
+       {
+         keyword = &sc->uri->data.ksk.keywords[i][1];
+         GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
+         pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
+         GNUNET_assert (pk != NULL);
+         GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
+         GNUNET_CRYPTO_rsa_key_free (pk);
+         GNUNET_CRYPTO_hash (&pub,
+                             sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
+                             &sc->requests[i].query);
+         sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
+         if (sc->requests[i].mandatory)
+           sc->mandatory_count++;
+         sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
+         GNUNET_CRYPTO_hash (keyword,
+                             strlen (keyword),
+                             &sc->requests[i].key);
+       }
     }
     }
-  pi.status = GNUNET_FS_STATUS_SEARCH_START;
-  make_search_status (&pi, sc);
-  sc->client_info = h->upcb (h->upcb_cls,
-                            &pi);
-  GNUNET_CLIENT_notify_transmit_ready (client,
+  sc->client = GNUNET_CLIENT_connect (sc->h->sched,
+                                     "fs",
+                                     sc->h->cfg);
+  if (NULL == sc->client)
+    return GNUNET_SYSERR;
+  GNUNET_CLIENT_notify_transmit_ready (sc->client,
                                       size,
                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
                                       GNUNET_NO,
                                       &transmit_search_request,
                                       size,
                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
                                       GNUNET_NO,
                                       &transmit_search_request,
-                                      sc);  
-  return sc;
+                                      sc);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Freeze probes for the given search result.
+ *
+ * @param cls the global FS handle
+ * @param key the key for the search result (unused)
+ * @param value the search result to free
+ * @return GNUNET_OK
+ */
+static int
+search_result_freeze_probes (void *cls,
+                            const GNUNET_HashCode * key,
+                            void *value)
+{
+  struct GNUNET_FS_SearchContext *sc = cls;
+  struct GNUNET_FS_Handle *h = sc->h;
+  struct GNUNET_FS_SearchResult *sr = value;
+
+  if (sr->probe_ctx != NULL)
+    {
+      GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
+      sr->probe_ctx = NULL;
+    }
+  if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      GNUNET_SCHEDULER_cancel (h->sched,
+                              sr->probe_cancel_task);  
+      sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+  if (sr->update_search != NULL)
+    GNUNET_FS_search_pause (sr->update_search);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Resume probes for the given search result.
+ *
+ * @param cls the global FS handle
+ * @param key the key for the search result (unused)
+ * @param value the search result to free
+ * @return GNUNET_OK
+ */
+static int
+search_result_resume_probes (void *cls,
+                            const GNUNET_HashCode * key,
+                            void *value)
+{
+  struct GNUNET_FS_SearchResult *sr = value;
+
+  GNUNET_FS_search_start_probe_ (sr);
+  if (sr->update_search != NULL)
+    GNUNET_FS_search_continue (sr->update_search);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Signal suspend and free the given search result.
+ *
+ * @param cls the global FS handle
+ * @param key the key for the search result (unused)
+ * @param value the search result to free
+ * @return GNUNET_OK
+ */
+static int
+search_result_suspend (void *cls,
+                      const GNUNET_HashCode * key,
+                      void *value)
+{
+  struct GNUNET_FS_SearchContext *sc = cls;
+  struct GNUNET_FS_Handle *h = sc->h;
+  struct GNUNET_FS_SearchResult *sr = value;
+  struct GNUNET_FS_ProgressInfo pi;
+
+  if (sr->download != NULL)
+    GNUNET_FS_download_signal_suspend_ (sr->download);
+  if (sr->update_search != NULL)
+    GNUNET_FS_search_signal_suspend_ (sr->update_search);
+  pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
+  pi.value.search.specifics.result_suspend.cctx = sr->client_info;
+  pi.value.search.specifics.result_suspend.meta = sr->meta;
+  pi.value.search.specifics.result_suspend.uri = sr->uri;
+  sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
+  GNUNET_break (NULL == sr->client_info);
+  GNUNET_free_non_null (sr->serialization);
+  GNUNET_FS_uri_destroy (sr->uri);
+  GNUNET_CONTAINER_meta_data_destroy (sr->meta);
+  if (sr->probe_ctx != NULL)
+    GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
+  if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (h->sched,
+                            sr->probe_cancel_task);    
+  GNUNET_free (sr);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Create SUSPEND event for the given search operation
+ * and then clean up our state (without stop signal).
+ *
+ * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
+ */
+void
+GNUNET_FS_search_signal_suspend_ (void *cls)
+{
+  struct GNUNET_FS_SearchContext *sc = cls;
+  struct GNUNET_FS_ProgressInfo pi;
+  unsigned int i;
+
+  GNUNET_FS_end_top (sc->h, sc->top);
+  GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
+                                        &search_result_suspend,
+                                        sc);
+  pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
+  sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
+  GNUNET_break (NULL == sc->client_info);
+  if (sc->task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (sc->h->sched,
+                            sc->task);
+  if (NULL != sc->client)
+    GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
+  GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
+  if (sc->requests != NULL)
+    {
+      GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
+      for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
+       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
+    }
+  GNUNET_free_non_null (sc->requests);
+  GNUNET_free_non_null (sc->emsg);
+  GNUNET_FS_uri_destroy (sc->uri);
+  GNUNET_free_non_null (sc->serialization);
+  GNUNET_free (sc);
 }
 
 
 }
 
 
@@ -830,6 +1335,7 @@ search_start (struct GNUNET_FS_Handle *h,
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
  * @param uri specifies the search parameters; can be
  *        a KSK URI or an SKS URI.
  * @param anonymity desired level of anonymity
+ * @param options options for the search
  * @param cctx initial value for the client context
  * @return context that can be used to control the search
  */
  * @param cctx initial value for the client context
  * @return context that can be used to control the search
  */
@@ -837,9 +1343,15 @@ struct GNUNET_FS_SearchContext *
 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
                        const struct GNUNET_FS_Uri *uri,
                        uint32_t anonymity,
 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
                        const struct GNUNET_FS_Uri *uri,
                        uint32_t anonymity,
+                       enum GNUNET_FS_SearchOptions options,
                        void *cctx)
 {
                        void *cctx)
 {
-  return search_start (h, uri, anonymity, cctx, NULL);
+  struct GNUNET_FS_SearchContext *ret;
+  ret = search_start (h, uri, anonymity, options, cctx, NULL);
+  if (ret == NULL)
+    return NULL;
+  ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
+  return ret;
 }
 
 
 }
 
 
@@ -858,14 +1370,14 @@ GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
                             sc->task);
   sc->task = GNUNET_SCHEDULER_NO_TASK;
   if (NULL != sc->client)
                             sc->task);
   sc->task = GNUNET_SCHEDULER_NO_TASK;
   if (NULL != sc->client)
-    GNUNET_CLIENT_disconnect (sc->client);
+    GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
   sc->client = NULL;
   sc->client = NULL;
-  // FIXME: make persistent!
-  // FIXME: should this freeze all active probes?
+  GNUNET_FS_search_sync_ (sc);
+  GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
+                                        &search_result_freeze_probes,
+                                        sc);
   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
-  make_search_status (&pi, sc);
-  sc->client_info = sc->h->upcb (sc->h->upcb_cls,
-                                &pi);
+  sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
 }
 
 
 }
 
 
@@ -882,11 +1394,12 @@ GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
   GNUNET_assert (sc->client == NULL);
   GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
   do_reconnect (sc, NULL);
   GNUNET_assert (sc->client == NULL);
   GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
   do_reconnect (sc, NULL);
-  // FIXME: make persistent!
+  GNUNET_FS_search_sync_ (sc);
   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
-  make_search_status (&pi, sc);
-  sc->client_info = sc->h->upcb (sc->h->upcb_cls,
-                                &pi);
+  sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
+  GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
+                                        &search_result_resume_probes,
+                                        sc);
 }
 
 
 }
 
 
@@ -905,34 +1418,48 @@ search_result_free (void *cls,
 {
   struct GNUNET_FS_SearchContext *sc = cls;
   struct GNUNET_FS_Handle *h = sc->h;
 {
   struct GNUNET_FS_SearchContext *sc = cls;
   struct GNUNET_FS_Handle *h = sc->h;
-  struct SearchResult *sr = value;
+  struct GNUNET_FS_SearchResult *sr = value;
   struct GNUNET_FS_ProgressInfo pi;
 
   struct GNUNET_FS_ProgressInfo pi;
 
+  if (NULL != sr->download)
+    {
+      sr->download->search = NULL;
+      sr->download->top = GNUNET_FS_make_top (sr->download->h,
+                                             &GNUNET_FS_download_signal_suspend_,
+                                             sr->download);
+      if (NULL != sr->download->serialization)
+       {
+         GNUNET_FS_remove_sync_file_ (sc->h,
+                                      GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
+                                      sr->download->serialization);
+         GNUNET_free (sr->download->serialization);
+         sr->download->serialization = NULL;
+       }
+      pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
+      GNUNET_FS_download_make_status_ (&pi,
+                                      sr->download);      
+      GNUNET_FS_download_sync_ (sr->download);
+      sr->download = NULL;     
+    }
+  if (NULL != sr->update_search)
+    {
+      GNUNET_FS_search_stop (sr->update_search);
+      GNUNET_assert (sr->update_search == NULL);
+    }
   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
-  make_search_status (&pi, sc);
   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
   pi.value.search.specifics.result_stopped.meta = sr->meta;
   pi.value.search.specifics.result_stopped.uri = sr->uri;
   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
   pi.value.search.specifics.result_stopped.meta = sr->meta;
   pi.value.search.specifics.result_stopped.uri = sr->uri;
-  sr->client_info = h->upcb (h->upcb_cls,
-                            &pi);
+  sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
   GNUNET_break (NULL == sr->client_info);
   GNUNET_break (NULL == sr->client_info);
-  
+  GNUNET_free_non_null (sr->serialization);
   GNUNET_FS_uri_destroy (sr->uri);
   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
   if (sr->probe_ctx != NULL)
   GNUNET_FS_uri_destroy (sr->uri);
   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
   if (sr->probe_ctx != NULL)
-    {
-      GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
-      h->active_probes--;
-      /* FIXME: trigger starting of new
-        probes here!? Maybe not -- could
-        cause new probes to be immediately
-        stopped again... */
-    }
+    GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (h->sched,
-                              sr->probe_cancel_task);
-    }
+    GNUNET_SCHEDULER_cancel (h->sched,
+                            sr->probe_cancel_task);    
   GNUNET_free (sr);
   return GNUNET_OK;
 }
   GNUNET_free (sr);
   return GNUNET_OK;
 }
@@ -949,29 +1476,44 @@ GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
   struct GNUNET_FS_ProgressInfo pi;
   unsigned int i;
 
   struct GNUNET_FS_ProgressInfo pi;
   unsigned int i;
 
-  // FIXME: make un-persistent!
-  if (NULL != sc->parent)
-    {
-      // FIXME: need to untrack sc
-      // in parent!
-    }
+  if (sc->top != NULL)
+    GNUNET_FS_end_top (sc->h, sc->top);
+  if (sc->psearch_result != NULL)
+    sc->psearch_result->update_search = NULL;
   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
                                         &search_result_free,
                                         sc);
   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
                                         &search_result_free,
                                         sc);
+  if (sc->serialization != NULL)
+    {
+      GNUNET_FS_remove_sync_file_ (sc->h,
+                                  (sc->psearch_result != NULL)  
+                                  ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
+                                  : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
+                                  sc->serialization);
+      GNUNET_FS_remove_sync_dir_ (sc->h,
+                                 (sc->psearch_result != NULL)  
+                                 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
+                                 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
+                                 sc->serialization);
+      GNUNET_free (sc->serialization);
+    }
   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
-  make_search_status (&pi, sc);
-  sc->client_info = sc->h->upcb (sc->h->upcb_cls,
-                                &pi);
+  sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
   GNUNET_break (NULL == sc->client_info);
   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (sc->h->sched,
                             sc->task);
   if (NULL != sc->client)
   GNUNET_break (NULL == sc->client_info);
   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (sc->h->sched,
                             sc->task);
   if (NULL != sc->client)
-    GNUNET_CLIENT_disconnect (sc->client);
+    GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
-  for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
-    GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
+  if (sc->requests != NULL)
+    {
+      GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
+      for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
+       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
+    }
   GNUNET_free_non_null (sc->requests);
   GNUNET_free_non_null (sc->requests);
+  GNUNET_free_non_null (sc->emsg);
   GNUNET_FS_uri_destroy (sc->uri);
   GNUNET_free (sc);
 }
   GNUNET_FS_uri_destroy (sc->uri);
   GNUNET_free (sc);
 }