2 This file is part of GNUnet.
3 Copyright (C) 2001-2014 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @file fs/fs_search.c
20 * @brief Helper functions for searching.
21 * @author Christian Grothoff
24 #include "gnunet_constants.h"
25 #include "gnunet_fs_service.h"
26 #include "gnunet_protocols.h"
28 #include "fs_publish_ublock.h"
32 * Number of availability trials we perform per search result.
34 #define AVAILABILITY_TRIALS_MAX 8
37 * Fill in all of the generic fields for a search event and
40 * @param pi structure to fill in
41 * @param h file-sharing handle
42 * @param sc overall search context
43 * @return value returned by the callback
46 GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
47 struct GNUNET_FS_Handle *h,
48 struct GNUNET_FS_SearchContext *sc)
52 pi->value.search.sc = sc;
53 pi->value.search.cctx = (NULL != sc) ? sc->client_info : NULL;
54 pi->value.search.pctx =
55 ((NULL == sc) || (NULL == sc->psearch_result))
57 : sc->psearch_result->client_info;
58 pi->value.search.query = (NULL != sc) ? sc->uri : NULL;
59 pi->value.search.duration = (NULL != sc)
60 ? GNUNET_TIME_absolute_get_duration (sc->start_time)
61 : GNUNET_TIME_UNIT_ZERO;
62 pi->value.search.anonymity = (NULL != sc) ? sc->anonymity : 0;
64 ret = h->upcb (h->upcb_cls, pi);
70 * Check if the given result is identical to the given URI.
72 * @param cls points to the URI we check against
74 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
76 * @return #GNUNET_SYSERR if the result is present,
77 * #GNUNET_OK otherwise
80 test_result_present (void *cls,
81 const struct GNUNET_HashCode * key,
84 const struct GNUNET_FS_Uri *uri = cls;
85 struct GNUNET_FS_SearchResult *sr = value;
87 if (GNUNET_FS_uri_test_equal (uri, sr->uri))
94 * We've found a new CHK result. Let the client
97 * @param sc the search context
98 * @param sr the specific result
101 notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
102 struct GNUNET_FS_SearchResult *sr)
104 struct GNUNET_FS_ProgressInfo pi;
106 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
107 pi.value.search.specifics.result.meta = sr->meta;
108 pi.value.search.specifics.result.uri = sr->uri;
109 pi.value.search.specifics.result.result = sr;
110 pi.value.search.specifics.result.applicability_rank = sr->optional_support;
111 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
116 * We've found new information about an existing CHK result. Let the
117 * client know about it.
119 * @param sc the search context
120 * @param sr the specific result
123 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
124 struct GNUNET_FS_SearchResult *sr)
126 struct GNUNET_FS_ProgressInfo pi;
128 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
129 pi.value.search.specifics.update.cctx = sr->client_info;
130 pi.value.search.specifics.update.meta = sr->meta;
131 pi.value.search.specifics.update.uri = sr->uri;
132 pi.value.search.specifics.update.availability_rank =
133 2 * sr->availability_success - sr->availability_trials;
134 pi.value.search.specifics.update.availability_certainty =
135 sr->availability_trials;
136 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
137 pi.value.search.specifics.update.current_probe_time
138 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
139 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
144 * Context for "get_result_present".
146 struct GetResultContext
149 * The URI we're looking for.
151 const struct GNUNET_FS_Uri *uri;
154 * Where to store a pointer to the search
155 * result struct if we found a match.
157 struct GNUNET_FS_SearchResult *sr;
162 * Check if the given result is identical to the given URI and if so
165 * @param cls a `struct GetResultContext`
166 * @param key not used
167 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
168 * should compare with
172 get_result_present (void *cls,
173 const struct GNUNET_HashCode *key,
176 struct GetResultContext *grc = cls;
177 struct GNUNET_FS_SearchResult *sr = value;
179 if (GNUNET_FS_uri_test_equal (grc->uri, sr->uri))
186 * Signal result of last probe to client and then schedule next
189 * @param sr search result to signal for
192 signal_probe_result (struct GNUNET_FS_SearchResult *sr)
194 struct GNUNET_FS_ProgressInfo pi;
196 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
197 pi.value.search.specifics.update.cctx = sr->client_info;
198 pi.value.search.specifics.update.meta = sr->meta;
199 pi.value.search.specifics.update.uri = sr->uri;
200 pi.value.search.specifics.update.availability_rank
201 = 2 * sr->availability_success - sr->availability_trials;
202 pi.value.search.specifics.update.availability_certainty
203 = sr->availability_trials;
204 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
205 pi.value.search.specifics.update.current_probe_time
206 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
207 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sr->sc);
208 GNUNET_FS_search_start_probe_ (sr);
213 * Handle the case where we have failed to receive a response for our probe.
215 * @param cls our `struct GNUNET_FS_SearchResult *`
218 probe_failure_handler (void *cls)
220 struct GNUNET_FS_SearchResult *sr = cls;
222 sr->probe_cancel_task = NULL;
223 sr->availability_trials++;
224 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
225 sr->probe_ctx = NULL;
226 GNUNET_FS_stop_probe_ping_task_ (sr);
227 GNUNET_FS_search_result_sync_ (sr);
228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229 "Probe #%u for search result %p failed\n",
230 sr->availability_trials,
232 signal_probe_result (sr);
237 * Handle the case where we have gotten a response for our probe.
239 * @param cls our `struct GNUNET_FS_SearchResult *`
242 probe_success_handler (void *cls)
244 struct GNUNET_FS_SearchResult *sr = cls;
246 sr->probe_cancel_task = NULL;
247 sr->availability_trials++;
248 sr->availability_success++;
249 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
250 sr->probe_ctx = NULL;
251 GNUNET_FS_stop_probe_ping_task_ (sr);
252 GNUNET_FS_search_result_sync_ (sr);
253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
254 "Probe #%u for search result %p succeeded\n",
255 sr->availability_trials,
257 signal_probe_result (sr);
262 * Notification of FS that a search probe has made progress.
263 * This function is used INSTEAD of the client's event handler
264 * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
266 * @param cls closure, always NULL (!), actual closure
267 * is in the client-context of the info struct
268 * @param info details about the event, specifying the event type
269 * and various bits about the event
270 * @return client-context (for the next progress call
271 * for this operation; should be set to NULL for
272 * SUSPEND and STOPPED events). The value returned
273 * will be passed to future callbacks in the respective
274 * field in the `struct GNUNET_FS_ProgressInfo`.
277 GNUNET_FS_search_probe_progress_ (void *cls,
278 const struct GNUNET_FS_ProgressInfo *info)
280 struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
281 struct GNUNET_TIME_Relative dur;
283 switch (info->status)
285 case GNUNET_FS_STATUS_DOWNLOAD_START:
288 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
289 /* probes should never be resumed */
292 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
293 /* probes should never be suspended */
296 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
299 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
300 if (NULL != sr->probe_cancel_task)
302 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
303 sr->probe_cancel_task = NULL;
305 sr->probe_cancel_task =
306 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
307 &probe_failure_handler, sr);
309 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
310 if (NULL != sr->probe_cancel_task)
312 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
313 sr->probe_cancel_task = NULL;
315 sr->probe_cancel_task =
316 GNUNET_SCHEDULER_add_now (&probe_success_handler, sr);
318 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
319 if (NULL != sr->probe_cancel_task)
321 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
322 sr->probe_cancel_task = NULL;
326 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
327 if (NULL == sr->probe_cancel_task)
329 sr->probe_active_time = GNUNET_TIME_absolute_get ();
330 sr->probe_cancel_task =
331 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
332 &probe_failure_handler, sr);
335 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
336 if (NULL != sr->probe_cancel_task)
338 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
339 sr->probe_cancel_task = NULL;
341 dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
342 sr->remaining_probe_time =
343 GNUNET_TIME_relative_subtract (sr->remaining_probe_time, dur);
344 if (0 == sr->remaining_probe_time.rel_value_us)
345 sr->probe_cancel_task =
346 GNUNET_SCHEDULER_add_now (&probe_failure_handler, sr);
347 GNUNET_FS_search_result_sync_ (sr);
358 * Task run periodically to remind clients that a probe is active.
360 * @param cls the `struct GNUNET_FS_SearchResult` that we are probing for
363 probe_ping_task_cb (void *cls)
365 struct GNUNET_FS_Handle *h = cls;
366 struct GNUNET_FS_SearchResult *sr;
368 for (sr = h->probes_head; NULL != sr; sr = sr->next)
369 if (NULL != sr->probe_ctx->mq)
370 signal_probe_result (sr);
372 = GNUNET_SCHEDULER_add_delayed (GNUNET_FS_PROBE_UPDATE_FREQUENCY,
379 * Start the ping task for this search result.
381 * @param sr result to start pinging for.
384 start_probe_ping_task (struct GNUNET_FS_SearchResult *sr)
386 struct GNUNET_FS_Handle *h = sr->h;
388 GNUNET_CONTAINER_DLL_insert (h->probes_head,
391 if (NULL == h->probe_ping_task)
393 = GNUNET_SCHEDULER_add_now (&probe_ping_task_cb,
399 * Stop the ping task for this search result.
401 * @param sr result to start pinging for.
404 GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr)
406 struct GNUNET_FS_Handle *h = sr->h;
408 GNUNET_CONTAINER_DLL_remove (h->probes_head,
411 if (NULL == h->probes_head)
413 GNUNET_SCHEDULER_cancel (h->probe_ping_task);
414 h->probe_ping_task = NULL;
420 * Start download probes for the given search result.
422 * @param sr the search result
425 GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
430 if (NULL != sr->probe_ctx)
432 if (NULL != sr->download)
434 if (0 == (sr->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
436 if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
438 if ( (GNUNET_FS_URI_CHK != sr->uri->type) && (GNUNET_FS_URI_LOC != sr->uri->type))
440 len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
443 if ((len <= DBLOCK_SIZE) && (sr->availability_success > 0))
445 off = len / DBLOCK_SIZE;
447 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
449 if (len - off < DBLOCK_SIZE)
453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454 "Starting probe #%u (at offset %llu) for search result %p\n",
455 sr->availability_trials + 1,
456 (unsigned long long) off,
458 sr->remaining_probe_time =
459 GNUNET_TIME_relative_saturating_multiply (sr->h->avg_block_latency,
460 2 * (1 + sr->availability_trials));
462 GNUNET_FS_download_start (sr->h, sr->uri, sr->meta, NULL, NULL, off,
464 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
465 GNUNET_FS_DOWNLOAD_IS_PROBE, sr, NULL);
466 start_probe_ping_task (sr);
471 * Start download probes for the given search result.
473 * @param h file-sharing handle to use for the operation
474 * @param uri URI to probe
475 * @param meta meta data associated with the URI
476 * @param client_info client info pointer to use for associated events
477 * @param anonymity anonymity level to use for the probes
478 * @return the search result handle to access the probe activity
480 struct GNUNET_FS_SearchResult *
481 GNUNET_FS_probe (struct GNUNET_FS_Handle *h,
482 const struct GNUNET_FS_Uri *uri,
483 const struct GNUNET_CONTAINER_MetaData *meta,
487 struct GNUNET_FS_SearchResult *sr;
489 GNUNET_assert (NULL != h);
490 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
492 sr->uri = GNUNET_FS_uri_dup (uri);
493 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
494 sr->client_info = client_info;
495 sr->anonymity = anonymity;
496 GNUNET_FS_search_start_probe_ (sr);
502 * Stop probing activity associated with a search result.
504 * @param sr search result
507 GNUNET_FS_search_stop_probe_ (struct GNUNET_FS_SearchResult *sr)
509 if (NULL != sr->probe_ctx)
511 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
512 sr->probe_ctx = NULL;
513 GNUNET_FS_stop_probe_ping_task_ (sr);
515 if (NULL != sr->probe_cancel_task)
517 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
518 sr->probe_cancel_task = NULL;
524 * Stop probe activity. Must ONLY be used on values
525 * returned from #GNUNET_FS_probe.
527 * @param sr search result to stop probing for (freed)
528 * @return the value of the 'client_info' pointer
531 GNUNET_FS_probe_stop (struct GNUNET_FS_SearchResult *sr)
535 GNUNET_assert (NULL == sr->sc);
536 GNUNET_FS_search_stop_probe_ (sr);
537 GNUNET_FS_uri_destroy (sr->uri);
538 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
539 client_info = sr->client_info;
546 * We have received a KSK result. Check how it fits in with the
547 * overall query and notify the client accordingly.
549 * @param sc context for the overall query
550 * @param ent entry for the specific keyword
551 * @param uri the URI that was found
552 * @param meta metadata associated with the URI
553 * under the @a ent keyword
556 process_ksk_result (struct GNUNET_FS_SearchContext *sc,
557 struct SearchRequestEntry *ent,
558 const struct GNUNET_FS_Uri *uri,
559 const struct GNUNET_CONTAINER_MetaData *meta)
561 struct GNUNET_HashCode key;
562 struct GNUNET_FS_SearchResult *sr;
563 struct GetResultContext grc;
568 GNUNET_assert (NULL != sc);
570 GNUNET_FS_uri_to_key (uri,
577 GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
579 &test_result_present,
581 return; /* duplicate result */
582 /* try to find search result in master map */
585 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
587 &get_result_present, &grc);
589 is_new = (NULL == sr) || (sr->mandatory_missing > 0);
592 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
595 sr->anonymity = sc->anonymity;
596 sr->uri = GNUNET_FS_uri_dup (uri);
597 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
598 sr->mandatory_missing = sc->mandatory_count;
600 sr->keyword_bitmap = GNUNET_malloc ((sc->uri->data.ksk.keywordCount + 7) / 8); /* round up, count bits */
601 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
602 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
606 GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
608 GNUNET_break (GNUNET_OK ==
609 GNUNET_CONTAINER_multihashmap_put (ent->results,
612 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
614 koff = ent - sc->requests;
615 GNUNET_assert ( (ent >= sc->requests) &&
616 (koff < sc->uri->data.ksk.keywordCount));
617 sr->keyword_bitmap[koff / 8] |= (1 << (koff % 8));
618 /* check if mandatory satisfied */
619 if (1 <= GNUNET_CONTAINER_multihashmap_size (ent->results))
623 GNUNET_break (sr->mandatory_missing > 0);
624 sr->mandatory_missing--;
628 sr->optional_support++;
631 if (0 != sr->mandatory_missing)
633 GNUNET_break (NULL == sr->client_info);
637 notify_client_chk_result (sc, sr);
639 notify_client_chk_update (sc, sr);
640 GNUNET_FS_search_result_sync_ (sr);
641 GNUNET_FS_search_start_probe_ (sr);
646 * Start search for content, internal API.
648 * @param h handle to the file sharing subsystem
649 * @param uri specifies the search parameters; can be
650 * a KSK URI or an SKS URI.
651 * @param anonymity desired level of anonymity
652 * @param options options for the search
653 * @param cctx client context
654 * @param psearch parent search result (for namespace update searches)
655 * @return context that can be used to control the search
657 static struct GNUNET_FS_SearchContext *
658 search_start (struct GNUNET_FS_Handle *h,
659 const struct GNUNET_FS_Uri *uri,
661 enum GNUNET_FS_SearchOptions options,
663 struct GNUNET_FS_SearchResult *psearch);
667 * We have received an SKS result. Start searching for updates and
668 * notify the client if it is a new result.
670 * @param sc context for the overall query
671 * @param id_update identifier for updates, NULL for none
672 * @param uri the URI that was found
673 * @param meta metadata associated with the URI
676 process_sks_result (struct GNUNET_FS_SearchContext *sc,
677 const char *id_update,
678 const struct GNUNET_FS_Uri *uri,
679 const struct GNUNET_CONTAINER_MetaData *meta)
681 struct GNUNET_FS_Uri uu;
682 struct GNUNET_HashCode key;
683 struct GNUNET_FS_SearchResult *sr;
686 GNUNET_assert (NULL != sc);
688 GNUNET_FS_uri_to_key (uri,
694 GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
695 &uri->data.chk.chk.query,
698 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map, &key,
699 &test_result_present,
701 return; /* duplicate result */
702 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
705 sr->anonymity = sc->anonymity;
706 sr->uri = GNUNET_FS_uri_dup (uri);
707 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
709 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
710 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
711 GNUNET_FS_search_result_sync_ (sr);
712 GNUNET_FS_search_start_probe_ (sr);
714 if (0 == sr->mandatory_missing)
715 notify_client_chk_result (sc, sr);
717 GNUNET_break (NULL == sr->client_info);
718 /* search for updates */
719 if (0 == strlen (id_update))
720 return; /* no updates */
721 uu.type = GNUNET_FS_URI_SKS;
722 uu.data.sks.ns = sc->uri->data.sks.ns;
723 uu.data.sks.identifier = GNUNET_strdup (id_update);
724 (void) search_start (sc->h, &uu, sc->anonymity, sc->options, NULL, sr);
725 GNUNET_free (uu.data.sks.identifier);
730 * Decrypt a ublock using a 'keyword' as the passphrase. Given the
731 * KSK public key derived from the keyword, this function looks up
732 * the original keyword in the search context and decrypts the
733 * given ciphertext block.
735 * @param sc search context with the keywords
736 * @param dpub derived public key used for the search
737 * @param edata encrypted data
738 * @param edata_size number of bytes in @a edata (and @a data)
739 * @param data where to store the plaintext
740 * @return keyword index on success, #GNUNET_SYSERR on error (no such
741 * keyword, internal error)
744 decrypt_block_with_keyword (const struct GNUNET_FS_SearchContext *sc,
745 const struct GNUNET_CRYPTO_EcdsaPublicKey *dpub,
750 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
751 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
755 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
756 if (0 == memcmp (dpub,
757 &sc->requests[i].dpub,
758 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
760 if (i == sc->uri->data.ksk.keywordCount)
762 /* oops, does not match any of our keywords!? */
764 return GNUNET_SYSERR;
767 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
768 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
769 GNUNET_FS_ublock_decrypt_ (edata, edata_size,
771 sc->requests[i].keyword,
778 * Process a keyword search result. The actual type of block is
779 * a UBlock; we know it is a keyword search result because that's
780 * what we were searching for.
782 * @param sc our search context
783 * @param ub the ublock with the keyword search result
784 * @param size size of @a ub
787 process_kblock (struct GNUNET_FS_SearchContext *sc,
788 const struct UBlock *ub,
792 char pt[size - sizeof (struct UBlock)];
794 struct GNUNET_CONTAINER_MetaData *meta;
795 struct GNUNET_FS_Uri *uri;
799 if (-1 == (i = decrypt_block_with_keyword (sc,
800 &ub->verification_key,
802 size - sizeof (struct UBlock),
805 /* parse; pt[0] is just '\0', so we skip over that */
806 eos = memchr (&pt[1], '\0', sizeof (pt) - 1);
812 if (NULL == (uri = GNUNET_FS_uri_parse (&pt[1], &emsg)))
814 if (GNUNET_FS_VERSION > 0x00090400)
816 /* we broke this in 0x00090300, so don't bitch
817 too loudly just one version up... */
818 GNUNET_break_op (0); /* ublock malformed */
819 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
820 _("Failed to parse URI `%s': %s\n"),
824 GNUNET_free_non_null (emsg);
828 if (sizeof (pt) == j)
829 meta = GNUNET_CONTAINER_meta_data_create ();
831 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j], sizeof (pt) - j);
834 GNUNET_break_op (0); /* ublock malformed */
835 GNUNET_FS_uri_destroy (uri);
838 process_ksk_result (sc,
844 GNUNET_CONTAINER_meta_data_destroy (meta);
845 GNUNET_FS_uri_destroy (uri);
850 * Process a namespace-search result. The actual type of block is
851 * a UBlock; we know it is a namespace search result because that's
852 * what we were searching for.
854 * @param sc our search context
855 * @param ub the ublock with a namespace result
856 * @param size size of @a ub
859 process_sblock (struct GNUNET_FS_SearchContext *sc,
860 const struct UBlock *ub,
863 size_t len = size - sizeof (struct UBlock);
865 struct GNUNET_FS_Uri *uri;
866 struct GNUNET_CONTAINER_MetaData *meta;
872 GNUNET_FS_ublock_decrypt_ (&ub[1], len,
873 &sc->uri->data.sks.ns,
874 sc->uri->data.sks.identifier,
877 if (0 == (off = GNUNET_STRINGS_buffer_tokenize (pt, len, 2, &id, &uris)))
879 GNUNET_break_op (0); /* ublock malformed */
882 if (NULL == (meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off], len - off)))
884 GNUNET_break_op (0); /* ublock malformed */
887 if (NULL == (uri = GNUNET_FS_uri_parse (uris, &emsg)))
889 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
890 _("Failed to parse URI `%s': %s\n"),
892 GNUNET_break_op (0); /* ublock malformed */
893 GNUNET_free_non_null (emsg);
894 GNUNET_CONTAINER_meta_data_destroy (meta);
898 process_sks_result (sc, id, uri, meta);
900 GNUNET_FS_uri_destroy (uri);
901 GNUNET_CONTAINER_meta_data_destroy (meta);
906 * Shutdown any existing connection to the FS
907 * service and try to establish a fresh one
908 * (and then re-transmit our search request).
910 * @param sc the search to reconnec
913 try_reconnect (struct GNUNET_FS_SearchContext *sc);
917 * We check a result message from the service.
920 * @param msg result message received
923 check_result (void *cls,
924 const struct ClientPutMessage *cm)
926 /* payload of any variable size is OK */
932 * We process a search result from the service.
935 * @param msg result message received
938 handle_result (void *cls,
939 const struct ClientPutMessage *cm)
941 struct GNUNET_FS_SearchContext *sc = cls;
942 uint16_t msize = ntohs (cm->header.size) - sizeof (*cm);
943 enum GNUNET_BLOCK_Type type = ntohl (cm->type);
945 if (GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh (cm->expiration)).rel_value_us > 0)
947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
948 "Result received has already expired.\n");
949 return; /* result expired */
953 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
954 if (GNUNET_FS_URI_SKS == sc->uri->type)
956 (const struct UBlock *) &cm[1],
960 (const struct UBlock *) &cm[1],
963 case GNUNET_BLOCK_TYPE_ANY:
966 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
969 case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
972 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
976 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
977 _("Got result with unknown block type `%d', ignoring"),
985 * Schedule the transmission of the (next) search request
988 * @param sc context for the search
991 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
995 * Closure for #build_result_set().
997 struct MessageBuilderContext
1000 * How many entries can we store to xoff.
1002 unsigned int put_cnt;
1005 * How many entries should we skip.
1007 unsigned int skip_cnt;
1010 * Where to store the keys.
1012 struct GNUNET_HashCode *xoff;
1015 * Search context we are iterating for.
1017 struct GNUNET_FS_SearchContext *sc;
1020 * Keyword offset the search result must match (0 for SKS)
1022 unsigned int keyword_offset;
1027 * Iterating over the known results, pick those matching the given
1028 * result range and store their keys at 'xoff'.
1030 * @param cls the `struct MessageBuilderContext`
1031 * @param key key for a result
1032 * @param value the search result
1033 * @return #GNUNET_OK to continue iterating
1036 build_result_set (void *cls,
1037 const struct GNUNET_HashCode *key,
1040 struct MessageBuilderContext *mbc = cls;
1041 struct GNUNET_FS_SearchResult *sr = value;
1043 if ( (NULL != sr->keyword_bitmap) &&
1044 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
1045 return GNUNET_OK; /* have no match for this keyword yet */
1046 if (mbc->skip_cnt > 0)
1051 if (0 == mbc->put_cnt)
1052 return GNUNET_SYSERR;
1053 mbc->xoff[--mbc->put_cnt] = *key;
1060 * Iterating over the known results, count those matching the given
1061 * result range and increment put count for each.
1063 * @param cls the `struct MessageBuilderContext`
1064 * @param key key for a result
1065 * @param value the search result
1066 * @return #GNUNET_OK to continue iterating
1069 find_result_set (void *cls,
1070 const struct GNUNET_HashCode *key,
1073 struct MessageBuilderContext *mbc = cls;
1074 struct GNUNET_FS_SearchResult *sr = value;
1076 if ( (NULL != sr->keyword_bitmap) &&
1077 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
1078 return GNUNET_OK; /* have no match for this keyword yet */
1085 * Schedule the transmission of the (next) search request
1088 * @param sc context for the search
1091 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc)
1093 struct MessageBuilderContext mbc;
1094 struct GNUNET_MQ_Envelope *env;
1095 struct SearchMessage *sm;
1096 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
1097 unsigned int total_seen_results; /* total number of result hashes to send */
1102 unsigned int search_request_map_offset;
1103 unsigned int keyword_offset;
1106 memset (&mbc, 0, sizeof (mbc));
1108 if (GNUNET_FS_uri_test_ksk (sc->uri))
1110 /* This will calculate the result set size ONLY for
1111 "keyword_offset == 0", so we will have to recalculate
1112 it for the other keywords later! */
1113 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1116 total_seen_results = mbc.put_cnt;
1121 = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
1123 search_request_map_offset = 0;
1125 first_call = GNUNET_YES;
1126 while ( (0 != (left =
1127 (total_seen_results - search_request_map_offset))) ||
1128 (GNUNET_YES == first_call) )
1130 first_call = GNUNET_NO;
1131 options = SEARCH_MESSAGE_OPTION_NONE;
1132 if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1133 options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY;
1135 fit = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof (*sm)) / sizeof (struct GNUNET_HashCode);
1136 todo = GNUNET_MIN (fit,
1138 env = GNUNET_MQ_msg_extra (sm,
1139 sizeof (struct GNUNET_HashCode) * todo,
1140 GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1141 mbc.skip_cnt = search_request_map_offset;
1142 mbc.xoff = (struct GNUNET_HashCode *) &sm[1];
1143 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK);
1144 sm->anonymity_level = htonl (sc->anonymity);
1145 memset (&sm->target,
1147 sizeof (struct GNUNET_PeerIdentity));
1149 if (GNUNET_FS_uri_test_ksk (sc->uri))
1151 mbc.keyword_offset = keyword_offset;
1152 /* calculate how many results we can send in this message */
1154 /* now build message */
1155 sm->query = sc->requests[keyword_offset].uquery;
1156 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1159 search_request_map_offset += todo;
1160 GNUNET_assert (0 == mbc.put_cnt);
1161 GNUNET_assert (total_seen_results >= search_request_map_offset);
1162 if (total_seen_results != search_request_map_offset)
1164 /* more requesting to be done... */
1165 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1169 sm->options = htonl (options);
1171 if (sc->uri->data.ksk.keywordCount != keyword_offset)
1173 /* more keywords => more requesting to be done... */
1174 first_call = GNUNET_YES;
1175 search_request_map_offset = 0;
1177 mbc.keyword_offset = keyword_offset;
1178 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1181 total_seen_results = mbc.put_cnt;
1187 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1189 GNUNET_CRYPTO_ecdsa_public_key_derive (&sc->uri->data.sks.ns,
1190 sc->uri->data.sks.identifier,
1193 GNUNET_CRYPTO_hash (&dpub,
1197 mbc.keyword_offset = 0;
1198 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1201 GNUNET_assert (total_seen_results >= search_request_map_offset);
1202 if (total_seen_results != search_request_map_offset)
1204 /* more requesting to be done... */
1205 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1209 sm->options = htonl (options);
1212 GNUNET_MQ_send (sc->mq,
1219 * Generic error handler, called with the appropriate error code and
1220 * the same closure specified at the creation of the message queue.
1221 * Not every message queue implementation supports an error handler.
1223 * @param cls closure with the `struct GNUNET_FS_SearchContext *`
1224 * @param error error code
1227 search_mq_error_handler (void *cls,
1228 enum GNUNET_MQ_Error error)
1230 struct GNUNET_FS_SearchContext *sc = cls;
1234 GNUNET_MQ_destroy (sc->mq);
1242 * Reconnect to the FS service and transmit
1245 * @param cls our search context
1248 do_reconnect (void *cls)
1250 struct GNUNET_FS_SearchContext *sc = cls;
1251 struct GNUNET_MQ_MessageHandler handlers[] = {
1252 GNUNET_MQ_hd_var_size (result,
1253 GNUNET_MESSAGE_TYPE_FS_PUT,
1254 struct ClientPutMessage,
1256 GNUNET_MQ_handler_end ()
1260 sc->mq = GNUNET_CLIENT_connect (sc->h->cfg,
1263 &search_mq_error_handler,
1270 schedule_transmit_search_request (sc);
1275 * Shutdown any existing connection to the FS
1276 * service and try to establish a fresh one
1277 * (and then re-transmit our search request).
1279 * @param sc the search to reconnec
1282 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1286 GNUNET_MQ_destroy (sc->mq);
1289 sc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (sc->reconnect_backoff);
1291 GNUNET_SCHEDULER_add_delayed (sc->reconnect_backoff,
1298 * Start search for content, internal API.
1300 * @param h handle to the file sharing subsystem
1301 * @param uri specifies the search parameters; can be
1302 * a KSK URI or an SKS URI.
1303 * @param anonymity desired level of anonymity
1304 * @param options options for the search
1305 * @param cctx initial value for the client context
1306 * @param psearch parent search result (for namespace update searches)
1307 * @return context that can be used to control the search
1309 static struct GNUNET_FS_SearchContext *
1310 search_start (struct GNUNET_FS_Handle *h,
1311 const struct GNUNET_FS_Uri *uri,
1313 enum GNUNET_FS_SearchOptions options,
1315 struct GNUNET_FS_SearchResult *psearch)
1317 struct GNUNET_FS_SearchContext *sc;
1318 struct GNUNET_FS_ProgressInfo pi;
1320 sc = GNUNET_new (struct GNUNET_FS_SearchContext);
1322 sc->options = options;
1323 sc->uri = GNUNET_FS_uri_dup (uri);
1324 sc->anonymity = anonymity;
1325 sc->start_time = GNUNET_TIME_absolute_get ();
1326 if (NULL != psearch)
1328 sc->psearch_result = psearch;
1329 psearch->update_search = sc;
1331 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
1332 sc->client_info = cctx;
1333 if (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))
1335 GNUNET_FS_uri_destroy (sc->uri);
1336 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1340 GNUNET_FS_search_sync_ (sc);
1341 pi.status = GNUNET_FS_STATUS_SEARCH_START;
1342 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1348 * Update the 'results' map for the individual keywords with the
1349 * results from the 'global' result set.
1351 * @param cls closure, the `struct GNUNET_FS_SearchContext *`
1352 * @param key current key code
1353 * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
1354 * @return #GNUNET_YES (we should continue to iterate)
1357 update_sre_result_maps (void *cls,
1358 const struct GNUNET_HashCode *key,
1361 struct GNUNET_FS_SearchContext *sc = cls;
1362 struct GNUNET_FS_SearchResult *sr = value;
1365 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1366 if (0 != (sr->keyword_bitmap[i / 8] & (1 << (i % 8))))
1367 GNUNET_break (GNUNET_OK ==
1368 GNUNET_CONTAINER_multihashmap_put (sc->requests[i].results,
1371 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1378 * Build the request and actually initiate the search using the
1379 * GNUnet FS service.
1381 * @param sc search context
1382 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1385 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1388 const char *keyword;
1389 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
1390 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
1391 struct SearchRequestEntry *sre;
1393 GNUNET_assert (NULL == sc->mq);
1394 if (GNUNET_FS_uri_test_ksk (sc->uri))
1396 GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1397 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
1398 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
1400 = GNUNET_new_array (sc->uri->data.ksk.keywordCount,
1401 struct SearchRequestEntry);
1403 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1405 keyword = &sc->uri->data.ksk.keywords[i][1];
1406 sre = &sc->requests[i];
1407 sre->keyword = GNUNET_strdup (keyword);
1408 GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
1412 GNUNET_CRYPTO_hash (&sre->dpub,
1413 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
1415 sre->mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1417 sc->mandatory_count++;
1418 sre->results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
1420 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1421 &update_sre_result_maps,
1424 GNUNET_assert (NULL == sc->task);
1428 GNUNET_SCHEDULER_cancel (sc->task);
1430 return GNUNET_SYSERR;
1437 * Freeze probes for the given search result.
1439 * @param cls the global FS handle
1440 * @param key the key for the search result (unused)
1441 * @param value the search result to free
1442 * @return #GNUNET_OK
1445 search_result_freeze_probes (void *cls,
1446 const struct GNUNET_HashCode *key,
1449 struct GNUNET_FS_SearchResult *sr = value;
1451 if (NULL != sr->probe_ctx)
1453 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1454 sr->probe_ctx = NULL;
1455 GNUNET_FS_stop_probe_ping_task_ (sr);
1457 if (NULL != sr->probe_cancel_task)
1459 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1460 sr->probe_cancel_task = NULL;
1462 if (NULL != sr->update_search)
1463 GNUNET_FS_search_pause (sr->update_search);
1469 * Resume probes for the given search result.
1471 * @param cls the global FS handle
1472 * @param key the key for the search result (unused)
1473 * @param value the search result to free
1474 * @return #GNUNET_OK
1477 search_result_resume_probes (void *cls,
1478 const struct GNUNET_HashCode * key,
1481 struct GNUNET_FS_SearchResult *sr = value;
1483 GNUNET_FS_search_start_probe_ (sr);
1484 if (NULL != sr->update_search)
1485 GNUNET_FS_search_continue (sr->update_search);
1491 * Signal suspend and free the given search result.
1493 * @param cls the global FS handle
1494 * @param key the key for the search result (unused)
1495 * @param value the search result to free
1496 * @return #GNUNET_OK
1499 search_result_suspend (void *cls,
1500 const struct GNUNET_HashCode *key,
1503 struct GNUNET_FS_SearchContext *sc = cls;
1504 struct GNUNET_FS_SearchResult *sr = value;
1505 struct GNUNET_FS_ProgressInfo pi;
1507 if (NULL != sr->download)
1509 GNUNET_FS_download_signal_suspend_ (sr->download);
1510 sr->download = NULL;
1512 if (NULL != sr->update_search)
1514 GNUNET_FS_search_signal_suspend_ (sr->update_search);
1515 sr->update_search = NULL;
1517 GNUNET_FS_search_stop_probe_ (sr);
1518 if (0 == sr->mandatory_missing)
1520 /* client is aware of search result, notify about suspension event */
1521 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1522 pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1523 pi.value.search.specifics.result_suspend.meta = sr->meta;
1524 pi.value.search.specifics.result_suspend.uri = sr->uri;
1525 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1527 GNUNET_break (NULL == sr->client_info);
1528 GNUNET_free_non_null (sr->serialization);
1529 GNUNET_FS_uri_destroy (sr->uri);
1530 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1531 GNUNET_free_non_null (sr->keyword_bitmap);
1538 * Create SUSPEND event for the given search operation
1539 * and then clean up our state (without stop signal).
1541 * @param cls the `struct GNUNET_FS_SearchContext` to signal for
1544 GNUNET_FS_search_signal_suspend_ (void *cls)
1546 struct GNUNET_FS_SearchContext *sc = cls;
1547 struct GNUNET_FS_ProgressInfo pi;
1550 GNUNET_FS_end_top (sc->h, sc->top);
1551 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1552 &search_result_suspend, sc);
1553 pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1554 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1555 GNUNET_break (NULL == sc->client_info);
1556 if (sc->task != NULL)
1558 GNUNET_SCHEDULER_cancel (sc->task);
1563 GNUNET_MQ_destroy (sc->mq);
1566 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1567 if (NULL != sc->requests)
1569 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1570 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1572 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1573 GNUNET_free (sc->requests[i].keyword);
1576 GNUNET_free_non_null (sc->requests);
1577 GNUNET_free_non_null (sc->emsg);
1578 GNUNET_FS_uri_destroy (sc->uri);
1579 GNUNET_free_non_null (sc->serialization);
1585 * Start search for content.
1587 * @param h handle to the file sharing subsystem
1588 * @param uri specifies the search parameters; can be
1589 * a KSK URI or an SKS URI.
1590 * @param anonymity desired level of anonymity
1591 * @param options options for the search
1592 * @param cctx initial value for the client context
1593 * @return context that can be used to control the search
1595 struct GNUNET_FS_SearchContext *
1596 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1597 const struct GNUNET_FS_Uri *uri, uint32_t anonymity,
1598 enum GNUNET_FS_SearchOptions options, void *cctx)
1600 struct GNUNET_FS_SearchContext *ret;
1602 ret = search_start (h, uri, anonymity, options, cctx, NULL);
1605 ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1613 * @param sc context for the search that should be paused
1616 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1618 struct GNUNET_FS_ProgressInfo pi;
1620 if (NULL != sc->task)
1622 GNUNET_SCHEDULER_cancel (sc->task);
1627 GNUNET_MQ_destroy (sc->mq);
1630 GNUNET_FS_search_sync_ (sc);
1631 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1632 &search_result_freeze_probes,
1634 pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1635 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1642 * Continue paused search.
1644 * @param sc context for the search that should be resumed
1647 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1649 struct GNUNET_FS_ProgressInfo pi;
1651 GNUNET_assert (NULL == sc->mq);
1652 GNUNET_assert (NULL == sc->task);
1654 GNUNET_FS_search_sync_ (sc);
1655 pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1656 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1657 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1658 &search_result_resume_probes, sc);
1663 * Signal stop for the given search result.
1665 * @param cls the global FS handle
1666 * @param key the key for the search result (unused)
1667 * @param value the search result to free
1668 * @return #GNUNET_OK
1671 search_result_stop (void *cls,
1672 const struct GNUNET_HashCode *key,
1675 struct GNUNET_FS_SearchContext *sc = cls;
1676 struct GNUNET_FS_SearchResult *sr = value;
1677 struct GNUNET_FS_ProgressInfo pi;
1679 GNUNET_FS_search_stop_probe_ (sr);
1680 if (NULL != sr->download)
1682 sr->download->search = NULL;
1684 = GNUNET_FS_make_top (sr->download->h,
1685 &GNUNET_FS_download_signal_suspend_,
1687 if (NULL != sr->download->serialization)
1689 GNUNET_FS_remove_sync_file_ (sc->h,
1690 GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1691 sr->download->serialization);
1692 GNUNET_free (sr->download->serialization);
1693 sr->download->serialization = NULL;
1695 pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1696 GNUNET_FS_download_make_status_ (&pi,
1698 GNUNET_FS_download_sync_ (sr->download);
1699 sr->download = NULL;
1701 if (0 != sr->mandatory_missing)
1703 /* client is unaware of search result as
1704 it does not match required keywords */
1705 GNUNET_break (NULL == sr->client_info);
1708 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1709 pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1710 pi.value.search.specifics.result_stopped.meta = sr->meta;
1711 pi.value.search.specifics.result_stopped.uri = sr->uri;
1712 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sc);
1718 * Free the given search result.
1720 * @param cls the global FS handle
1721 * @param key the key for the search result (unused)
1722 * @param value the search result to free
1723 * @return #GNUNET_OK
1726 search_result_free (void *cls,
1727 const struct GNUNET_HashCode *key,
1730 struct GNUNET_FS_SearchResult *sr = value;
1732 if (NULL != sr->update_search)
1734 GNUNET_FS_search_stop (sr->update_search);
1735 GNUNET_assert (NULL == sr->update_search);
1737 GNUNET_break (NULL == sr->probe_ctx);
1738 GNUNET_break (NULL == sr->probe_cancel_task);
1739 GNUNET_break (NULL == sr->client_info);
1740 GNUNET_free_non_null (sr->serialization);
1741 GNUNET_FS_uri_destroy (sr->uri);
1742 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1743 GNUNET_free_non_null (sr->keyword_bitmap);
1750 * Stop search for content.
1752 * @param sc context for the search that should be stopped
1755 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1757 struct GNUNET_FS_ProgressInfo pi;
1760 if (NULL != sc->top)
1761 GNUNET_FS_end_top (sc->h, sc->top);
1762 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1763 &search_result_stop,
1765 if (NULL != sc->psearch_result)
1766 sc->psearch_result->update_search = NULL;
1767 if (NULL != sc->serialization)
1769 GNUNET_FS_remove_sync_file_ (sc->h,
1770 (NULL != sc->psearch_result)
1771 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1772 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1774 GNUNET_FS_remove_sync_dir_ (sc->h,
1775 (NULL != sc->psearch_result)
1776 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1777 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1779 GNUNET_free (sc->serialization);
1781 pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1782 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1785 GNUNET_break (NULL == sc->client_info);
1786 if (NULL != sc->task)
1788 GNUNET_SCHEDULER_cancel (sc->task);
1793 GNUNET_MQ_destroy (sc->mq);
1796 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1797 &search_result_free, sc);
1798 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1799 if (NULL != sc->requests)
1801 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1802 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1803 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1805 GNUNET_free_non_null (sc->requests);
1806 GNUNET_free_non_null (sc->emsg);
1807 GNUNET_FS_uri_destroy (sc->uri);
1811 /* end of fs_search.c */