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