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