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