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