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