fix
[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->psearch_result == NULL) ? NULL : sc->psearch_result->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 psearch parent search result (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_SearchResult *psearch);
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   (void) search_start (sc->h,
527                        &uu,
528                        sc->anonymity,
529                        sc->options,
530                        NULL,
531                        sr);
532   GNUNET_free (uu.data.sks.identifier);
533 }
534
535
536 /**
537  * Process a keyword-search result.
538  *
539  * @param sc our search context
540  * @param kb the kblock
541  * @param size size of kb
542  */
543 static void
544 process_kblock (struct GNUNET_FS_SearchContext *sc,
545                 const struct KBlock *kb,
546                 size_t size)
547 {
548   unsigned int i;
549   size_t j;
550   GNUNET_HashCode q;
551   char pt[size - sizeof (struct KBlock)];
552   struct GNUNET_CRYPTO_AesSessionKey skey;
553   struct GNUNET_CRYPTO_AesInitializationVector iv;
554   const char *eos;
555   struct GNUNET_CONTAINER_MetaData *meta;
556   struct GNUNET_FS_Uri *uri;
557   char *emsg;
558   
559   GNUNET_CRYPTO_hash (&kb->keyspace,
560                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
561                       &q);
562   /* find key */
563   for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
564     if (0 == memcmp (&q,
565                      &sc->requests[i].query,
566                      sizeof (GNUNET_HashCode)))
567       break;
568   if (i == sc->uri->data.ksk.keywordCount)
569     {
570       /* oops, does not match any of our keywords!? */
571       GNUNET_break (0);
572       return;
573     }
574   /* decrypt */
575   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
576   GNUNET_CRYPTO_aes_decrypt (&kb[1],
577                              size - sizeof (struct KBlock),
578                              &skey,
579                              &iv,
580                              pt);
581   /* parse */
582   eos = memchr (pt, 0, sizeof (pt));
583   if (NULL == eos)
584     {
585       GNUNET_break_op (0);
586       return;
587     }
588   j = eos - pt + 1;
589   if (sizeof (pt) == j)
590     meta = GNUNET_CONTAINER_meta_data_create ();
591   else
592     meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
593                                                    sizeof (pt) - j);
594   if (meta == NULL)
595     {
596       GNUNET_break_op (0);       /* kblock malformed */
597       return;
598     }
599   uri = GNUNET_FS_uri_parse (pt, &emsg);
600   if (uri == NULL)
601     {
602       GNUNET_break_op (0);       /* kblock malformed */
603       GNUNET_free_non_null (emsg);
604       GNUNET_CONTAINER_meta_data_destroy (meta);
605       return;
606     }
607   /* process */
608   process_ksk_result (sc, &sc->requests[i], uri, meta);
609
610   /* clean up */
611   GNUNET_CONTAINER_meta_data_destroy (meta);
612   GNUNET_FS_uri_destroy (uri);
613 }
614
615
616 /**
617  * Process a keyword-search result with a namespace advertisment.
618  *
619  * @param sc our search context
620  * @param nb the nblock
621  * @param size size of nb
622  */
623 static void
624 process_nblock (struct GNUNET_FS_SearchContext *sc,
625                 const struct NBlock *nb,
626                 size_t size)
627 {
628   unsigned int i;
629   size_t j;
630   GNUNET_HashCode q;
631   char pt[size - sizeof (struct NBlock)];
632   struct GNUNET_CRYPTO_AesSessionKey skey;
633   struct GNUNET_CRYPTO_AesInitializationVector iv;
634   const char *eos;
635   struct GNUNET_CONTAINER_MetaData *meta;
636   struct GNUNET_FS_Uri *uri;
637   char *uris;
638   
639   GNUNET_CRYPTO_hash (&nb->keyspace,
640                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
641                       &q);
642   /* find key */
643   for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
644     if (0 == memcmp (&q,
645                      &sc->requests[i].query,
646                      sizeof (GNUNET_HashCode)))
647       break;
648   if (i == sc->uri->data.ksk.keywordCount)
649     {
650       /* oops, does not match any of our keywords!? */
651       GNUNET_break (0);
652       return;
653     }
654   /* decrypt */
655   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
656   GNUNET_CRYPTO_aes_decrypt (&nb[1],
657                              size - sizeof (struct NBlock),
658                              &skey,
659                              &iv,
660                              pt);
661   /* parse */
662   eos = memchr (pt, 0, sizeof (pt));
663   if (NULL == eos)
664     {
665       GNUNET_break_op (0);
666       return;
667     }
668   j = eos - pt + 1;
669   if (sizeof (pt) == j)
670     meta = GNUNET_CONTAINER_meta_data_create ();
671   else
672     meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
673                                                    sizeof (pt) - j);
674   if (meta == NULL)
675     {
676       GNUNET_break_op (0);       /* nblock malformed */
677       return;
678     }
679
680   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
681   uri->type = sks;
682   uri->data.sks.identifier = GNUNET_strdup (pt);
683   GNUNET_CRYPTO_hash (&nb->subspace,
684                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
685                       &uri->data.sks.namespace);
686   uris = GNUNET_FS_uri_to_string (uri);
687   GNUNET_CONTAINER_meta_data_insert (meta,
688                                      "<gnunet>",
689                                      EXTRACTOR_METATYPE_URI,
690                                      EXTRACTOR_METAFORMAT_UTF8,
691                                      "text/plain",
692                                      uris,
693                                      strlen (uris)+1);
694   GNUNET_free (uris);
695   GNUNET_PSEUDONYM_add (sc->h->cfg,
696                         &uri->data.sks.namespace,
697                         meta);
698   /* process */
699   process_ksk_result (sc, &sc->requests[i], uri, meta);
700
701   /* clean up */
702   GNUNET_CONTAINER_meta_data_destroy (meta);
703   GNUNET_FS_uri_destroy (uri);
704 }
705
706
707 /**
708  * Process a namespace-search result.
709  *
710  * @param sc our search context
711  * @param sb the sblock
712  * @param size size of sb
713  */
714 static void
715 process_sblock (struct GNUNET_FS_SearchContext *sc,
716                 const struct SBlock *sb,
717                 size_t size)
718 {
719   size_t len = size - sizeof (struct SBlock);
720   char pt[len];
721   struct GNUNET_CRYPTO_AesSessionKey skey;
722   struct GNUNET_CRYPTO_AesInitializationVector iv;
723   struct GNUNET_FS_Uri *uri;
724   struct GNUNET_CONTAINER_MetaData *meta;
725   const char *id;
726   const char *uris;
727   size_t off;
728   char *emsg;
729   GNUNET_HashCode key;
730   char *identifier;
731
732   /* decrypt */
733   identifier = sc->uri->data.sks.identifier;
734   GNUNET_CRYPTO_hash (identifier, 
735                       strlen (identifier), 
736                       &key);
737   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
738   GNUNET_CRYPTO_aes_decrypt (&sb[1],
739                              len,
740                              &skey,
741                              &iv,
742                              pt);
743   /* parse */
744   off = GNUNET_STRINGS_buffer_tokenize (pt,
745                                         len, 
746                                         2, 
747                                         &id, 
748                                         &uris);
749   if (off == 0)
750     {
751       GNUNET_break_op (0);     /* sblock malformed */
752       return;
753     }
754   meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off], 
755                                                  len - off);
756   if (meta == NULL)
757     {
758       GNUNET_break_op (0);     /* sblock malformed */
759       return;
760     }
761   uri = GNUNET_FS_uri_parse (uris, &emsg);
762   if (uri == NULL)
763     {
764       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
765                   "Failed to parse URI `%s': %s\n",
766                   uris, emsg);
767       GNUNET_break_op (0);     /* sblock malformed */
768       GNUNET_free_non_null (emsg);
769       GNUNET_CONTAINER_meta_data_destroy (meta);
770       return;
771     }
772   /* process */
773   process_sks_result (sc, id, uri, meta);
774   /* clean up */
775   GNUNET_FS_uri_destroy (uri);
776   GNUNET_CONTAINER_meta_data_destroy (meta);
777 }
778
779
780 /**
781  * Process a search result.
782  *
783  * @param sc our search context
784  * @param type type of the result
785  * @param expiration when it will expire
786  * @param data the (encrypted) response
787  * @param size size of data
788  */
789 static void
790 process_result (struct GNUNET_FS_SearchContext *sc,
791                 enum GNUNET_BLOCK_Type type,
792                 struct GNUNET_TIME_Absolute expiration,
793                 const void *data,
794                 size_t size)
795 {
796   if (GNUNET_TIME_absolute_get_duration (expiration).value > 0)
797     {
798       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799                   "Result received has already expired.\n");
800       return; /* result expired */
801     }
802   switch (type)
803     {
804     case GNUNET_BLOCK_TYPE_KBLOCK:
805       if (! GNUNET_FS_uri_test_ksk (sc->uri))
806         {
807           GNUNET_break (0);
808           return;
809         }
810       if (sizeof (struct KBlock) > size)
811         {
812           GNUNET_break_op (0);
813           return;
814         }
815       process_kblock (sc, data, size);
816       break;
817     case GNUNET_BLOCK_TYPE_SBLOCK:
818       if (! GNUNET_FS_uri_test_sks (sc->uri))
819         {
820           GNUNET_break (0);
821           return;
822         }
823       if (sizeof (struct SBlock) > size)
824         {
825           GNUNET_break_op (0);
826           return;
827         }
828       process_sblock (sc, data, size);
829       break;
830     case GNUNET_BLOCK_TYPE_NBLOCK:
831       if (! GNUNET_FS_uri_test_ksk (sc->uri))
832         {
833           GNUNET_break (0);
834           return;
835         }
836       if (sizeof (struct NBlock) > size)
837         {
838           GNUNET_break_op (0);
839           return;
840         }
841       process_nblock (sc, data, size);
842       break;
843     case GNUNET_BLOCK_TYPE_ANY:
844     case GNUNET_BLOCK_TYPE_DBLOCK:
845     case GNUNET_BLOCK_TYPE_ONDEMAND:
846     case GNUNET_BLOCK_TYPE_IBLOCK:
847       GNUNET_break (0);
848       break;
849     default:
850       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
851                   _("Got result with unknown block type `%d', ignoring"),
852                   type);
853       break;
854     }
855 }
856
857
858 /**
859  * Shutdown any existing connection to the FS
860  * service and try to establish a fresh one
861  * (and then re-transmit our search request).
862  *
863  * @param sc the search to reconnec
864  */
865 static void 
866 try_reconnect (struct GNUNET_FS_SearchContext *sc);
867
868
869 /**
870  * Type of a function to call when we receive a message
871  * from the service.
872  *
873  * @param cls closure
874  * @param msg message received, NULL on timeout or fatal error
875  */
876 static void 
877 receive_results (void *cls,
878                  const struct GNUNET_MessageHeader * msg)
879 {
880   struct GNUNET_FS_SearchContext *sc = cls;
881   const struct PutMessage *cm;
882   uint16_t msize;
883
884   if ( (NULL == msg) ||
885        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
886        (ntohs (msg->size) <= sizeof (struct PutMessage)) )
887     {
888       try_reconnect (sc);
889       return;
890     }
891   msize = ntohs (msg->size);
892   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
893               "Receiving %u bytes of result from fs service\n",
894               msize);
895   cm = (const struct PutMessage*) msg;
896   process_result (sc, 
897                   ntohl (cm->type),
898                   GNUNET_TIME_absolute_ntoh (cm->expiration),
899                   &cm[1],
900                   msize - sizeof (struct PutMessage));
901   /* continue receiving */
902   GNUNET_CLIENT_receive (sc->client,
903                          &receive_results,
904                          sc,
905                          GNUNET_TIME_UNIT_FOREVER_REL);
906 }
907
908
909 /**
910  * We're ready to transmit the search request to the
911  * file-sharing service.  Do it.
912  *
913  * @param cls closure
914  * @param size number of bytes available in buf
915  * @param buf where the callee should write the message
916  * @return number of bytes written to buf
917  */
918 static size_t
919 transmit_search_request (void *cls,
920                          size_t size, 
921                          void *buf)
922 {
923   struct GNUNET_FS_SearchContext *sc = cls;
924   size_t msize;
925   struct SearchMessage *sm;
926   unsigned int i;
927   const char *identifier;
928   GNUNET_HashCode key;
929   GNUNET_HashCode idh;
930
931   if (NULL == buf)
932     {
933       try_reconnect (sc);
934       return 0;
935     }
936   if (GNUNET_FS_uri_test_ksk (sc->uri))
937     {
938       msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
939       GNUNET_assert (size >= msize);
940       sm = buf;
941       memset (sm, 0, msize);
942       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
943         {
944           sm[i].header.size = htons (sizeof (struct SearchMessage));
945           sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
946           if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
947             sm[i].options = htonl (1);
948           else
949             sm[i].options = htonl (0);            
950           sm[i].type = htonl (GNUNET_BLOCK_TYPE_ANY);
951           sm[i].anonymity_level = htonl (sc->anonymity);
952           sm[i].query = sc->requests[i].query;
953           /* FIXME: should transmit hash codes of all already-known results here! 
954              (and if they do not fit, add another message with the same 
955              header and additional already-seen results!) */
956         }
957     }
958   else
959     {
960       GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
961       msize = sizeof (struct SearchMessage);
962       GNUNET_assert (size >= msize);
963       sm = buf;
964       memset (sm, 0, msize);
965       sm->header.size = htons (sizeof (struct SearchMessage));
966       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
967       if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
968         sm->options = htonl (1);
969       else
970         sm->options = htonl (0);      
971       sm->type = htonl (GNUNET_BLOCK_TYPE_SBLOCK);
972       sm->anonymity_level = htonl (sc->anonymity);
973       sm->target = sc->uri->data.sks.namespace;
974       identifier = sc->uri->data.sks.identifier;
975       GNUNET_CRYPTO_hash (identifier,
976                           strlen (identifier),
977                           &key);
978       GNUNET_CRYPTO_hash (&key,
979                           sizeof (GNUNET_HashCode),
980                           &idh);
981       GNUNET_CRYPTO_hash_xor (&idh,
982                               &sm->target,
983                               &sm->query);
984       /* FIXME: should transmit hash codes of all already-known results here!
985          (and if they do not fit, add another message with the same 
986          header and additional already-seen results!) */      
987    }
988   GNUNET_CLIENT_receive (sc->client,
989                          &receive_results,
990                          sc,
991                          GNUNET_TIME_UNIT_FOREVER_REL);
992   return msize;
993 }
994
995
996 /**
997  * Reconnect to the FS service and transmit
998  * our queries NOW.
999  *
1000  * @param cls our search context
1001  * @param tc unused
1002  */
1003 static void
1004 do_reconnect (void *cls,
1005               const struct GNUNET_SCHEDULER_TaskContext *tc)
1006 {
1007   struct GNUNET_FS_SearchContext *sc = cls;
1008   struct GNUNET_CLIENT_Connection *client;
1009   size_t size;
1010   
1011   sc->task = GNUNET_SCHEDULER_NO_TASK;
1012   client = GNUNET_CLIENT_connect (sc->h->sched,
1013                                   "fs",
1014                                   sc->h->cfg);
1015   if (NULL == client)
1016     {
1017       try_reconnect (sc);
1018       return;
1019     }
1020   sc->client = client;
1021   if (GNUNET_FS_uri_test_ksk (sc->uri))
1022     size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1023   else
1024     size = sizeof (struct SearchMessage);
1025   GNUNET_CLIENT_notify_transmit_ready (client,
1026                                        size,
1027                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1028                                        GNUNET_NO,
1029                                        &transmit_search_request,
1030                                        sc);  
1031 }
1032
1033
1034 /**
1035  * Shutdown any existing connection to the FS
1036  * service and try to establish a fresh one
1037  * (and then re-transmit our search request).
1038  *
1039  * @param sc the search to reconnec
1040  */
1041 static void 
1042 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1043 {
1044   if (NULL != sc->client)
1045     {
1046       GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1047       sc->client = NULL;
1048     }
1049   sc->task
1050     = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
1051                                     GNUNET_TIME_UNIT_SECONDS,
1052                                     &do_reconnect,
1053                                     sc);
1054 }
1055
1056
1057 /**
1058  * Start search for content, internal API.
1059  *
1060  * @param h handle to the file sharing subsystem
1061  * @param uri specifies the search parameters; can be
1062  *        a KSK URI or an SKS URI.
1063  * @param anonymity desired level of anonymity
1064  * @param options options for the search
1065  * @param cctx initial value for the client context
1066  * @param psearch parent search result (for namespace update searches)
1067  * @return context that can be used to control the search
1068  */
1069 static struct GNUNET_FS_SearchContext *
1070 search_start (struct GNUNET_FS_Handle *h,
1071               const struct GNUNET_FS_Uri *uri,
1072               uint32_t anonymity,
1073               enum GNUNET_FS_SearchOptions options,
1074               void *cctx,
1075               struct GNUNET_FS_SearchResult *psearch)
1076 {
1077   struct GNUNET_FS_SearchContext *sc;
1078   struct GNUNET_FS_ProgressInfo pi;
1079
1080   sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
1081   sc->h = h;
1082   sc->options = options;
1083   sc->uri = GNUNET_FS_uri_dup (uri);
1084   sc->anonymity = anonymity;
1085   sc->start_time = GNUNET_TIME_absolute_get ();
1086   if (psearch != NULL)
1087     {
1088       sc->psearch_result = psearch;  
1089       psearch->update_search = sc;
1090     }
1091   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1092   sc->client_info = cctx;
1093   if (GNUNET_OK !=
1094       GNUNET_FS_search_start_searching_ (sc))
1095     {
1096       GNUNET_FS_uri_destroy (sc->uri);
1097       GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1098       GNUNET_free (sc);      
1099       return NULL;
1100     }
1101   GNUNET_FS_search_sync_ (sc);
1102   pi.status = GNUNET_FS_STATUS_SEARCH_START;
1103   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1104   return sc;
1105 }
1106
1107
1108 /**
1109  * Build the request and actually initiate the search using the
1110  * GNUnet FS service.
1111  *
1112  * @param sc search context
1113  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1114  */
1115 int
1116 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1117 {
1118   unsigned int i;
1119   const char *keyword;
1120   GNUNET_HashCode hc;
1121   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;  
1122   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1123   size_t size;
1124
1125   GNUNET_assert (NULL == sc->client);
1126   if (GNUNET_FS_uri_test_ksk (sc->uri))
1127     {
1128       size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1129     }
1130   else
1131     {
1132       GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1133       size = sizeof (struct SearchMessage);
1134     }
1135   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1136     {
1137       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1138                   _("Too many keywords specified for a single search."));
1139       return GNUNET_SYSERR;
1140     }
1141   if (GNUNET_FS_uri_test_ksk (sc->uri))
1142     {
1143       GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1144       sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1145                                     sc->uri->data.ksk.keywordCount);
1146       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1147         {
1148           keyword = &sc->uri->data.ksk.keywords[i][1];
1149           GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
1150           pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1151           GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1152           GNUNET_CRYPTO_rsa_key_free (pk);
1153           GNUNET_CRYPTO_hash (&pub,
1154                               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
1155                               &sc->requests[i].query);
1156           sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1157           if (sc->requests[i].mandatory)
1158             sc->mandatory_count++;
1159           sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1160           GNUNET_CRYPTO_hash (keyword,
1161                               strlen (keyword),
1162                               &sc->requests[i].key);
1163         }
1164     }
1165   sc->client = GNUNET_CLIENT_connect (sc->h->sched,
1166                                       "fs",
1167                                       sc->h->cfg);
1168   if (NULL == sc->client)
1169     return GNUNET_SYSERR;
1170   GNUNET_CLIENT_notify_transmit_ready (sc->client,
1171                                        size,
1172                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1173                                        GNUNET_NO,
1174                                        &transmit_search_request,
1175                                        sc);
1176   return GNUNET_OK;
1177 }
1178
1179
1180 /**
1181  * Freeze probes for the given search result.
1182  *
1183  * @param cls the global FS handle
1184  * @param key the key for the search result (unused)
1185  * @param value the search result to free
1186  * @return GNUNET_OK
1187  */
1188 static int
1189 search_result_freeze_probes (void *cls,
1190                              const GNUNET_HashCode * key,
1191                              void *value)
1192 {
1193   struct GNUNET_FS_SearchContext *sc = cls;
1194   struct GNUNET_FS_Handle *h = sc->h;
1195   struct GNUNET_FS_SearchResult *sr = value;
1196
1197   if (sr->probe_ctx != NULL)
1198     {
1199       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1200       sr->probe_ctx = NULL;
1201     }
1202   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1203     {
1204       GNUNET_SCHEDULER_cancel (h->sched,
1205                                sr->probe_cancel_task);  
1206       sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1207     }
1208   if (sr->update_search != NULL)
1209     GNUNET_FS_search_pause (sr->update_search);
1210   return GNUNET_OK;
1211 }
1212
1213
1214 /**
1215  * Resume probes for the given search result.
1216  *
1217  * @param cls the global FS handle
1218  * @param key the key for the search result (unused)
1219  * @param value the search result to free
1220  * @return GNUNET_OK
1221  */
1222 static int
1223 search_result_resume_probes (void *cls,
1224                              const GNUNET_HashCode * key,
1225                              void *value)
1226 {
1227   struct GNUNET_FS_SearchResult *sr = value;
1228
1229   GNUNET_FS_search_start_probe_ (sr);
1230   if (sr->update_search != NULL)
1231     GNUNET_FS_search_continue (sr->update_search);
1232   return GNUNET_OK;
1233 }
1234
1235
1236 /**
1237  * Signal suspend and free the given search result.
1238  *
1239  * @param cls the global FS handle
1240  * @param key the key for the search result (unused)
1241  * @param value the search result to free
1242  * @return GNUNET_OK
1243  */
1244 static int
1245 search_result_suspend (void *cls,
1246                        const GNUNET_HashCode * key,
1247                        void *value)
1248 {
1249   struct GNUNET_FS_SearchContext *sc = cls;
1250   struct GNUNET_FS_Handle *h = sc->h;
1251   struct GNUNET_FS_SearchResult *sr = value;
1252   struct GNUNET_FS_ProgressInfo pi;
1253
1254   if (sr->download != NULL)
1255     GNUNET_FS_download_signal_suspend_ (sr->download);
1256   if (sr->update_search != NULL)
1257     GNUNET_FS_search_signal_suspend_ (sr->update_search);
1258   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1259   pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1260   pi.value.search.specifics.result_suspend.meta = sr->meta;
1261   pi.value.search.specifics.result_suspend.uri = sr->uri;
1262   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1263   GNUNET_break (NULL == sr->client_info);
1264   GNUNET_free_non_null (sr->serialization);
1265   GNUNET_FS_uri_destroy (sr->uri);
1266   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1267   if (sr->probe_ctx != NULL)
1268     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1269   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1270     GNUNET_SCHEDULER_cancel (h->sched,
1271                              sr->probe_cancel_task);    
1272   GNUNET_free (sr);
1273   return GNUNET_OK;
1274 }
1275
1276
1277 /**
1278  * Create SUSPEND event for the given search operation
1279  * and then clean up our state (without stop signal).
1280  *
1281  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1282  */
1283 void
1284 GNUNET_FS_search_signal_suspend_ (void *cls)
1285 {
1286   struct GNUNET_FS_SearchContext *sc = cls;
1287   struct GNUNET_FS_ProgressInfo pi;
1288   unsigned int i;
1289
1290   GNUNET_FS_end_top (sc->h, sc->top);
1291   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1292                                          &search_result_suspend,
1293                                          sc);
1294   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1295   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1296   GNUNET_break (NULL == sc->client_info);
1297   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1298     GNUNET_SCHEDULER_cancel (sc->h->sched,
1299                              sc->task);
1300   if (NULL != sc->client)
1301     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1302   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1303   if (sc->requests != NULL)
1304     {
1305       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1306       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1307         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1308     }
1309   GNUNET_free_non_null (sc->requests);
1310   GNUNET_free_non_null (sc->emsg);
1311   GNUNET_FS_uri_destroy (sc->uri);
1312   GNUNET_free_non_null (sc->serialization);
1313   GNUNET_free (sc);
1314 }
1315
1316
1317 /**
1318  * Start search for content.
1319  *
1320  * @param h handle to the file sharing subsystem
1321  * @param uri specifies the search parameters; can be
1322  *        a KSK URI or an SKS URI.
1323  * @param anonymity desired level of anonymity
1324  * @param options options for the search
1325  * @param cctx initial value for the client context
1326  * @return context that can be used to control the search
1327  */
1328 struct GNUNET_FS_SearchContext *
1329 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1330                         const struct GNUNET_FS_Uri *uri,
1331                         uint32_t anonymity,
1332                         enum GNUNET_FS_SearchOptions options,
1333                         void *cctx)
1334 {
1335   struct GNUNET_FS_SearchContext *ret;
1336   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1337   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1338   return ret;
1339 }
1340
1341
1342 /**
1343  * Pause search.  
1344  *
1345  * @param sc context for the search that should be paused
1346  */
1347 void 
1348 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1349 {
1350   struct GNUNET_FS_ProgressInfo pi;
1351
1352   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1353     GNUNET_SCHEDULER_cancel (sc->h->sched,
1354                              sc->task);
1355   sc->task = GNUNET_SCHEDULER_NO_TASK;
1356   if (NULL != sc->client)
1357     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1358   sc->client = NULL;
1359   GNUNET_FS_search_sync_ (sc);
1360   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1361                                          &search_result_freeze_probes,
1362                                          sc);
1363   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1364   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1365 }
1366
1367
1368 /**
1369  * Continue paused search.
1370  *
1371  * @param sc context for the search that should be resumed
1372  */
1373 void 
1374 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1375 {
1376   struct GNUNET_FS_ProgressInfo pi;
1377
1378   GNUNET_assert (sc->client == NULL);
1379   GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
1380   do_reconnect (sc, NULL);
1381   GNUNET_FS_search_sync_ (sc);
1382   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1383   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1384   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1385                                          &search_result_resume_probes,
1386                                          sc);
1387 }
1388
1389
1390 /**
1391  * Free the given search result.
1392  *
1393  * @param cls the global FS handle
1394  * @param key the key for the search result (unused)
1395  * @param value the search result to free
1396  * @return GNUNET_OK
1397  */
1398 static int
1399 search_result_free (void *cls,
1400                     const GNUNET_HashCode * key,
1401                     void *value)
1402 {
1403   struct GNUNET_FS_SearchContext *sc = cls;
1404   struct GNUNET_FS_Handle *h = sc->h;
1405   struct GNUNET_FS_SearchResult *sr = value;
1406   struct GNUNET_FS_ProgressInfo pi;
1407
1408   if (NULL != sr->download)
1409     {
1410       sr->download->search = NULL;
1411       if (NULL != sr->download->serialization)
1412         {
1413           GNUNET_FS_remove_sync_file_ (sc->h,
1414                                        GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1415                                        sr->download->serialization);
1416           GNUNET_free (sr->download->serialization);
1417           sr->download->serialization = NULL;
1418         }
1419       pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1420       GNUNET_FS_download_make_status_ (&pi,
1421                                        sr->download);      
1422       GNUNET_FS_download_sync_ (sr->download);
1423       sr->download = NULL;     
1424     }
1425   if (NULL != sr->update_search)
1426     {
1427       GNUNET_FS_search_stop (sr->update_search);
1428       GNUNET_assert (sr->update_search == NULL);
1429     }
1430   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1431   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1432   pi.value.search.specifics.result_stopped.meta = sr->meta;
1433   pi.value.search.specifics.result_stopped.uri = sr->uri;
1434   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1435   GNUNET_break (NULL == sr->client_info);
1436   GNUNET_free_non_null (sr->serialization);
1437   GNUNET_FS_uri_destroy (sr->uri);
1438   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1439   if (sr->probe_ctx != NULL)
1440     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1441   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1442     GNUNET_SCHEDULER_cancel (h->sched,
1443                              sr->probe_cancel_task);    
1444   GNUNET_free (sr);
1445   return GNUNET_OK;
1446 }
1447
1448
1449 /**
1450  * Stop search for content.
1451  *
1452  * @param sc context for the search that should be stopped
1453  */
1454 void 
1455 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1456 {
1457   struct GNUNET_FS_ProgressInfo pi;
1458   unsigned int i;
1459
1460   if (sc->top != NULL)
1461     GNUNET_FS_end_top (sc->h, sc->top);
1462   if (sc->psearch_result != NULL)
1463     sc->psearch_result->update_search = NULL;
1464   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1465                                          &search_result_free,
1466                                          sc);
1467   if (sc->serialization != NULL)
1468     {
1469       GNUNET_FS_remove_sync_file_ (sc->h,
1470                                    (sc->psearch_result != NULL)  
1471                                    ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1472                                    : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1473                                    sc->serialization);
1474       GNUNET_FS_remove_sync_dir_ (sc->h,
1475                                   (sc->psearch_result != NULL)  
1476                                   ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1477                                   : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1478                                   sc->serialization);
1479       GNUNET_free (sc->serialization);
1480     }
1481   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1482   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1483   GNUNET_break (NULL == sc->client_info);
1484   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1485     GNUNET_SCHEDULER_cancel (sc->h->sched,
1486                              sc->task);
1487   if (NULL != sc->client)
1488     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1489   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1490   if (sc->requests != NULL)
1491     {
1492       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1493       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1494         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1495     }
1496   GNUNET_free_non_null (sc->requests);
1497   GNUNET_free_non_null (sc->emsg);
1498   GNUNET_FS_uri_destroy (sc->uri);
1499   GNUNET_free (sc);
1500 }
1501
1502 /* end of fs_search.c */