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