-fix, handle case where there is no update
[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);
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)
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_malloc (sizeof (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_malloc (sizeof (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 'edata' (and '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_EccPublicKey *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_EccPublicKey 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_EccPublicKey)))
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 (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, const struct UBlock *ub,
725                 size_t size)
726 {
727   size_t j;
728   char pt[size - sizeof (struct UBlock)];
729   const char *eos;
730   struct GNUNET_CONTAINER_MetaData *meta;
731   struct GNUNET_FS_Uri *uri;
732   char *emsg;
733   int i;
734
735   if (-1 == (i = decrypt_block_with_keyword (sc,
736                                              &ub->verification_key,
737                                              &ub[1],
738                                              size - sizeof (struct UBlock),
739                                              pt)))
740     return;
741   /* parse; pt[0] is just '\0', so we skip over that */
742   eos = memchr (&pt[1], '\0', sizeof (pt) - 1);
743   if (NULL == eos)
744   {
745     GNUNET_break_op (0);
746     return;
747   }
748   if (NULL == (uri = GNUNET_FS_uri_parse (&pt[1], &emsg)))
749   {
750     GNUNET_break_op (0);        /* ublock malformed */
751     GNUNET_free_non_null (emsg);   
752     return;
753   }
754   j = eos - pt + 1;
755   if (sizeof (pt) == j)
756     meta = GNUNET_CONTAINER_meta_data_create ();
757   else
758     meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j], sizeof (pt) - j);
759   if (NULL == meta)
760   {
761     GNUNET_break_op (0);        /* ublock malformed */
762     GNUNET_FS_uri_destroy (uri);
763     return;
764   }
765   process_ksk_result (sc, &sc->requests[i], uri, meta);
766
767   /* clean up */
768   GNUNET_CONTAINER_meta_data_destroy (meta);
769   GNUNET_FS_uri_destroy (uri);
770 }
771
772
773 /**
774  * Process a namespace-search result.  The actual type of block is
775  * a UBlock; we know it is a namespace search result because that's
776  * what we were searching for.
777  *
778  * @param sc our search context
779  * @param ub the ublock with a namespace result
780  * @param size size of sb
781  */
782 static void
783 process_sblock (struct GNUNET_FS_SearchContext *sc, 
784                 const struct UBlock *ub,
785                 size_t size)
786 {
787   size_t len = size - sizeof (struct UBlock);
788   char pt[len];
789   struct GNUNET_FS_Uri *uri;
790   struct GNUNET_CONTAINER_MetaData *meta;
791   const char *id;
792   const char *uris;
793   size_t off;
794   char *emsg;
795
796   /* decrypt */
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, enum GNUNET_BLOCK_Type type,
841                 struct GNUNET_TIME_Absolute expiration, 
842                 const void *data,
843                 size_t size)
844 {
845   if (GNUNET_TIME_absolute_get_duration (expiration).rel_value > 0)
846   {
847     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
848                 "Result received has already expired.\n");
849     return;                     /* result expired */
850   }
851   switch (type)
852   {
853   case GNUNET_BLOCK_TYPE_FS_UBLOCK:
854     if (GNUNET_FS_URI_SKS == sc->uri->type)
855       process_sblock (sc, data, size);
856     else
857       process_kblock (sc, data, size);
858     break;
859   case GNUNET_BLOCK_TYPE_ANY:
860     GNUNET_break (0);
861     break;
862   case GNUNET_BLOCK_TYPE_FS_DBLOCK:
863     GNUNET_break (0);
864     break;
865   case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
866     GNUNET_break (0);
867     break;
868   case GNUNET_BLOCK_TYPE_FS_IBLOCK:
869     GNUNET_break (0);
870     break;
871   default:
872     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
873                 _("Got result with unknown block type `%d', ignoring"), type);
874     break;
875   }
876 }
877
878
879 /**
880  * Shutdown any existing connection to the FS
881  * service and try to establish a fresh one
882  * (and then re-transmit our search request).
883  *
884  * @param sc the search to reconnec
885  */
886 static void
887 try_reconnect (struct GNUNET_FS_SearchContext *sc);
888
889
890 /**
891  * Type of a function to call when we receive a message
892  * from the service.
893  *
894  * @param cls closure
895  * @param msg message received, NULL on timeout or fatal error
896  */
897 static void
898 receive_results (void *cls, const struct GNUNET_MessageHeader *msg)
899 {
900   struct GNUNET_FS_SearchContext *sc = cls;
901   const struct ClientPutMessage *cm;
902   uint16_t msize;
903
904   if ((NULL == msg) || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
905       (ntohs (msg->size) <= sizeof (struct ClientPutMessage)))
906   {
907     try_reconnect (sc);
908     return;
909   }
910   msize = ntohs (msg->size);
911   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
912               "Receiving %u bytes of result from fs service\n", msize);
913   cm = (const struct ClientPutMessage *) msg;
914   process_result (sc, ntohl (cm->type),
915                   GNUNET_TIME_absolute_ntoh (cm->expiration), &cm[1],
916                   msize - sizeof (struct ClientPutMessage));
917   /* continue receiving */
918   GNUNET_CLIENT_receive (sc->client, &receive_results, sc,
919                          GNUNET_TIME_UNIT_FOREVER_REL);
920 }
921
922
923 /**
924  * Schedule the transmission of the (next) search request
925  * to the service.
926  *
927  * @param sc context for the search
928  */
929 static void
930 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
931
932
933 /**
934  * Closure for 'build_result_set'.
935  */
936 struct MessageBuilderContext
937 {
938   /**
939    * How many entries can we store to xoff.
940    */
941   unsigned int put_cnt;
942
943   /**
944    * How many entries should we skip.
945    */
946   unsigned int skip_cnt;
947
948   /**
949    * Where to store the keys.
950    */
951   struct GNUNET_HashCode *xoff;
952
953   /**
954    * Search context we are iterating for.
955    */
956   struct GNUNET_FS_SearchContext *sc;
957
958   /**
959    * Keyword offset the search result must match (0 for SKS)
960    */
961   unsigned int keyword_offset;
962 };
963
964
965 /**
966  * Iterating over the known results, pick those matching the given
967  * result range and store their keys at 'xoff'.
968  *
969  * @param cls the 'struct MessageBuilderContext'
970  * @param key key for a result
971  * @param value the search result
972  * @return GNUNET_OK to continue iterating
973  */
974 static int
975 build_result_set (void *cls, const struct GNUNET_HashCode * key, void *value)
976 {
977   struct MessageBuilderContext *mbc = cls;
978   struct GNUNET_FS_SearchResult *sr = value;
979
980   if ( (NULL != sr->keyword_bitmap) &&
981        (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
982     return GNUNET_OK; /* have no match for this keyword yet */
983   if (mbc->skip_cnt > 0)
984   {
985     mbc->skip_cnt--;
986     return GNUNET_OK;
987   }
988   if (0 == mbc->put_cnt)
989     return GNUNET_SYSERR;
990   mbc->sc->search_request_map_offset++;
991   mbc->xoff[--mbc->put_cnt] = *key;
992
993   return GNUNET_OK;
994 }
995
996
997 /**
998  * Iterating over the known results, count those
999  * matching the given result range and increment
1000  * put count for each.
1001  *
1002  * @param cls the 'struct MessageBuilderContext'
1003  * @param key key for a result
1004  * @param value the search result
1005  * @return GNUNET_OK to continue iterating
1006  */
1007 static int
1008 find_result_set (void *cls, const struct GNUNET_HashCode * key, void *value)
1009 {
1010   struct MessageBuilderContext *mbc = cls;
1011   struct GNUNET_FS_SearchResult *sr = value;
1012
1013   if ( (NULL != sr->keyword_bitmap) &&
1014        (0 == (sr->keyword_bitmap[mbc->keyword_offset / 8] & (1 << (mbc->keyword_offset % 8)))) )
1015     return GNUNET_OK; /* have no match for this keyword yet */
1016   mbc->put_cnt++;
1017   return GNUNET_OK;
1018 }
1019
1020
1021 /**
1022  * We're ready to transmit the search request to the
1023  * file-sharing service.  Do it.
1024  *
1025  * @param cls closure
1026  * @param size number of bytes available in buf
1027  * @param buf where the callee should write the message
1028  * @return number of bytes written to buf
1029  */
1030 static size_t
1031 transmit_search_request (void *cls, size_t size, void *buf)
1032 {
1033   struct GNUNET_FS_SearchContext *sc = cls;
1034   struct MessageBuilderContext mbc;
1035   size_t msize;
1036   struct SearchMessage *sm;
1037   struct GNUNET_CRYPTO_EccPublicKey dpub;
1038   unsigned int sqms;
1039   uint32_t options;
1040
1041   if (NULL == buf)
1042   {
1043     try_reconnect (sc);
1044     return 0;
1045   }
1046   mbc.sc = sc;
1047   mbc.skip_cnt = sc->search_request_map_offset;
1048   sm = buf;
1049   sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1050   mbc.xoff = (struct GNUNET_HashCode *) & sm[1];
1051   options = SEARCH_MESSAGE_OPTION_NONE;
1052   if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1053     options |= SEARCH_MESSAGE_OPTION_LOOPBACK_ONLY;
1054   if (GNUNET_FS_uri_test_ksk (sc->uri))
1055   {
1056     msize = sizeof (struct SearchMessage);
1057     GNUNET_assert (size >= msize);
1058     mbc.keyword_offset = sc->keyword_offset;
1059     /* calculate total number of known results (in put_cnt => sqms) */
1060     mbc.put_cnt = 0;
1061     GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1062                                            &find_result_set, &mbc);
1063     sqms = mbc.put_cnt;
1064     /* calculate how many results we can send in this message */
1065     mbc.put_cnt = (size - msize) / sizeof (struct GNUNET_HashCode);
1066     mbc.put_cnt = GNUNET_MIN (mbc.put_cnt, sqms - mbc.skip_cnt);
1067     if (sc->search_request_map_offset < sqms)
1068       GNUNET_assert (mbc.put_cnt > 0);
1069
1070     /* now build message */
1071     msize += sizeof (struct GNUNET_HashCode) * mbc.put_cnt;
1072     sm->header.size = htons (msize);
1073     sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK);
1074     sm->anonymity_level = htonl (sc->anonymity);
1075     memset (&sm->target, 0, sizeof (struct GNUNET_HashCode));
1076     sm->query = sc->requests[sc->keyword_offset].uquery;
1077     GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1078                                            &build_result_set, &mbc);
1079     GNUNET_assert (sqms >= sc->search_request_map_offset);
1080     if (sqms != sc->search_request_map_offset)
1081     {
1082       /* more requesting to be done... */
1083       sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1084       schedule_transmit_search_request (sc);
1085       return msize;
1086     }
1087     sm->options = htonl (options);
1088     sc->keyword_offset++;
1089     if (sc->uri->data.ksk.keywordCount != sc->keyword_offset)
1090     {
1091       /* more requesting to be done... */
1092       schedule_transmit_search_request (sc);
1093       return msize;
1094     }
1095   }
1096   else
1097   {
1098     GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1099     msize = sizeof (struct SearchMessage);
1100     GNUNET_assert (size >= msize);
1101     sm->type = htonl (GNUNET_BLOCK_TYPE_FS_UBLOCK);
1102     sm->anonymity_level = htonl (sc->anonymity);
1103     memset (&sm->target, 0, sizeof (struct GNUNET_HashCode));
1104     GNUNET_CRYPTO_ecc_public_key_derive (&sc->uri->data.sks.ns,
1105                                          sc->uri->data.sks.identifier,
1106                                          &dpub);
1107     GNUNET_CRYPTO_hash (&dpub,
1108                         sizeof (dpub),
1109                         &sm->query);
1110     mbc.put_cnt = (size - msize) / sizeof (struct GNUNET_HashCode);
1111     sqms = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
1112     mbc.put_cnt = GNUNET_MIN (mbc.put_cnt, sqms - mbc.skip_cnt);
1113     mbc.keyword_offset = 0;
1114     if (sc->search_request_map_offset < sqms)
1115       GNUNET_assert (mbc.put_cnt > 0);
1116     msize += sizeof (struct GNUNET_HashCode) * mbc.put_cnt;
1117     GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1118                                            &build_result_set, &mbc);
1119     sm->header.size = htons (msize);
1120     GNUNET_assert (sqms >= sc->search_request_map_offset);
1121     if (sqms != sc->search_request_map_offset)
1122     {
1123       /* more requesting to be done... */
1124       sm->options = htonl (options | SEARCH_MESSAGE_OPTION_CONTINUED);
1125       schedule_transmit_search_request (sc);
1126       return msize;
1127     }
1128     sm->options = htonl (options);
1129   }
1130   GNUNET_CLIENT_receive (sc->client, &receive_results, sc,
1131                          GNUNET_TIME_UNIT_FOREVER_REL);
1132   return msize;
1133 }
1134
1135
1136 /**
1137  * Schedule the transmission of the (next) search request
1138  * to the service.
1139  *
1140  * @param sc context for the search
1141  */
1142 static void
1143 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc)
1144 {
1145   size_t size;
1146   unsigned int sqms;
1147   unsigned int fit;
1148
1149   size = sizeof (struct SearchMessage);
1150   sqms =
1151       GNUNET_CONTAINER_multihashmap_size (sc->master_result_map) -
1152       sc->search_request_map_offset;
1153   fit = (GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - size) / sizeof (struct GNUNET_HashCode);
1154   fit = GNUNET_MIN (fit, sqms);
1155   size += sizeof (struct GNUNET_HashCode) * fit;
1156   GNUNET_CLIENT_notify_transmit_ready (sc->client, size,
1157                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1158                                        GNUNET_NO, &transmit_search_request, sc);
1159
1160 }
1161
1162
1163 /**
1164  * Reconnect to the FS service and transmit
1165  * our queries NOW.
1166  *
1167  * @param cls our search context
1168  * @param tc unused
1169  */
1170 static void
1171 do_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1172 {
1173   struct GNUNET_FS_SearchContext *sc = cls;
1174   struct GNUNET_CLIENT_Connection *client;
1175
1176   sc->task = GNUNET_SCHEDULER_NO_TASK;
1177   client = GNUNET_CLIENT_connect ("fs", sc->h->cfg);
1178   if (NULL == client)
1179   {
1180     try_reconnect (sc);
1181     return;
1182   }
1183   sc->client = client;
1184   sc->search_request_map_offset = 0;
1185   sc->keyword_offset = 0;
1186   schedule_transmit_search_request (sc);
1187 }
1188
1189
1190 /**
1191  * Shutdown any existing connection to the FS
1192  * service and try to establish a fresh one
1193  * (and then re-transmit our search request).
1194  *
1195  * @param sc the search to reconnec
1196  */
1197 static void
1198 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1199 {
1200   if (NULL != sc->client)
1201   {
1202     GNUNET_CLIENT_disconnect (sc->client);
1203     sc->client = NULL;
1204   }
1205   sc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (sc->reconnect_backoff);
1206   sc->task =
1207       GNUNET_SCHEDULER_add_delayed (sc->reconnect_backoff, 
1208                                     &do_reconnect,
1209                                     sc);
1210 }
1211
1212
1213 /**
1214  * Start search for content, internal API.
1215  *
1216  * @param h handle to the file sharing subsystem
1217  * @param uri specifies the search parameters; can be
1218  *        a KSK URI or an SKS URI.
1219  * @param anonymity desired level of anonymity
1220  * @param options options for the search
1221  * @param cctx initial value for the client context
1222  * @param psearch parent search result (for namespace update searches)
1223  * @return context that can be used to control the search
1224  */
1225 static struct GNUNET_FS_SearchContext *
1226 search_start (struct GNUNET_FS_Handle *h, const struct GNUNET_FS_Uri *uri,
1227               uint32_t anonymity, enum GNUNET_FS_SearchOptions options,
1228               void *cctx, struct GNUNET_FS_SearchResult *psearch)
1229 {
1230   struct GNUNET_FS_SearchContext *sc;
1231   struct GNUNET_FS_ProgressInfo pi;
1232
1233   sc = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchContext));
1234   sc->h = h;
1235   sc->options = options;
1236   sc->uri = GNUNET_FS_uri_dup (uri);
1237   sc->anonymity = anonymity;
1238   sc->start_time = GNUNET_TIME_absolute_get ();
1239   if (NULL != psearch)
1240   {
1241     sc->psearch_result = psearch;
1242     psearch->update_search = sc;
1243   }
1244   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
1245   sc->client_info = cctx;
1246   if (GNUNET_OK != GNUNET_FS_search_start_searching_ (sc))
1247   {
1248     GNUNET_FS_uri_destroy (sc->uri);
1249     GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1250     GNUNET_free (sc);
1251     return NULL;
1252   }
1253   GNUNET_FS_search_sync_ (sc);
1254   pi.status = GNUNET_FS_STATUS_SEARCH_START;
1255   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1256   return sc;
1257 }
1258
1259
1260 /**
1261  * Build the request and actually initiate the search using the
1262  * GNUnet FS service.
1263  *
1264  * @param sc search context
1265  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1266  */
1267 int
1268 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1269 {
1270   unsigned int i;
1271   const char *keyword;
1272   const struct GNUNET_CRYPTO_EccPrivateKey *anon;
1273   struct GNUNET_CRYPTO_EccPublicKey anon_pub;
1274   struct SearchRequestEntry *sre;
1275
1276   GNUNET_assert (NULL == sc->client);
1277   if (GNUNET_FS_uri_test_ksk (sc->uri))
1278   {
1279     GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1280     anon = GNUNET_CRYPTO_ecc_key_get_anonymous ();
1281     GNUNET_CRYPTO_ecc_key_get_public (anon, &anon_pub);
1282     sc->requests =
1283         GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1284                        sc->uri->data.ksk.keywordCount);
1285     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1286     {
1287       keyword = &sc->uri->data.ksk.keywords[i][1];
1288       sre = &sc->requests[i];
1289       sre->keyword = GNUNET_strdup (keyword);
1290       GNUNET_CRYPTO_ecc_public_key_derive (&anon_pub,
1291                                            keyword,
1292                                            &sre->dpub);
1293       GNUNET_CRYPTO_hash (&sre->dpub, 
1294                           sizeof (struct GNUNET_CRYPTO_EccPublicKey), 
1295                           &sre->uquery);
1296       sre->mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1297       if (sre->mandatory)
1298         sc->mandatory_count++;
1299       sre->results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
1300     }
1301   }
1302   sc->client = GNUNET_CLIENT_connect ("fs", sc->h->cfg);
1303   if (NULL == sc->client)
1304     return GNUNET_SYSERR;
1305   schedule_transmit_search_request (sc);
1306   return GNUNET_OK;
1307 }
1308
1309
1310 /**
1311  * Freeze probes for the given search result.
1312  *
1313  * @param cls the global FS handle
1314  * @param key the key for the search result (unused)
1315  * @param value the search result to free
1316  * @return GNUNET_OK
1317  */
1318 static int
1319 search_result_freeze_probes (void *cls, const struct GNUNET_HashCode * key,
1320                              void *value)
1321 {
1322   struct GNUNET_FS_SearchResult *sr = value;
1323
1324   if (NULL != sr->probe_ctx)
1325   {
1326     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1327     sr->probe_ctx = NULL;
1328   }
1329   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
1330   {
1331     GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
1332     sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
1333   }
1334   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_cancel_task)
1335   {
1336     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1337     sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1338   }
1339   if (NULL != sr->update_search)
1340     GNUNET_FS_search_pause (sr->update_search);
1341   return GNUNET_OK;
1342 }
1343
1344
1345 /**
1346  * Resume probes for the given search result.
1347  *
1348  * @param cls the global FS handle
1349  * @param key the key for the search result (unused)
1350  * @param value the search result to free
1351  * @return GNUNET_OK
1352  */
1353 static int
1354 search_result_resume_probes (void *cls, const struct GNUNET_HashCode * key,
1355                              void *value)
1356 {
1357   struct GNUNET_FS_SearchResult *sr = value;
1358
1359   GNUNET_FS_search_start_probe_ (sr);
1360   if (NULL != sr->update_search)
1361     GNUNET_FS_search_continue (sr->update_search);
1362   return GNUNET_OK;
1363 }
1364
1365
1366 /**
1367  * Signal suspend and free the given search result.
1368  *
1369  * @param cls the global FS handle
1370  * @param key the key for the search result (unused)
1371  * @param value the search result to free
1372  * @return GNUNET_OK
1373  */
1374 static int
1375 search_result_suspend (void *cls, const struct GNUNET_HashCode * key, void *value)
1376 {
1377   struct GNUNET_FS_SearchContext *sc = cls;
1378   struct GNUNET_FS_SearchResult *sr = value;
1379   struct GNUNET_FS_ProgressInfo pi;
1380
1381   if (NULL != sr->download)
1382   {
1383     GNUNET_FS_download_signal_suspend_ (sr->download);
1384     sr->download = NULL;
1385   }
1386   if (NULL != sr->probe_ctx)
1387   {
1388     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1389     sr->probe_ctx = NULL;
1390   }
1391   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
1392   {
1393     GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
1394     sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
1395   }
1396   if (NULL != sr->update_search)
1397   {
1398     GNUNET_FS_search_signal_suspend_ (sr->update_search);
1399     sr->update_search = NULL;
1400   }
1401   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1402   pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1403   pi.value.search.specifics.result_suspend.meta = sr->meta;
1404   pi.value.search.specifics.result_suspend.uri = sr->uri;
1405   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1406   GNUNET_break (NULL == sr->client_info);
1407   GNUNET_free_non_null (sr->serialization);
1408   GNUNET_FS_uri_destroy (sr->uri);
1409   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1410   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_cancel_task)
1411   {
1412     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1413     sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1414   }
1415   GNUNET_free_non_null (sr->keyword_bitmap);
1416   GNUNET_free (sr);
1417   return GNUNET_OK;
1418 }
1419
1420
1421 /**
1422  * Create SUSPEND event for the given search operation
1423  * and then clean up our state (without stop signal).
1424  *
1425  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1426  */
1427 void
1428 GNUNET_FS_search_signal_suspend_ (void *cls)
1429 {
1430   struct GNUNET_FS_SearchContext *sc = cls;
1431   struct GNUNET_FS_ProgressInfo pi;
1432   unsigned int i;
1433
1434   GNUNET_FS_end_top (sc->h, sc->top);
1435   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1436                                          &search_result_suspend, sc);
1437   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1438   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1439   GNUNET_break (NULL == sc->client_info);
1440   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1441     GNUNET_SCHEDULER_cancel (sc->task);
1442   if (NULL != sc->client)
1443     GNUNET_CLIENT_disconnect (sc->client);
1444   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1445   if (NULL != sc->requests)
1446   {
1447     GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1448     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1449     {
1450       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1451       GNUNET_free (sc->requests[i].keyword);
1452     }
1453   }
1454   GNUNET_free_non_null (sc->requests);
1455   GNUNET_free_non_null (sc->emsg);
1456   GNUNET_FS_uri_destroy (sc->uri);
1457   GNUNET_free_non_null (sc->serialization);
1458   GNUNET_free (sc);
1459 }
1460
1461
1462 /**
1463  * Start search for content.
1464  *
1465  * @param h handle to the file sharing subsystem
1466  * @param uri specifies the search parameters; can be
1467  *        a KSK URI or an SKS URI.
1468  * @param anonymity desired level of anonymity
1469  * @param options options for the search
1470  * @param cctx initial value for the client context
1471  * @return context that can be used to control the search
1472  */
1473 struct GNUNET_FS_SearchContext *
1474 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1475                         const struct GNUNET_FS_Uri *uri, uint32_t anonymity,
1476                         enum GNUNET_FS_SearchOptions options, void *cctx)
1477 {
1478   struct GNUNET_FS_SearchContext *ret;
1479
1480   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1481   if (NULL == ret)
1482     return NULL;
1483   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1484   return ret;
1485 }
1486
1487
1488 /**
1489  * Pause search.
1490  *
1491  * @param sc context for the search that should be paused
1492  */
1493 void
1494 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1495 {
1496   struct GNUNET_FS_ProgressInfo pi;
1497
1498   if (GNUNET_SCHEDULER_NO_TASK != sc->task)
1499     GNUNET_SCHEDULER_cancel (sc->task);
1500   sc->task = GNUNET_SCHEDULER_NO_TASK;
1501   if (NULL != sc->client)
1502     GNUNET_CLIENT_disconnect (sc->client);
1503   sc->client = NULL;
1504   GNUNET_FS_search_sync_ (sc);
1505   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1506                                          &search_result_freeze_probes, sc);
1507   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1508   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1509 }
1510
1511
1512 /**
1513  * Continue paused search.
1514  *
1515  * @param sc context for the search that should be resumed
1516  */
1517 void
1518 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1519 {
1520   struct GNUNET_FS_ProgressInfo pi;
1521
1522   GNUNET_assert (NULL == sc->client);
1523   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sc->task);
1524   do_reconnect (sc, NULL);
1525   GNUNET_FS_search_sync_ (sc);
1526   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1527   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1528   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1529                                          &search_result_resume_probes, sc);
1530 }
1531
1532
1533 /**
1534  * Signal stop for the given search result.
1535  *
1536  * @param cls the global FS handle
1537  * @param key the key for the search result (unused)
1538  * @param value the search result to free
1539  * @return GNUNET_OK
1540  */
1541 static int
1542 search_result_stop (void *cls, const struct GNUNET_HashCode * key, void *value)
1543 {
1544   struct GNUNET_FS_SearchContext *sc = cls;
1545   struct GNUNET_FS_SearchResult *sr = value;
1546   struct GNUNET_FS_ProgressInfo pi;
1547
1548   GNUNET_FS_search_stop_probe_ (sr);
1549
1550   if (NULL != sr->download)
1551   {
1552     sr->download->search = NULL;
1553     sr->download->top =
1554         GNUNET_FS_make_top (sr->download->h,
1555                             &GNUNET_FS_download_signal_suspend_, sr->download);
1556     if (NULL != sr->download->serialization)
1557     {
1558       GNUNET_FS_remove_sync_file_ (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1559                                    sr->download->serialization);
1560       GNUNET_free (sr->download->serialization);
1561       sr->download->serialization = NULL;
1562     }
1563     pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1564     GNUNET_FS_download_make_status_ (&pi, sr->download);
1565     GNUNET_FS_download_sync_ (sr->download);
1566     sr->download = NULL;
1567   }
1568   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1569   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1570   pi.value.search.specifics.result_stopped.meta = sr->meta;
1571   pi.value.search.specifics.result_stopped.uri = sr->uri;
1572   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sr->h, sc);
1573   return GNUNET_OK;
1574 }
1575
1576
1577 /**
1578  * Free the given search result.
1579  *
1580  * @param cls the global FS handle
1581  * @param key the key for the search result (unused)
1582  * @param value the search result to free
1583  * @return GNUNET_OK
1584  */
1585 static int
1586 search_result_free (void *cls, const struct GNUNET_HashCode * key, void *value)
1587 {
1588   struct GNUNET_FS_SearchResult *sr = value;
1589
1590   if (NULL != sr->update_search)
1591   {
1592     GNUNET_FS_search_stop (sr->update_search);
1593     GNUNET_assert (NULL == sr->update_search);
1594   }
1595   GNUNET_break (NULL == sr->probe_ctx);
1596   GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sr->probe_cancel_task);
1597   GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sr->probe_ping_task);
1598   GNUNET_break (NULL == sr->client_info);
1599   GNUNET_free_non_null (sr->serialization);
1600   GNUNET_FS_uri_destroy (sr->uri);
1601   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1602   GNUNET_free_non_null (sr->keyword_bitmap);
1603   GNUNET_free (sr);
1604   return GNUNET_OK;
1605 }
1606
1607
1608 /**
1609  * Stop search for content.
1610  *
1611  * @param sc context for the search that should be stopped
1612  */
1613 void
1614 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1615 {
1616   struct GNUNET_FS_ProgressInfo pi;
1617   unsigned int i;
1618
1619   if (NULL != sc->top)
1620     GNUNET_FS_end_top (sc->h, sc->top);
1621   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1622                                          &search_result_stop, sc);
1623   if (NULL != sc->psearch_result)
1624     sc->psearch_result->update_search = NULL;
1625   if (NULL != sc->serialization)
1626   {
1627     GNUNET_FS_remove_sync_file_ (sc->h,
1628                                  (sc->psearch_result !=
1629                                   NULL) ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH :
1630                                  GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1631                                  sc->serialization);
1632     GNUNET_FS_remove_sync_dir_ (sc->h,
1633                                 (sc->psearch_result !=
1634                                  NULL) ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH :
1635                                 GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1636                                 sc->serialization);
1637     GNUNET_free (sc->serialization);
1638   }
1639   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1640   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc->h, sc);
1641   GNUNET_break (NULL == sc->client_info);
1642   if (GNUNET_SCHEDULER_NO_TASK != sc->task)
1643     GNUNET_SCHEDULER_cancel (sc->task);
1644   if (NULL != sc->client)
1645     GNUNET_CLIENT_disconnect (sc->client);
1646   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1647                                          &search_result_free, sc);
1648   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1649   if (NULL != sc->requests)
1650   {
1651     GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1652     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1653       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1654   }
1655   GNUNET_free_non_null (sc->requests);
1656   GNUNET_free_non_null (sc->emsg);
1657   GNUNET_FS_uri_destroy (sc->uri);
1658   GNUNET_free (sc);
1659 }
1660
1661 /* end of fs_search.c */