aboutsummaryrefslogtreecommitdiff
path: root/src/service/fs/fs_search.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/fs/fs_search.c')
-rw-r--r--src/service/fs/fs_search.c1826
1 files changed, 1826 insertions, 0 deletions
diff --git a/src/service/fs/fs_search.c b/src/service/fs/fs_search.c
new file mode 100644
index 000000000..8b8c54c67
--- /dev/null
+++ b/src/service/fs/fs_search.c
@@ -0,0 +1,1826 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2014 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/fs_search.c
22 * @brief Helper functions for searching.
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_constants.h"
27
28#include "gnunet_fs_service.h"
29#include "gnunet_protocols.h"
30#include "fs_api.h"
31#include "fs_publish_ublock.h"
32
33
34/**
35 * Number of availability trials we perform per search result.
36 */
37#define AVAILABILITY_TRIALS_MAX 8
38
39/**
40 * Fill in all of the generic fields for a search event and
41 * call the callback.
42 *
43 * @param pi structure to fill in
44 * @param h file-sharing handle
45 * @param sc overall search context
46 * @return value returned by the callback
47 */
48void *
49GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
50 struct GNUNET_FS_Handle *h,
51 struct GNUNET_FS_SearchContext *sc)
52{
53 void *ret;
54
55 pi->value.search.sc = sc;
56 pi->value.search.cctx = (NULL != sc) ? sc->client_info : NULL;
57 pi->value.search.pctx =
58 ((NULL == sc) || (NULL == sc->psearch_result))
59 ? NULL
60 : sc->psearch_result->client_info;
61 pi->value.search.query = (NULL != sc) ? sc->uri : NULL;
62 pi->value.search.duration = (NULL != sc)
63 ? GNUNET_TIME_absolute_get_duration (
64 sc->start_time)
65 : GNUNET_TIME_UNIT_ZERO;
66 pi->value.search.anonymity = (NULL != sc) ? sc->anonymity : 0;
67 pi->fsh = h;
68 ret = h->upcb (h->upcb_cls, pi);
69 return ret;
70}
71
72
73/**
74 * Check if the given result is identical to the given URI.
75 *
76 * @param cls points to the URI we check against
77 * @param key not used
78 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
79 * should compare with
80 * @return #GNUNET_SYSERR if the result is present,
81 * #GNUNET_OK otherwise
82 */
83static int
84test_result_present (void *cls,
85 const struct GNUNET_HashCode *key,
86 void *value)
87{
88 const struct GNUNET_FS_Uri *uri = cls;
89 struct GNUNET_FS_SearchResult *sr = value;
90
91 if (GNUNET_FS_uri_test_equal (uri, sr->uri))
92 return GNUNET_SYSERR;
93 return GNUNET_OK;
94}
95
96
97/**
98 * We've found a new CHK result. Let the client
99 * know about it.
100 *
101 * @param sc the search context
102 * @param sr the specific result
103 */
104static void
105notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
106 struct GNUNET_FS_SearchResult *sr)
107{
108 struct GNUNET_FS_ProgressInfo pi;
109
110 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
111 pi.value.search.specifics.result.meta = sr->meta;
112 pi.value.search.specifics.result.uri = sr->uri;
113 pi.value.search.specifics.result.result = sr;
114 pi.value.search.specifics.result.applicability_rank = sr->optional_support;
115 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
116}
117
118
119/**
120 * We've found new information about an existing CHK result. Let the
121 * client know about it.
122 *
123 * @param sc the search context
124 * @param sr the specific result
125 */
126static void
127notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
128 struct GNUNET_FS_SearchResult *sr)
129{
130 struct GNUNET_FS_ProgressInfo pi;
131
132 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
133 pi.value.search.specifics.update.cctx = sr->client_info;
134 pi.value.search.specifics.update.meta = sr->meta;
135 pi.value.search.specifics.update.uri = sr->uri;
136 pi.value.search.specifics.update.availability_rank =
137 2 * sr->availability_success - sr->availability_trials;
138 pi.value.search.specifics.update.availability_certainty =
139 sr->availability_trials;
140 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
141 pi.value.search.specifics.update.current_probe_time
142 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
143 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
144}
145
146
147/**
148 * Context for "get_result_present".
149 */
150struct GetResultContext
151{
152 /**
153 * The URI we're looking for.
154 */
155 const struct GNUNET_FS_Uri *uri;
156
157 /**
158 * Where to store a pointer to the search
159 * result struct if we found a match.
160 */
161 struct GNUNET_FS_SearchResult *sr;
162};
163
164
165/**
166 * Check if the given result is identical to the given URI and if so
167 * return it.
168 *
169 * @param cls a `struct GetResultContext`
170 * @param key not used
171 * @param value a `struct GNUNET_FS_SearchResult` who's URI we
172 * should compare with
173 * @return #GNUNET_OK
174 */
175static int
176get_result_present (void *cls,
177 const struct GNUNET_HashCode *key,
178 void *value)
179{
180 struct GetResultContext *grc = cls;
181 struct GNUNET_FS_SearchResult *sr = value;
182
183 if (GNUNET_FS_uri_test_equal (grc->uri, sr->uri))
184 grc->sr = sr;
185 return GNUNET_OK;
186}
187
188
189/**
190 * Signal result of last probe to client and then schedule next
191 * probe.
192 *
193 * @param sr search result to signal for
194 */
195static void
196signal_probe_result (struct GNUNET_FS_SearchResult *sr)
197{
198 struct GNUNET_FS_ProgressInfo pi;
199
200 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
201 pi.value.search.specifics.update.cctx = sr->client_info;
202 pi.value.search.specifics.update.meta = sr->meta;
203 pi.value.search.specifics.update.uri = sr->uri;
204 pi.value.search.specifics.update.availability_rank
205 = 2 * sr->availability_success - sr->availability_trials;
206 pi.value.search.specifics.update.availability_certainty
207 = sr->availability_trials;
208 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
209 pi.value.search.specifics.update.current_probe_time
210 = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
211 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sr->sc);
212 GNUNET_FS_search_start_probe_ (sr);
213}
214
215
216/**
217 * Handle the case where we have failed to receive a response for our probe.
218 *
219 * @param cls our `struct GNUNET_FS_SearchResult *`
220 */
221static void
222probe_failure_handler (void *cls)
223{
224 struct GNUNET_FS_SearchResult *sr = cls;
225
226 sr->probe_cancel_task = NULL;
227 sr->availability_trials++;
228 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
229 sr->probe_ctx = NULL;
230 GNUNET_FS_stop_probe_ping_task_ (sr);
231 GNUNET_FS_search_result_sync_ (sr);
232 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
233 "Probe #%u for search result %p failed\n",
234 sr->availability_trials,
235 sr);
236 signal_probe_result (sr);
237}
238
239
240/**
241 * Handle the case where we have gotten a response for our probe.
242 *
243 * @param cls our `struct GNUNET_FS_SearchResult *`
244 */
245static void
246probe_success_handler (void *cls)
247{
248 struct GNUNET_FS_SearchResult *sr = cls;
249
250 sr->probe_cancel_task = NULL;
251 sr->availability_trials++;
252 sr->availability_success++;
253 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
254 sr->probe_ctx = NULL;
255 GNUNET_FS_stop_probe_ping_task_ (sr);
256 GNUNET_FS_search_result_sync_ (sr);
257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
258 "Probe #%u for search result %p succeeded\n",
259 sr->availability_trials,
260 sr);
261 signal_probe_result (sr);
262}
263
264
265/**
266 * Notification of FS that a search probe has made progress.
267 * This function is used INSTEAD of the client's event handler
268 * for downloads where the #GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
269 *
270 * @param cls closure, always NULL (!), actual closure
271 * is in the client-context of the info struct
272 * @param info details about the event, specifying the event type
273 * and various bits about the event
274 * @return client-context (for the next progress call
275 * for this operation; should be set to NULL for
276 * SUSPEND and STOPPED events). The value returned
277 * will be passed to future callbacks in the respective
278 * field in the `struct GNUNET_FS_ProgressInfo`.
279 */
280void *
281GNUNET_FS_search_probe_progress_ (void *cls,
282 const struct GNUNET_FS_ProgressInfo *info)
283{
284 struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
285 struct GNUNET_TIME_Relative dur;
286
287 switch (info->status)
288 {
289 case GNUNET_FS_STATUS_DOWNLOAD_START:
290 /* ignore */
291 break;
292 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
293 /* probes should never be resumed */
294 GNUNET_assert (0);
295 break;
296 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
297 /* probes should never be suspended */
298 GNUNET_break (0);
299 break;
300 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
301 /* ignore */
302 break;
303 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
304 if (NULL != sr->probe_cancel_task)
305 {
306 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
307 sr->probe_cancel_task = NULL;
308 }
309 sr->probe_cancel_task =
310 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
311 &probe_failure_handler, sr);
312 break;
313 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
314 if (NULL != sr->probe_cancel_task)
315 {
316 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
317 sr->probe_cancel_task = NULL;
318 }
319 sr->probe_cancel_task =
320 GNUNET_SCHEDULER_add_now (&probe_success_handler, sr);
321 break;
322 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
323 if (NULL != sr->probe_cancel_task)
324 {
325 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
326 sr->probe_cancel_task = NULL;
327 }
328 sr = NULL;
329 break;
330 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
331 if (NULL == sr->probe_cancel_task)
332 {
333 sr->probe_active_time = GNUNET_TIME_absolute_get ();
334 sr->probe_cancel_task =
335 GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
336 &probe_failure_handler, sr);
337 }
338 break;
339 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
340 if (NULL != sr->probe_cancel_task)
341 {
342 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
343 sr->probe_cancel_task = NULL;
344 }
345 dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
346 sr->remaining_probe_time =
347 GNUNET_TIME_relative_subtract (sr->remaining_probe_time, dur);
348 if (0 == sr->remaining_probe_time.rel_value_us)
349 sr->probe_cancel_task =
350 GNUNET_SCHEDULER_add_now (&probe_failure_handler, sr);
351 GNUNET_FS_search_result_sync_ (sr);
352 break;
353
354 default:
355 GNUNET_break (0);
356 return NULL;
357 }
358 return sr;
359}
360
361
362/**
363 * Task run periodically to remind clients that a probe is active.
364 *
365 * @param cls the `struct GNUNET_FS_SearchResult` that we are probing for
366 */
367static void
368probe_ping_task_cb (void *cls)
369{
370 struct GNUNET_FS_Handle *h = cls;
371
372 for (struct GNUNET_FS_SearchResult *sr = h->probes_head;
373 NULL != sr;
374 sr = sr->next)
375 if (NULL != sr->probe_ctx->mq)
376 signal_probe_result (sr);
377 h->probe_ping_task
378 = GNUNET_SCHEDULER_add_delayed (GNUNET_FS_PROBE_UPDATE_FREQUENCY,
379 &probe_ping_task_cb,
380 h);
381}
382
383
384/**
385 * Start the ping task for this search result.
386 *
387 * @param sr result to start pinging for.
388 */
389static void
390start_probe_ping_task (struct GNUNET_FS_SearchResult *sr)
391{
392 struct GNUNET_FS_Handle *h = sr->h;
393
394 GNUNET_CONTAINER_DLL_insert (h->probes_head,
395 h->probes_tail,
396 sr);
397 if (NULL == h->probe_ping_task)
398 h->probe_ping_task
399 = GNUNET_SCHEDULER_add_now (&probe_ping_task_cb,
400 h);
401}
402
403
404/**
405 * Stop the ping task for this search result.
406 *
407 * @param sr result to start pinging for.
408 */
409void
410GNUNET_FS_stop_probe_ping_task_ (struct GNUNET_FS_SearchResult *sr)
411{
412 struct GNUNET_FS_Handle *h = sr->h;
413
414 GNUNET_CONTAINER_DLL_remove (h->probes_head,
415 h->probes_tail,
416 sr);
417 if (NULL == h->probes_head)
418 {
419 GNUNET_SCHEDULER_cancel (h->probe_ping_task);
420 h->probe_ping_task = NULL;
421 }
422}
423
424
425/**
426 * Start download probes for the given search result.
427 *
428 * @param sr the search result
429 */
430void
431GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
432{
433 uint64_t off;
434 uint64_t len;
435
436 if (NULL != sr->probe_ctx)
437 return;
438 if (NULL != sr->download)
439 return;
440 if (0 == (sr->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
441 return;
442 if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
443 return;
444 if ( (GNUNET_FS_URI_CHK != sr->uri->type) &&
445 (GNUNET_FS_URI_LOC != sr->uri->type) )
446 return;
447 len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
448 if (0 == len)
449 return;
450 if ((len <= DBLOCK_SIZE) && (sr->availability_success > 0))
451 return;
452 off = len / DBLOCK_SIZE;
453 if (off > 0)
454 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
455 off *= DBLOCK_SIZE;
456 if (len - off < DBLOCK_SIZE)
457 len = len - off;
458 else
459 len = DBLOCK_SIZE;
460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461 "Starting probe #%u (at offset %llu) for search result %p\n",
462 sr->availability_trials + 1,
463 (unsigned long long) off,
464 sr);
465 sr->remaining_probe_time =
466 GNUNET_TIME_relative_saturating_multiply (sr->h->avg_block_latency,
467 2 * (1
468 + sr->availability_trials));
469 sr->probe_ctx =
470 GNUNET_FS_download_start (sr->h, sr->uri, sr->meta, NULL, NULL, off,
471 len, sr->anonymity,
472 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES
473 | GNUNET_FS_DOWNLOAD_IS_PROBE, sr, NULL);
474 start_probe_ping_task (sr);
475}
476
477
478/**
479 * Start download probes for the given search result.
480 *
481 * @param h file-sharing handle to use for the operation
482 * @param uri URI to probe
483 * @param meta meta data associated with the URI
484 * @param client_info client info pointer to use for associated events
485 * @param anonymity anonymity level to use for the probes
486 * @return the search result handle to access the probe activity
487 */
488struct GNUNET_FS_SearchResult *
489GNUNET_FS_probe (struct GNUNET_FS_Handle *h,
490 const struct GNUNET_FS_Uri *uri,
491 const struct GNUNET_FS_MetaData *meta,
492 void *client_info,
493 uint32_t anonymity)
494{
495 struct GNUNET_FS_SearchResult *sr;
496
497 GNUNET_assert (NULL != h);
498 GNUNET_assert (NULL != uri);
499 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
500 sr->h = h;
501 sr->uri = GNUNET_FS_uri_dup (uri);
502 sr->meta = GNUNET_FS_meta_data_duplicate (meta);
503 sr->client_info = client_info;
504 sr->anonymity = anonymity;
505 GNUNET_FS_search_start_probe_ (sr);
506 return sr;
507}
508
509
510/**
511 * Stop probing activity associated with a search result.
512 *
513 * @param sr search result
514 */
515static void
516GNUNET_FS_search_stop_probe_ (struct GNUNET_FS_SearchResult *sr)
517{
518 if (NULL != sr->probe_ctx)
519 {
520 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
521 sr->probe_ctx = NULL;
522 GNUNET_FS_stop_probe_ping_task_ (sr);
523 }
524 if (NULL != sr->probe_cancel_task)
525 {
526 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
527 sr->probe_cancel_task = NULL;
528 }
529}
530
531
532/**
533 * Stop probe activity. Must ONLY be used on values
534 * returned from #GNUNET_FS_probe.
535 *
536 * @param sr search result to stop probing for (freed)
537 * @return the value of the 'client_info' pointer
538 */
539void *
540GNUNET_FS_probe_stop (struct GNUNET_FS_SearchResult *sr)
541{
542 void *client_info;
543
544 GNUNET_assert (NULL == sr->sc);
545 GNUNET_FS_search_stop_probe_ (sr);
546 GNUNET_FS_uri_destroy (sr->uri);
547 GNUNET_FS_meta_data_destroy (sr->meta);
548 client_info = sr->client_info;
549 GNUNET_free (sr);
550 return client_info;
551}
552
553
554/**
555 * We have received a KSK result. Check how it fits in with the
556 * overall query and notify the client accordingly.
557 *
558 * @param sc context for the overall query
559 * @param ent entry for the specific keyword
560 * @param uri the URI that was found
561 * @param meta metadata associated with the URI
562 * under the @a ent keyword
563 */
564static void
565process_ksk_result (struct GNUNET_FS_SearchContext *sc,
566 struct SearchRequestEntry *ent,
567 const struct GNUNET_FS_Uri *uri,
568 const struct GNUNET_FS_MetaData *meta)
569{
570 struct GNUNET_HashCode key;
571 struct GNUNET_FS_SearchResult *sr;
572 struct GetResultContext grc;
573 int is_new;
574 unsigned int koff;
575
576 /* check if new */
577 GNUNET_assert (NULL != sc);
578 if (GNUNET_OK !=
579 GNUNET_FS_uri_to_key (uri,
580 &key))
581 {
582 GNUNET_break_op (0);
583 return;
584 }
585 if (GNUNET_SYSERR ==
586 GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
587 &key,
588 &test_result_present,
589 (void *) uri))
590 return; /* duplicate result */
591 /* try to find search result in master map */
592 grc.sr = NULL;
593 grc.uri = uri;
594 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
595 &key,
596 &get_result_present, &grc);
597 sr = grc.sr;
598 is_new = (NULL == sr) || (sr->mandatory_missing > 0);
599 if (NULL == sr)
600 {
601 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
602 sr->h = sc->h;
603 sr->sc = sc;
604 sr->anonymity = sc->anonymity;
605 sr->uri = GNUNET_FS_uri_dup (uri);
606 sr->meta = GNUNET_FS_meta_data_duplicate (meta);
607 sr->mandatory_missing = sc->mandatory_count;
608 sr->key = key;
609 sr->keyword_bitmap = GNUNET_malloc ((sc->uri->data.ksk.keywordCount + 7)
610 / 8); /* round up, count bits */
611 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
612 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
613 }
614 else
615 {
616 GNUNET_FS_meta_data_merge (sr->meta, meta);
617 }
618 GNUNET_break (GNUNET_OK ==
619 GNUNET_CONTAINER_multihashmap_put (ent->results,
620 &sr->key,
621 sr,
622 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
623
624 koff = ent - sc->requests;
625 GNUNET_assert ((ent >= sc->requests) &&
626 (koff < sc->uri->data.ksk.keywordCount));
627 sr->keyword_bitmap[koff / 8] |= (1 << (koff % 8));
628 /* check if mandatory satisfied */
629 if (1 <= GNUNET_CONTAINER_multihashmap_size (ent->results))
630 {
631 if (ent->mandatory)
632 {
633 GNUNET_break (sr->mandatory_missing > 0);
634 sr->mandatory_missing--;
635 }
636 else
637 {
638 sr->optional_support++;
639 }
640 }
641 if (0 != sr->mandatory_missing)
642 {
643 GNUNET_break (NULL == sr->client_info);
644 return;
645 }
646 if (is_new)
647 notify_client_chk_result (sc, sr);
648 else
649 notify_client_chk_update (sc, sr);
650 GNUNET_FS_search_result_sync_ (sr);
651 GNUNET_FS_search_start_probe_ (sr);
652}
653
654
655/**
656 * Start search for content, internal API.
657 *
658 * @param h handle to the file sharing subsystem
659 * @param uri specifies the search parameters; can be
660 * a KSK URI or an SKS URI.
661 * @param anonymity desired level of anonymity
662 * @param options options for the search
663 * @param cctx client context
664 * @param psearch parent search result (for namespace update searches)
665 * @return context that can be used to control the search
666 */
667static struct GNUNET_FS_SearchContext *
668search_start (struct GNUNET_FS_Handle *h,
669 const struct GNUNET_FS_Uri *uri,
670 uint32_t anonymity,
671 enum GNUNET_FS_SearchOptions options,
672 void *cctx,
673 struct GNUNET_FS_SearchResult *psearch);
674
675
676/**
677 * We have received an SKS result. Start searching for updates and
678 * notify the client if it is a new result.
679 *
680 * @param sc context for the overall query
681 * @param id_update identifier for updates, NULL for none
682 * @param uri the URI that was found
683 * @param meta metadata associated with the URI
684 */
685static void
686process_sks_result (struct GNUNET_FS_SearchContext *sc,
687 const char *id_update,
688 const struct GNUNET_FS_Uri *uri,
689 const struct GNUNET_FS_MetaData *meta)
690{
691 struct GNUNET_FS_Uri uu;
692 struct GNUNET_HashCode key;
693 struct GNUNET_FS_SearchResult *sr;
694
695 /* check if new */
696 GNUNET_assert (NULL != sc);
697 if (GNUNET_OK !=
698 GNUNET_FS_uri_to_key (uri,
699 &key))
700 {
701 GNUNET_break (0);
702 return;
703 }
704 GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
705 &uri->data.chk.chk.query,
706 &key);
707 if (GNUNET_SYSERR ==
708 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map, &key,
709 &test_result_present,
710 (void *) uri))
711 return; /* duplicate result */
712 sr = GNUNET_new (struct GNUNET_FS_SearchResult);
713 sr->h = sc->h;
714 sr->sc = sc;
715 sr->anonymity = sc->anonymity;
716 sr->uri = GNUNET_FS_uri_dup (uri);
717 sr->meta = GNUNET_FS_meta_data_duplicate (meta);
718 sr->key = key;
719 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map, &key, sr,
720 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
721 GNUNET_FS_search_result_sync_ (sr);
722 GNUNET_FS_search_start_probe_ (sr);
723 /* notify client */
724 if (0 == sr->mandatory_missing)
725 notify_client_chk_result (sc, sr);
726 else
727 GNUNET_break (NULL == sr->client_info);
728 /* search for updates */
729 if (0 == strlen (id_update))
730 return; /* no updates */
731 uu.type = GNUNET_FS_URI_SKS;
732 uu.data.sks.ns = sc->uri->data.sks.ns;
733 uu.data.sks.identifier = GNUNET_strdup (id_update);
734 (void) search_start (sc->h, &uu, sc->anonymity, sc->options, NULL, sr);
735 GNUNET_free (uu.data.sks.identifier);
736}
737
738
739/**
740 * Decrypt a ublock using a 'keyword' as the passphrase. Given the
741 * KSK public key derived from the keyword, this function looks up
742 * the original keyword in the search context and decrypts the
743 * given ciphertext block.
744 *
745 * @param sc search context with the keywords
746 * @param dpub derived public key used for the search
747 * @param edata encrypted data
748 * @param edata_size number of bytes in @a edata (and @a data)
749 * @param data where to store the plaintext
750 * @return keyword index on success, #GNUNET_SYSERR on error (no such
751 * keyword, internal error)
752 */
753static int
754decrypt_block_with_keyword (const struct GNUNET_FS_SearchContext *sc,
755 const struct GNUNET_CRYPTO_EcdsaPublicKey *dpub,
756 const void *edata,
757 size_t edata_size,
758 char *data)
759{
760 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
761 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
762 unsigned int i;
763
764 /* find key */
765 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
766 if (0 == memcmp (dpub,
767 &sc->requests[i].dpub,
768 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
769 break;
770 if (i == sc->uri->data.ksk.keywordCount)
771 {
772 /* oops, does not match any of our keywords!? */
773 GNUNET_break (0);
774 return GNUNET_SYSERR;
775 }
776 /* decrypt */
777 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
778 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
779 GNUNET_FS_ublock_decrypt_ (edata, edata_size,
780 &anon_pub,
781 sc->requests[i].keyword,
782 data);
783 return i;
784}
785
786
787/**
788 * Process a keyword search result. The actual type of block is
789 * a UBlock; we know it is a keyword search result because that's
790 * what we were searching for.
791 *
792 * @param sc our search context
793 * @param ub the ublock with the keyword search result
794 * @param size size of @a ub
795 */
796static void
797process_kblock (struct GNUNET_FS_SearchContext *sc,
798 const struct UBlock *ub,
799 size_t size)
800{
801 size_t j;
802 char pt[size - sizeof(struct UBlock)];
803 const char *eos;
804 struct GNUNET_FS_MetaData *meta;
805 struct GNUNET_FS_Uri *uri;
806 char *emsg;
807 int i;
808
809 if (-1 == (i = decrypt_block_with_keyword (sc,
810 &ub->verification_key,
811 &ub[1],
812 size - sizeof(struct UBlock),
813 pt)))
814 return;
815 /* parse; pt[0] is just '\0', so we skip over that */
816 eos = memchr (&pt[1], '\0', sizeof(pt) - 1);
817 if (NULL == eos)
818 {
819 GNUNET_break_op (0);
820 return;
821 }
822 if (NULL == (uri = GNUNET_FS_uri_parse (&pt[1], &emsg)))
823 {
824 if (GNUNET_FS_VERSION > 0x00090400)
825 {
826 /* we broke this in 0x00090300, so don't bitch
827 too loudly just one version up... */
828 GNUNET_break_op (0); /* ublock malformed */
829 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
830 _ ("Failed to parse URI `%s': %s\n"),
831 &pt[1],
832 emsg);
833 }
834 GNUNET_free (emsg);
835 return;
836 }
837 j = eos - pt + 1;
838 if (sizeof(pt) == j)
839 meta = GNUNET_FS_meta_data_create ();
840 else
841 meta = GNUNET_FS_meta_data_deserialize (&pt[j], sizeof(pt) - j);
842 if (NULL == meta)
843 {
844 GNUNET_break_op (0); /* ublock malformed */
845 GNUNET_FS_uri_destroy (uri);
846 return;
847 }
848 process_ksk_result (sc,
849 &sc->requests[i],
850 uri,
851 meta);
852
853 /* clean up */
854 GNUNET_FS_meta_data_destroy (meta);
855 GNUNET_FS_uri_destroy (uri);
856}
857
858
859/**
860 * Process a namespace-search result. The actual type of block is
861 * a UBlock; we know it is a namespace search result because that's
862 * what we were searching for.
863 *
864 * @param sc our search context
865 * @param ub the ublock with a namespace result
866 * @param size size of @a ub
867 */
868static void
869process_sblock (struct GNUNET_FS_SearchContext *sc,
870 const struct UBlock *ub,
871 size_t size)
872{
873 size_t len = size - sizeof(struct UBlock);
874 char pt[len];
875 struct GNUNET_FS_Uri *uri;
876 struct GNUNET_FS_MetaData *meta;
877 const char *id;
878 const char *uris;
879 size_t off;
880 char *emsg;
881
882 GNUNET_FS_ublock_decrypt_ (&ub[1], len,
883 &sc->uri->data.sks.ns,
884 sc->uri->data.sks.identifier,
885 pt);
886 /* parse */
887 if (0 == (off = GNUNET_STRINGS_buffer_tokenize (pt, len, 2, &id, &uris)))
888 {
889 GNUNET_break_op (0); /* ublock malformed */
890 return;
891 }
892 if (NULL == (meta = GNUNET_FS_meta_data_deserialize (&pt[off], len
893 - off)))
894 {
895 GNUNET_break_op (0); /* ublock malformed */
896 return;
897 }
898 if (NULL == (uri = GNUNET_FS_uri_parse (uris, &emsg)))
899 {
900 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
901 _ ("Failed to parse URI `%s': %s\n"),
902 uris, emsg);
903 GNUNET_break_op (0); /* ublock malformed */
904 GNUNET_free (emsg);
905 GNUNET_FS_meta_data_destroy (meta);
906 return;
907 }
908 /* process */
909 process_sks_result (sc, id, uri, meta);
910 /* clean up */
911 GNUNET_FS_uri_destroy (uri);
912 GNUNET_FS_meta_data_destroy (meta);
913}
914
915
916/**
917 * Shutdown any existing connection to the FS
918 * service and try to establish a fresh one
919 * (and then re-transmit our search request).
920 *
921 * @param sc the search to reconnec
922 */
923static void
924try_reconnect (struct GNUNET_FS_SearchContext *sc);
925
926
927/**
928 * We check a result message from the service.
929 *
930 * @param cls closure
931 * @param cm result message received
932 */
933static int
934check_result (void *cls,
935 const struct ClientPutMessage *cm)
936{
937 /* payload of any variable size is OK */
938 return GNUNET_OK;
939}
940
941
942/**
943 * We process a search result from the service.
944 *
945 * @param cls closure
946 * @param cm result message received
947 */
948static void
949handle_result (void *cls,
950 const struct ClientPutMessage *cm)
951{
952 struct GNUNET_FS_SearchContext *sc = cls;
953 uint16_t msize = ntohs (cm->header.size) - sizeof(*cm);
954 enum GNUNET_BLOCK_Type type = ntohl (cm->type);
955
956 if (GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh (
957 cm->expiration)).rel_value_us > 0)
958 {
959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
960 "Result received has already expired.\n");
961 return; /* result expired */
962 }
963 switch (type)
964 {
965 case GNUNET_BLOCK_TYPE_FS_UBLOCK:
966 if (GNUNET_FS_URI_SKS == sc->uri->type)
967 process_sblock (sc,
968 (const struct UBlock *) &cm[1],
969 msize);
970 else
971 process_kblock (sc,
972 (const struct UBlock *) &cm[1],
973 msize);
974 break;
975
976 case GNUNET_BLOCK_TYPE_ANY:
977 GNUNET_break (0);
978 break;
979
980 case GNUNET_BLOCK_TYPE_FS_DBLOCK:
981 GNUNET_break (0);
982 break;
983
984 case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
985 GNUNET_break (0);
986 break;
987
988 case GNUNET_BLOCK_TYPE_FS_IBLOCK:
989 GNUNET_break (0);
990 break;
991
992 default:
993 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
994 _ ("Got result with unknown block type `%d', ignoring"),
995 type);
996 break;
997 }
998}
999
1000
1001/**
1002 * Schedule the transmission of the (next) search request
1003 * to the service.
1004 *
1005 * @param sc context for the search
1006 */
1007static void
1008schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
1009
1010
1011/**
1012 * Closure for #build_result_set().
1013 */
1014struct MessageBuilderContext
1015{
1016 /**
1017 * How many entries can we store to xoff.
1018 */
1019 unsigned int put_cnt;
1020
1021 /**
1022 * How many entries should we skip.
1023 */
1024 unsigned int skip_cnt;
1025
1026 /**
1027 * Where to store the keys.
1028 */
1029 struct GNUNET_HashCode *xoff;
1030
1031 /**
1032 * Search context we are iterating for.
1033 */
1034 struct GNUNET_FS_SearchContext *sc;
1035
1036 /**
1037 * Keyword offset the search result must match (0 for SKS)
1038 */
1039 unsigned int keyword_offset;
1040};
1041
1042
1043/**
1044 * Iterating over the known results, pick those matching the given
1045 * result range and store their keys at 'xoff'.
1046 *
1047 * @param cls the `struct MessageBuilderContext`
1048 * @param key key for a result
1049 * @param value the search result
1050 * @return #GNUNET_OK to continue iterating
1051 */
1052static int
1053build_result_set (void *cls,
1054 const struct GNUNET_HashCode *key,
1055 void *value)
1056{
1057 struct MessageBuilderContext *mbc = cls;
1058 struct GNUNET_FS_SearchResult *sr = value;
1059
1060 if ((NULL != sr->keyword_bitmap) &&
1061 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1
1062 << (mbc->
1063 keyword_offset
1064 % 8)))))
1065 return GNUNET_OK; /* have no match for this keyword yet */
1066 if (mbc->skip_cnt > 0)
1067 {
1068 mbc->skip_cnt--;
1069 return GNUNET_OK;
1070 }
1071 if (0 == mbc->put_cnt)
1072 return GNUNET_SYSERR;
1073 mbc->xoff[--mbc->put_cnt] = *key;
1074
1075 return GNUNET_OK;
1076}
1077
1078
1079/**
1080 * Iterating over the known results, count those matching the given
1081 * result range and increment put count for each.
1082 *
1083 * @param cls the `struct MessageBuilderContext`
1084 * @param key key for a result
1085 * @param value the search result
1086 * @return #GNUNET_OK to continue iterating
1087 */
1088static int
1089find_result_set (void *cls,
1090 const struct GNUNET_HashCode *key,
1091 void *value)
1092{
1093 struct MessageBuilderContext *mbc = cls;
1094 struct GNUNET_FS_SearchResult *sr = value;
1095
1096 if ((NULL != sr->keyword_bitmap) &&
1097 (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1
1098 << (mbc->
1099 keyword_offset
1100 % 8)))))
1101 return GNUNET_OK; /* have no match for this keyword yet */
1102 mbc->put_cnt++;
1103 return GNUNET_OK;
1104}
1105
1106
1107/**
1108 * Schedule the transmission of the (next) search request
1109 * to the service.
1110 *
1111 * @param sc context for the search
1112 */
1113static void
1114schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc)
1115{
1116 struct MessageBuilderContext mbc;
1117 struct GNUNET_MQ_Envelope *env;
1118 struct SearchMessage *sm;
1119 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
1120 unsigned int total_seen_results; /* total number of result hashes to send */
1121 uint32_t options;
1122 unsigned int left;
1123 unsigned int todo;
1124 unsigned int fit;
1125 unsigned int search_request_map_offset;
1126 unsigned int keyword_offset;
1127 int first_call;
1128
1129 memset (&mbc, 0, sizeof(mbc));
1130 mbc.sc = sc;
1131 if (GNUNET_FS_uri_test_ksk (sc->uri))
1132 {
1133 /* This will calculate the result set size ONLY for
1134 "keyword_offset == 0", so we will have to recalculate
1135 it for the other keywords later! */
1136 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1137 &find_result_set,
1138 &mbc);
1139 total_seen_results = mbc.put_cnt;
1140 }
1141 else
1142 {
1143 total_seen_results
1144 = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
1145 }
1146 search_request_map_offset = 0;
1147 keyword_offset = 0;
1148 first_call = GNUNET_YES;
1149 while ((0 != (left =
1150 (total_seen_results - search_request_map_offset))) ||
1151 (GNUNET_YES == first_call))
1152 {
1153 first_call = GNUNET_NO;
1154 options = SEARCH_MESSAGE_OPTION_NONE;
1155 if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1156 options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY;
1157
1158 fit = (GNUNET_MAX_MESSAGE_SIZE - 1 - sizeof(*sm)) / sizeof(struct
1159 GNUNET_HashCode);
1160 todo = GNUNET_MIN (fit,
1161 left);
1162 env = GNUNET_MQ_msg_extra (sm,
1163 sizeof(struct GNUNET_HashCode) * todo,
1164 GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1165 mbc.skip_cnt = search_request_map_offset;
1166 mbc.xoff = (struct GNUNET_HashCode *) &sm[1];
1167 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK);
1168 sm->anonymity_level = htonl (sc->anonymity);
1169 memset (&sm->target,
1170 0,
1171 sizeof(struct GNUNET_PeerIdentity));
1172
1173 if (GNUNET_FS_uri_test_ksk (sc->uri))
1174 {
1175 mbc.keyword_offset = keyword_offset;
1176 /* calculate how many results we can send in this message */
1177 mbc.put_cnt = todo;
1178 /* now build message */
1179 sm->query = sc->requests[keyword_offset].uquery;
1180 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1181 &build_result_set,
1182 &mbc);
1183 search_request_map_offset += todo;
1184 GNUNET_assert (0 == mbc.put_cnt);
1185 GNUNET_assert (total_seen_results >= search_request_map_offset);
1186 if (total_seen_results != search_request_map_offset)
1187 {
1188 /* more requesting to be done... */
1189 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1190 }
1191 else
1192 {
1193 sm->options = htonl (options);
1194 keyword_offset++;
1195 if (sc->uri->data.ksk.keywordCount != keyword_offset)
1196 {
1197 /* more keywords => more requesting to be done... */
1198 first_call = GNUNET_YES;
1199 search_request_map_offset = 0;
1200 mbc.put_cnt = 0;
1201 mbc.keyword_offset = keyword_offset;
1202 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1203 &find_result_set,
1204 &mbc);
1205 total_seen_results = mbc.put_cnt;
1206 }
1207 }
1208 }
1209 else
1210 {
1211 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1212
1213 GNUNET_CRYPTO_ecdsa_public_key_derive (&sc->uri->data.sks.ns,
1214 sc->uri->data.sks.identifier,
1215 "fs-ublock",
1216 &dpub);
1217 GNUNET_CRYPTO_hash (&dpub,
1218 sizeof(dpub),
1219 &sm->query);
1220 mbc.put_cnt = todo;
1221 mbc.keyword_offset = 0;
1222 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1223 &build_result_set,
1224 &mbc);
1225 GNUNET_assert (total_seen_results >= search_request_map_offset);
1226 if (total_seen_results != search_request_map_offset)
1227 {
1228 /* more requesting to be done... */
1229 sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1230 }
1231 else
1232 {
1233 sm->options = htonl (options);
1234 }
1235 }
1236 GNUNET_MQ_send (sc->mq,
1237 env);
1238 }
1239}
1240
1241
1242/**
1243 * Generic error handler, called with the appropriate error code and
1244 * the same closure specified at the creation of the message queue.
1245 * Not every message queue implementation supports an error handler.
1246 *
1247 * @param cls closure with the `struct GNUNET_FS_SearchContext *`
1248 * @param error error code
1249 */
1250static void
1251search_mq_error_handler (void *cls,
1252 enum GNUNET_MQ_Error error)
1253{
1254 struct GNUNET_FS_SearchContext *sc = cls;
1255
1256 if (NULL != sc->mq)
1257 {
1258 GNUNET_MQ_destroy (sc->mq);
1259 sc->mq = NULL;
1260 }
1261 try_reconnect (sc);
1262}
1263
1264
1265/**
1266 * Reconnect to the FS service and transmit
1267 * our queries NOW.
1268 *
1269 * @param cls our search context
1270 */
1271static void
1272do_reconnect (void *cls)
1273{
1274 struct GNUNET_FS_SearchContext *sc = cls;
1275 struct GNUNET_MQ_MessageHandler handlers[] = {
1276 GNUNET_MQ_hd_var_size (result,
1277 GNUNET_MESSAGE_TYPE_FS_PUT,
1278 struct ClientPutMessage,
1279 sc),
1280 GNUNET_MQ_handler_end ()
1281 };
1282
1283 sc->task = NULL;
1284 sc->mq = GNUNET_CLIENT_connect (sc->h->cfg,
1285 "fs",
1286 handlers,
1287 &search_mq_error_handler,
1288 sc);
1289 if (NULL == sc->mq)
1290 {
1291 try_reconnect (sc);
1292 return;
1293 }
1294 schedule_transmit_search_request (sc);
1295}
1296
1297
1298/**
1299 * Shutdown any existing connection to the FS
1300 * service and try to establish a fresh one
1301 * (and then re-transmit our search request).
1302 *
1303 * @param sc the search to reconnec
1304 */
1305static void
1306try_reconnect (struct GNUNET_FS_SearchContext *sc)
1307{
1308 if (NULL != sc->mq)
1309 {
1310 GNUNET_MQ_destroy (sc->mq);
1311 sc->mq = NULL;
1312 }
1313 sc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (sc->reconnect_backoff);
1314 sc->task =
1315 GNUNET_SCHEDULER_add_delayed (sc->reconnect_backoff,
1316 &do_reconnect,
1317 sc);
1318}
1319
1320
1321/**
1322 * Start search for content, internal API.
1323 *
1324 * @param h handle to the file sharing subsystem
1325 * @param uri specifies the search parameters; can be
1326 * a KSK URI or an SKS URI.
1327 * @param anonymity desired level of anonymity
1328 * @param options options for the search
1329 * @param cctx initial value for the client context
1330 * @param psearch parent search result (for namespace update searches)
1331 * @return context that can be used to control the search
1332 */
1333static struct GNUNET_FS_SearchContext *
1334search_start (struct GNUNET_FS_Handle *h,
1335 const struct GNUNET_FS_Uri *uri,
1336 uint32_t anonymity,
1337 enum GNUNET_FS_SearchOptions options,
1338 void *cctx,
1339 struct GNUNET_FS_SearchResult *psearch)
1340{
1341 struct GNUNET_FS_SearchContext *sc;
1342 struct GNUNET_FS_ProgressInfo pi;
1343
1344 sc = GNUNET_new (struct GNUNET_FS_SearchContext);
1345 sc->h = h;
1346 sc->options = options;
1347 sc->uri = GNUNET_FS_uri_dup (uri);
1348 sc->anonymity = anonymity;
1349 sc->start_time = GNUNET_TIME_absolute_get ();
1350 if (NULL != psearch)
1351 {
1352 sc->psearch_result = psearch;
1353 psearch->update_search = sc;
1354 }
1355 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
1356 sc->client_info = cctx;
1357 if (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))
1358 {
1359 GNUNET_FS_uri_destroy (sc->uri);
1360 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1361 GNUNET_free (sc);
1362 return NULL;
1363 }
1364 GNUNET_FS_search_sync_ (sc);
1365 pi.status = GNUNET_FS_STATUS_SEARCH_START;
1366 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1367 return sc;
1368}
1369
1370
1371/**
1372 * Update the 'results' map for the individual keywords with the
1373 * results from the 'global' result set.
1374 *
1375 * @param cls closure, the `struct GNUNET_FS_SearchContext *`
1376 * @param key current key code
1377 * @param value value in the hash map, the `struct GNUNET_FS_SearchResult *`
1378 * @return #GNUNET_YES (we should continue to iterate)
1379 */
1380static int
1381update_sre_result_maps (void *cls,
1382 const struct GNUNET_HashCode *key,
1383 void *value)
1384{
1385 struct GNUNET_FS_SearchContext *sc = cls;
1386 struct GNUNET_FS_SearchResult *sr = value;
1387
1388 for (unsigned int i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1389 {
1390 if (0 != (sr->keyword_bitmap[i / 8] & (1 << (i % 8))))
1391 GNUNET_break (GNUNET_OK ==
1392 GNUNET_CONTAINER_multihashmap_put (sc->requests[i].results,
1393 &sr->key,
1394 sr,
1395 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1396 }
1397 return GNUNET_YES;
1398}
1399
1400
1401int
1402GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1403{
1404 unsigned int i;
1405 const char *keyword;
1406 const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
1407 struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
1408 struct SearchRequestEntry *sre;
1409
1410 GNUNET_assert (NULL == sc->mq);
1411 if (GNUNET_FS_uri_test_ksk (sc->uri))
1412 {
1413 GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1414 anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
1415 GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
1416 sc->requests
1417 = GNUNET_new_array (sc->uri->data.ksk.keywordCount,
1418 struct SearchRequestEntry);
1419
1420 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1421 {
1422 keyword = &sc->uri->data.ksk.keywords[i][1];
1423 sre = &sc->requests[i];
1424 sre->keyword = GNUNET_strdup (keyword);
1425 GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
1426 keyword,
1427 "fs-ublock",
1428 &sre->dpub);
1429 GNUNET_CRYPTO_hash (&sre->dpub,
1430 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
1431 &sre->uquery);
1432 sre->mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1433 if (sre->mandatory)
1434 sc->mandatory_count++;
1435 sre->results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
1436 }
1437 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1438 &update_sre_result_maps,
1439 sc);
1440 }
1441 GNUNET_assert (NULL == sc->task);
1442 do_reconnect (sc);
1443 if (NULL == sc->mq)
1444 {
1445 GNUNET_SCHEDULER_cancel (sc->task);
1446 sc->task = NULL;
1447 return GNUNET_SYSERR;
1448 }
1449 return GNUNET_OK;
1450}
1451
1452
1453/**
1454 * Freeze probes for the given search result.
1455 *
1456 * @param cls the global FS handle
1457 * @param key the key for the search result (unused)
1458 * @param value the search result to free
1459 * @return #GNUNET_OK
1460 */
1461static int
1462search_result_freeze_probes (void *cls,
1463 const struct GNUNET_HashCode *key,
1464 void *value)
1465{
1466 struct GNUNET_FS_SearchResult *sr = value;
1467
1468 if (NULL != sr->probe_ctx)
1469 {
1470 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1471 sr->probe_ctx = NULL;
1472 GNUNET_FS_stop_probe_ping_task_ (sr);
1473 }
1474 if (NULL != sr->probe_cancel_task)
1475 {
1476 GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1477 sr->probe_cancel_task = NULL;
1478 }
1479 if (NULL != sr->update_search)
1480 GNUNET_FS_search_pause (sr->update_search);
1481 return GNUNET_OK;
1482}
1483
1484
1485/**
1486 * Resume probes for the given search result.
1487 *
1488 * @param cls the global FS handle
1489 * @param key the key for the search result (unused)
1490 * @param value the search result to free
1491 * @return #GNUNET_OK
1492 */
1493static int
1494search_result_resume_probes (void *cls,
1495 const struct GNUNET_HashCode *key,
1496 void *value)
1497{
1498 struct GNUNET_FS_SearchResult *sr = value;
1499
1500 GNUNET_FS_search_start_probe_ (sr);
1501 if (NULL != sr->update_search)
1502 GNUNET_FS_search_continue (sr->update_search);
1503 return GNUNET_OK;
1504}
1505
1506
1507/**
1508 * Signal suspend and free the given search result.
1509 *
1510 * @param cls the global FS handle
1511 * @param key the key for the search result (unused)
1512 * @param value the search result to free
1513 * @return #GNUNET_OK
1514 */
1515static int
1516search_result_suspend (void *cls,
1517 const struct GNUNET_HashCode *key,
1518 void *value)
1519{
1520 struct GNUNET_FS_SearchContext *sc = cls;
1521 struct GNUNET_FS_SearchResult *sr = value;
1522 struct GNUNET_FS_ProgressInfo pi;
1523
1524 if (NULL != sr->download)
1525 {
1526 GNUNET_FS_download_signal_suspend_ (sr->download);
1527 sr->download = NULL;
1528 }
1529 if (NULL != sr->update_search)
1530 {
1531 GNUNET_FS_search_signal_suspend_ (sr->update_search);
1532 sr->update_search = NULL;
1533 }
1534 GNUNET_FS_search_stop_probe_ (sr);
1535 if (0 == sr->mandatory_missing)
1536 {
1537 /* client is aware of search result, notify about suspension event */
1538 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1539 pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1540 pi.value.search.specifics.result_suspend.meta = sr->meta;
1541 pi.value.search.specifics.result_suspend.uri = sr->uri;
1542 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1543 }
1544 GNUNET_break (NULL == sr->client_info);
1545 GNUNET_free (sr->serialization);
1546 GNUNET_FS_uri_destroy (sr->uri);
1547 GNUNET_FS_meta_data_destroy (sr->meta);
1548 GNUNET_free (sr->keyword_bitmap);
1549 GNUNET_free (sr);
1550 return GNUNET_OK;
1551}
1552
1553
1554void
1555GNUNET_FS_search_signal_suspend_ (void *cls)
1556{
1557 struct GNUNET_FS_SearchContext *sc = cls;
1558 struct GNUNET_FS_ProgressInfo pi;
1559 unsigned int i;
1560
1561 GNUNET_FS_end_top (sc->h, sc->top);
1562 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1563 &search_result_suspend, sc);
1564 pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1565 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1566 GNUNET_break (NULL == sc->client_info);
1567 if (sc->task != NULL)
1568 {
1569 GNUNET_SCHEDULER_cancel (sc->task);
1570 sc->task = NULL;
1571 }
1572 if (NULL != sc->mq)
1573 {
1574 GNUNET_MQ_destroy (sc->mq);
1575 sc->mq = NULL;
1576 }
1577 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1578 if (NULL != sc->requests)
1579 {
1580 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1581 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1582 {
1583 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1584 GNUNET_free (sc->requests[i].keyword);
1585 }
1586 }
1587 GNUNET_free (sc->requests);
1588 GNUNET_free (sc->emsg);
1589 GNUNET_FS_uri_destroy (sc->uri);
1590 GNUNET_free (sc->serialization);
1591 GNUNET_free (sc);
1592}
1593
1594
1595/**
1596 * Start search for content.
1597 *
1598 * @param h handle to the file sharing subsystem
1599 * @param uri specifies the search parameters; can be
1600 * a KSK URI or an SKS URI.
1601 * @param anonymity desired level of anonymity
1602 * @param options options for the search
1603 * @param cctx initial value for the client context
1604 * @return context that can be used to control the search
1605 */
1606struct GNUNET_FS_SearchContext *
1607GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1608 const struct GNUNET_FS_Uri *uri, uint32_t anonymity,
1609 enum GNUNET_FS_SearchOptions options, void *cctx)
1610{
1611 struct GNUNET_FS_SearchContext *ret;
1612
1613 ret = search_start (h, uri, anonymity, options, cctx, NULL);
1614 if (NULL == ret)
1615 return NULL;
1616 ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1617 return ret;
1618}
1619
1620
1621/**
1622 * Pause search.
1623 *
1624 * @param sc context for the search that should be paused
1625 */
1626void
1627GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1628{
1629 struct GNUNET_FS_ProgressInfo pi;
1630
1631 if (NULL != sc->task)
1632 {
1633 GNUNET_SCHEDULER_cancel (sc->task);
1634 sc->task = NULL;
1635 }
1636 if (NULL != sc->mq)
1637 {
1638 GNUNET_MQ_destroy (sc->mq);
1639 sc->mq = NULL;
1640 }
1641 GNUNET_FS_search_sync_ (sc);
1642 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1643 &search_result_freeze_probes,
1644 sc);
1645 pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1646 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1647 sc->h,
1648 sc);
1649}
1650
1651
1652/**
1653 * Continue paused search.
1654 *
1655 * @param sc context for the search that should be resumed
1656 */
1657void
1658GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1659{
1660 struct GNUNET_FS_ProgressInfo pi;
1661
1662 GNUNET_assert (NULL == sc->mq);
1663 GNUNET_assert (NULL == sc->task);
1664 do_reconnect (sc);
1665 GNUNET_FS_search_sync_ (sc);
1666 pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1667 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1668 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1669 &search_result_resume_probes, sc);
1670}
1671
1672
1673/**
1674 * Signal stop for the given search result.
1675 *
1676 * @param cls the global FS handle
1677 * @param key the key for the search result (unused)
1678 * @param value the search result to free
1679 * @return #GNUNET_OK
1680 */
1681static int
1682search_result_stop (void *cls,
1683 const struct GNUNET_HashCode *key,
1684 void *value)
1685{
1686 struct GNUNET_FS_SearchContext *sc = cls;
1687 struct GNUNET_FS_SearchResult *sr = value;
1688 struct GNUNET_FS_ProgressInfo pi;
1689
1690 GNUNET_FS_search_stop_probe_ (sr);
1691 if (NULL != sr->download)
1692 {
1693 sr->download->search = NULL;
1694 sr->download->top
1695 = GNUNET_FS_make_top (sr->download->h,
1696 &GNUNET_FS_download_signal_suspend_,
1697 sr->download);
1698 if (NULL != sr->download->serialization)
1699 {
1700 GNUNET_FS_remove_sync_file_ (sc->h,
1701 GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1702 sr->download->serialization);
1703 GNUNET_free (sr->download->serialization);
1704 sr->download->serialization = NULL;
1705 }
1706 pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1707 GNUNET_FS_download_make_status_ (&pi,
1708 sr->download);
1709 GNUNET_FS_download_sync_ (sr->download);
1710 sr->download = NULL;
1711 }
1712 if (0 != sr->mandatory_missing)
1713 {
1714 /* client is unaware of search result as
1715 it does not match required keywords */
1716 GNUNET_break (NULL == sr->client_info);
1717 return GNUNET_OK;
1718 }
1719 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1720 pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1721 pi.value.search.specifics.result_stopped.meta = sr->meta;
1722 pi.value.search.specifics.result_stopped.uri = sr->uri;
1723 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sc);
1724 return GNUNET_OK;
1725}
1726
1727
1728/**
1729 * Free the given search result.
1730 *
1731 * @param cls the global FS handle
1732 * @param key the key for the search result (unused)
1733 * @param value the search result to free
1734 * @return #GNUNET_OK
1735 */
1736static int
1737search_result_free (void *cls,
1738 const struct GNUNET_HashCode *key,
1739 void *value)
1740{
1741 struct GNUNET_FS_SearchResult *sr = value;
1742
1743 if (NULL != sr->update_search)
1744 {
1745 GNUNET_FS_search_stop (sr->update_search);
1746 GNUNET_assert (NULL == sr->update_search);
1747 }
1748 GNUNET_break (NULL == sr->probe_ctx);
1749 GNUNET_break (NULL == sr->probe_cancel_task);
1750 GNUNET_break (NULL == sr->client_info);
1751 GNUNET_free (sr->serialization);
1752 GNUNET_FS_uri_destroy (sr->uri);
1753 GNUNET_FS_meta_data_destroy (sr->meta);
1754 GNUNET_free (sr->keyword_bitmap);
1755 GNUNET_free (sr);
1756 return GNUNET_OK;
1757}
1758
1759
1760/**
1761 * Stop search for content.
1762 *
1763 * @param sc context for the search that should be stopped
1764 */
1765void
1766GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1767{
1768 struct GNUNET_FS_ProgressInfo pi;
1769 unsigned int i;
1770
1771 if (NULL != sc->top)
1772 GNUNET_FS_end_top (sc->h, sc->top);
1773 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1774 &search_result_stop,
1775 sc);
1776 if (NULL != sc->psearch_result)
1777 sc->psearch_result->update_search = NULL;
1778 if (NULL != sc->serialization)
1779 {
1780 GNUNET_FS_remove_sync_file_ (sc->h,
1781 (NULL != sc->psearch_result)
1782 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1783 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1784 sc->serialization);
1785 GNUNET_FS_remove_sync_dir_ (sc->h,
1786 (NULL != sc->psearch_result)
1787 ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH
1788 : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1789 sc->serialization);
1790 GNUNET_free (sc->serialization);
1791 }
1792 pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1793 sc->client_info = GNUNET_FS_search_make_status_ (&pi,
1794 sc->h,
1795 sc);
1796 GNUNET_break (NULL == sc->client_info);
1797 if (NULL != sc->task)
1798 {
1799 GNUNET_SCHEDULER_cancel (sc->task);
1800 sc->task = NULL;
1801 }
1802 if (NULL != sc->mq)
1803 {
1804 GNUNET_MQ_destroy (sc->mq);
1805 sc->mq = NULL;
1806 }
1807 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1808 &search_result_free, sc);
1809 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1810 if (NULL != sc->requests)
1811 {
1812 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1813 for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1814 {
1815 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1816 GNUNET_free (sc->requests[i].keyword);
1817 }
1818 }
1819 GNUNET_free (sc->requests);
1820 GNUNET_free (sc->emsg);
1821 GNUNET_FS_uri_destroy (sc->uri);
1822 GNUNET_free (sc);
1823}
1824
1825
1826/* end of fs_search.c */