fixes
[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   pi.value.search.specifics.result.applicability_rank = sr->optional_support;
111   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
112 }
113
114
115 /**
116  * We've found new information about an existing CHK result.  Let the
117  * client know about it.
118  * 
119  * @param sc the search context
120  * @param sr the specific result
121  */
122 static void
123 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc, 
124                           struct GNUNET_FS_SearchResult *sr)
125 {                         
126   struct GNUNET_FS_ProgressInfo pi;
127
128   pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
129   pi.value.search.specifics.update.cctx = sr->client_info;
130   pi.value.search.specifics.update.meta = sr->meta;
131   pi.value.search.specifics.update.uri = sr->uri;
132   pi.value.search.specifics.update.availability_rank
133     = 2*sr->availability_success - sr->availability_trials;
134   pi.value.search.specifics.update.availability_certainty 
135     = sr->availability_trials;
136   pi.value.search.specifics.update.applicability_rank 
137     = sr->optional_support;
138   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
139 }
140
141
142 /**
143  * Context for "get_result_present".
144  */
145 struct GetResultContext 
146 {
147   /**
148    * The URI we're looking for.
149    */
150   const struct GNUNET_FS_Uri *uri;
151
152   /**
153    * Where to store a pointer to the search
154    * result struct if we found a match.
155    */
156   struct GNUNET_FS_SearchResult *sr;
157 };
158
159
160 /**
161  * Check if the given result is identical to the given URI and if so
162  * return it.
163  * 
164  * @param cls a "struct GetResultContext"
165  * @param key not used
166  * @param value a "struct GNUNET_FS_SearchResult" who's URI we
167  *        should compare with
168  * @return GNUNET_OK
169  */
170 static int
171 get_result_present (void *cls,
172                      const GNUNET_HashCode * key,
173                      void *value)
174 {
175   struct GetResultContext *grc = cls;
176   struct GNUNET_FS_SearchResult *sr = value;
177
178   if (GNUNET_FS_uri_test_equal (grc->uri,
179                                 sr->uri))
180     grc->sr = sr;
181   return GNUNET_OK;
182 }
183
184
185 /**
186  * Signal result of last probe to client and then schedule next
187  * probe.
188  */
189 static void
190 signal_probe_result (struct GNUNET_FS_SearchResult *sr)
191 {
192   struct GNUNET_FS_ProgressInfo pi;
193
194   pi.status = GNUNET_FS_STATUS_SEARCH_START;
195   pi.value.search.specifics.update.cctx = sr->client_info;
196   pi.value.search.specifics.update.meta = sr->meta;
197   pi.value.search.specifics.update.uri = sr->uri;
198   pi.value.search.specifics.update.availability_rank = sr->availability_success;
199   pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
200   pi.value.search.specifics.update.applicability_rank = sr->optional_support;
201   sr->sc->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
202   GNUNET_FS_search_start_probe_ (sr);
203 }
204
205
206 /**
207  * Handle the case where we have failed to receive a response for our probe.
208  *
209  * @param cls our 'struct GNUNET_FS_SearchResult*'
210  * @param tc scheduler context
211  */
212 static void
213 probe_failure_handler (void *cls,
214                        const struct GNUNET_SCHEDULER_TaskContext *tc)
215 {
216   struct GNUNET_FS_SearchResult *sr = cls;
217   sr->availability_trials++;
218   GNUNET_FS_search_result_sync_ (sr);
219   signal_probe_result (sr);
220 }
221
222
223 /**
224  * Handle the case where we have gotten a response for our probe.
225  *
226  * @param cls our 'struct GNUNET_FS_SearchResult*'
227  * @param tc scheduler context
228  */
229 static void
230 probe_success_handler (void *cls,
231                        const struct GNUNET_SCHEDULER_TaskContext *tc)
232 {
233   struct GNUNET_FS_SearchResult *sr = cls;
234   sr->availability_trials++;
235   sr->availability_success++;
236   GNUNET_FS_search_result_sync_ (sr);
237   signal_probe_result (sr);
238 }
239
240
241 /**
242  * Notification of FS that a search probe has made progress.
243  * This function is used INSTEAD of the client's event handler
244  * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
245  *
246  * @param cls closure, always NULL (!), actual closure
247  *        is in the client-context of the info struct
248  * @param info details about the event, specifying the event type
249  *        and various bits about the event
250  * @return client-context (for the next progress call
251  *         for this operation; should be set to NULL for
252  *         SUSPEND and STOPPED events).  The value returned
253  *         will be passed to future callbacks in the respective
254  *         field in the GNUNET_FS_ProgressInfo struct.
255  */
256 void*
257 GNUNET_FS_search_probe_progress_ (void *cls,
258                                   const struct GNUNET_FS_ProgressInfo *info)
259 {
260   struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
261   struct GNUNET_TIME_Relative dur;
262
263   switch (info->status)
264     {
265     case GNUNET_FS_STATUS_DOWNLOAD_START:
266       /* ignore */
267       break;
268     case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
269       /* probes should never be resumed */
270       GNUNET_assert (0);
271       break;
272     case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
273       /* probes should never be suspended */
274       GNUNET_break (0);
275       break;
276     case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
277       /* ignore */
278       break;
279     case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
280       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
281         {
282           GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
283                                    sr->probe_cancel_task);
284           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
285         }     
286       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
287                                                             sr->remaining_probe_time,
288                                                             &probe_failure_handler,
289                                                             sr);
290       break;
291     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
292       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
293         {
294           GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
295                                    sr->probe_cancel_task);
296           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
297         }     
298       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
299                                                             sr->remaining_probe_time,
300                                                             &probe_success_handler,
301                                                             sr);
302       break;
303     case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
304       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
305         {
306           GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
307                                    sr->probe_cancel_task);
308           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
309         }     
310       sr = NULL;
311       break;
312     case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
313       GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
314       sr->probe_active_time = GNUNET_TIME_absolute_get ();
315       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
316                                                             sr->remaining_probe_time,
317                                                             &probe_failure_handler,
318                                                             sr);
319       break;
320     case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
321       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
322         {
323           GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
324                                    sr->probe_cancel_task);
325           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
326         }
327       dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
328       sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
329                                                                 dur);
330       GNUNET_FS_search_result_sync_ (sr);
331       break;
332     default:
333       GNUNET_break (0);
334       return NULL;
335     }
336   return sr;
337 }
338
339
340 /**
341  * Start download probes for the given search result.
342  *
343  * @param sr the search result
344  */
345 void
346 GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
347 {
348   uint64_t off;
349   uint64_t len;
350   
351   if (sr->probe_ctx != NULL)
352     return;
353   if (sr->download != NULL)
354     return;
355   if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
356     return;
357   if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
358     return;
359   len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
360   if (len == 0)
361     return;
362   if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
363     return;
364   off = len / DBLOCK_SIZE;
365   if (off > 0)
366     off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
367   off *= DBLOCK_SIZE;
368   if (len - off < DBLOCK_SIZE)
369     len = len - off;
370   else
371     len = DBLOCK_SIZE;
372   sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
373                                                             2 * (1 + sr->availability_trials));
374   sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
375                                             sr->uri,
376                                             sr->meta,
377                                             NULL, NULL,
378                                             off, len, 
379                                             sr->sc->anonymity,
380                                             GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
381                                             GNUNET_FS_DOWNLOAD_IS_PROBE,
382                                             sr, NULL);    
383 }
384
385
386 /**
387  * We have received a KSK result.  Check how it fits in with the
388  * overall query and notify the client accordingly.
389  *
390  * @param sc context for the overall query
391  * @param ent entry for the specific keyword
392  * @param uri the URI that was found
393  * @param meta metadata associated with the URI
394  *        under the "ent" keyword
395  */
396 static void
397 process_ksk_result (struct GNUNET_FS_SearchContext *sc, 
398                     struct SearchRequestEntry *ent,
399                     const struct GNUNET_FS_Uri *uri,
400                     const struct GNUNET_CONTAINER_MetaData *meta)
401 {
402   GNUNET_HashCode key;
403   struct GNUNET_FS_SearchResult *sr;
404   struct GetResultContext grc;
405   int is_new;
406
407   /* check if new */
408   GNUNET_FS_uri_to_key (uri, &key);
409   if (GNUNET_SYSERR ==
410       GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
411                                                   &key,
412                                                   &test_result_present,
413                                                   (void*) uri))
414     return; /* duplicate result */
415   /* try to find search result in master map */
416   grc.sr = NULL;
417   grc.uri = uri;
418   GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
419                                               &key,
420                                               &get_result_present,
421                                               &grc);
422   sr = grc.sr;
423   is_new = (NULL == sr) || (sr->mandatory_missing > 0);
424   if (NULL == sr)
425     {
426       sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
427       sr->sc = sc;
428       sr->uri = GNUNET_FS_uri_dup (uri);
429       sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
430       sr->mandatory_missing = sc->mandatory_count;
431       sr->key = key;
432       GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
433                                          &key,
434                                          sr,
435                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
436     }
437   else
438     {
439       GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
440     }
441   /* check if mandatory satisfied */
442   if (ent->mandatory)
443     sr->mandatory_missing--;
444   else
445     sr->optional_support++;
446   if (0 != sr->mandatory_missing)
447     return;
448   if (is_new)
449     notify_client_chk_result (sc, sr);
450   else
451     notify_client_chk_update (sc, sr);
452   GNUNET_FS_search_result_sync_ (sr);
453   GNUNET_FS_search_start_probe_ (sr);
454 }
455
456
457 /**
458  * Start search for content, internal API.
459  *
460  * @param h handle to the file sharing subsystem
461  * @param uri specifies the search parameters; can be
462  *        a KSK URI or an SKS URI.
463  * @param anonymity desired level of anonymity
464  * @param options options for the search
465  * @param cctx client context
466  * @param psearch parent search result (for namespace update searches)
467  * @return context that can be used to control the search
468  */
469 static struct GNUNET_FS_SearchContext *
470 search_start (struct GNUNET_FS_Handle *h,
471               const struct GNUNET_FS_Uri *uri,
472               uint32_t anonymity,
473               enum GNUNET_FS_SearchOptions options,
474               void *cctx,
475               struct GNUNET_FS_SearchResult *psearch);
476
477
478 /**
479  * We have received an SKS result.  Start searching for updates and
480  * notify the client if it is a new result.
481  *
482  * @param sc context for the overall query
483  * @param id_update identifier for updates, NULL for none
484  * @param uri the URI that was found
485  * @param meta metadata associated with the URI
486   */
487 static void
488 process_sks_result (struct GNUNET_FS_SearchContext *sc, 
489                     const char *id_update,
490                     const struct GNUNET_FS_Uri *uri,
491                     const struct GNUNET_CONTAINER_MetaData *meta)
492 {
493   struct GNUNET_FS_Uri uu;
494   GNUNET_HashCode key;
495   struct GNUNET_FS_SearchResult *sr;
496
497   /* check if new */
498   GNUNET_FS_uri_to_key (uri, &key);
499   GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
500                           &uri->data.chk.chk.query,
501                           &key);
502   if (GNUNET_SYSERR ==
503       GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
504                                                   &key,
505                                                   &test_result_present,
506                                                   (void*) uri))
507     return; /* duplicate result */
508   sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
509   sr->sc = sc;
510   sr->uri = GNUNET_FS_uri_dup (uri);
511   sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
512   sr->key = key;
513   GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
514                                      &key,
515                                      sr,
516                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
517   GNUNET_FS_search_result_sync_ (sr);
518   GNUNET_FS_search_start_probe_ (sr);
519   /* notify client */
520   notify_client_chk_result (sc, sr);
521   /* search for updates */
522   if (strlen (id_update) == 0)
523     return; /* no updates */
524   uu.type = sks;
525   uu.data.sks.namespace = sc->uri->data.sks.namespace;
526   uu.data.sks.identifier = GNUNET_strdup (id_update);
527   (void) search_start (sc->h,
528                        &uu,
529                        sc->anonymity,
530                        sc->options,
531                        NULL,
532                        sr);
533   GNUNET_free (uu.data.sks.identifier);
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 psearch parent search result (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_SearchResult *psearch)
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   if (psearch != NULL)
1088     {
1089       sc->psearch_result = psearch;  
1090       psearch->update_search = sc;
1091     }
1092   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1093   sc->client_info = cctx;
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_assert (pk != NULL);
1153           GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1154           GNUNET_CRYPTO_rsa_key_free (pk);
1155           GNUNET_CRYPTO_hash (&pub,
1156                               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
1157                               &sc->requests[i].query);
1158           sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1159           if (sc->requests[i].mandatory)
1160             sc->mandatory_count++;
1161           sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1162           GNUNET_CRYPTO_hash (keyword,
1163                               strlen (keyword),
1164                               &sc->requests[i].key);
1165         }
1166     }
1167   sc->client = GNUNET_CLIENT_connect (sc->h->sched,
1168                                       "fs",
1169                                       sc->h->cfg);
1170   if (NULL == sc->client)
1171     return GNUNET_SYSERR;
1172   GNUNET_CLIENT_notify_transmit_ready (sc->client,
1173                                        size,
1174                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1175                                        GNUNET_NO,
1176                                        &transmit_search_request,
1177                                        sc);
1178   return GNUNET_OK;
1179 }
1180
1181
1182 /**
1183  * Freeze probes for the given search result.
1184  *
1185  * @param cls the global FS handle
1186  * @param key the key for the search result (unused)
1187  * @param value the search result to free
1188  * @return GNUNET_OK
1189  */
1190 static int
1191 search_result_freeze_probes (void *cls,
1192                              const GNUNET_HashCode * key,
1193                              void *value)
1194 {
1195   struct GNUNET_FS_SearchContext *sc = cls;
1196   struct GNUNET_FS_Handle *h = sc->h;
1197   struct GNUNET_FS_SearchResult *sr = value;
1198
1199   if (sr->probe_ctx != NULL)
1200     {
1201       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1202       sr->probe_ctx = NULL;
1203     }
1204   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1205     {
1206       GNUNET_SCHEDULER_cancel (h->sched,
1207                                sr->probe_cancel_task);  
1208       sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1209     }
1210   if (sr->update_search != NULL)
1211     GNUNET_FS_search_pause (sr->update_search);
1212   return GNUNET_OK;
1213 }
1214
1215
1216 /**
1217  * Resume probes for the given search result.
1218  *
1219  * @param cls the global FS handle
1220  * @param key the key for the search result (unused)
1221  * @param value the search result to free
1222  * @return GNUNET_OK
1223  */
1224 static int
1225 search_result_resume_probes (void *cls,
1226                              const GNUNET_HashCode * key,
1227                              void *value)
1228 {
1229   struct GNUNET_FS_SearchResult *sr = value;
1230
1231   GNUNET_FS_search_start_probe_ (sr);
1232   if (sr->update_search != NULL)
1233     GNUNET_FS_search_continue (sr->update_search);
1234   return GNUNET_OK;
1235 }
1236
1237
1238 /**
1239  * Signal suspend and free the given search result.
1240  *
1241  * @param cls the global FS handle
1242  * @param key the key for the search result (unused)
1243  * @param value the search result to free
1244  * @return GNUNET_OK
1245  */
1246 static int
1247 search_result_suspend (void *cls,
1248                        const GNUNET_HashCode * key,
1249                        void *value)
1250 {
1251   struct GNUNET_FS_SearchContext *sc = cls;
1252   struct GNUNET_FS_Handle *h = sc->h;
1253   struct GNUNET_FS_SearchResult *sr = value;
1254   struct GNUNET_FS_ProgressInfo pi;
1255
1256   if (sr->download != NULL)
1257     GNUNET_FS_download_signal_suspend_ (sr->download);
1258   if (sr->update_search != NULL)
1259     GNUNET_FS_search_signal_suspend_ (sr->update_search);
1260   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1261   pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1262   pi.value.search.specifics.result_suspend.meta = sr->meta;
1263   pi.value.search.specifics.result_suspend.uri = sr->uri;
1264   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1265   GNUNET_break (NULL == sr->client_info);
1266   GNUNET_free_non_null (sr->serialization);
1267   GNUNET_FS_uri_destroy (sr->uri);
1268   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1269   if (sr->probe_ctx != NULL)
1270     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1271   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1272     GNUNET_SCHEDULER_cancel (h->sched,
1273                              sr->probe_cancel_task);    
1274   GNUNET_free (sr);
1275   return GNUNET_OK;
1276 }
1277
1278
1279 /**
1280  * Create SUSPEND event for the given search operation
1281  * and then clean up our state (without stop signal).
1282  *
1283  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1284  */
1285 void
1286 GNUNET_FS_search_signal_suspend_ (void *cls)
1287 {
1288   struct GNUNET_FS_SearchContext *sc = cls;
1289   struct GNUNET_FS_ProgressInfo pi;
1290   unsigned int i;
1291
1292   GNUNET_FS_end_top (sc->h, sc->top);
1293   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1294                                          &search_result_suspend,
1295                                          sc);
1296   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1297   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1298   GNUNET_break (NULL == sc->client_info);
1299   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1300     GNUNET_SCHEDULER_cancel (sc->h->sched,
1301                              sc->task);
1302   if (NULL != sc->client)
1303     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1304   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1305   if (sc->requests != NULL)
1306     {
1307       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1308       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1309         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1310     }
1311   GNUNET_free_non_null (sc->requests);
1312   GNUNET_free_non_null (sc->emsg);
1313   GNUNET_FS_uri_destroy (sc->uri);
1314   GNUNET_free_non_null (sc->serialization);
1315   GNUNET_free (sc);
1316 }
1317
1318
1319 /**
1320  * Start search for content.
1321  *
1322  * @param h handle to the file sharing subsystem
1323  * @param uri specifies the search parameters; can be
1324  *        a KSK URI or an SKS URI.
1325  * @param anonymity desired level of anonymity
1326  * @param options options for the search
1327  * @param cctx initial value for the client context
1328  * @return context that can be used to control the search
1329  */
1330 struct GNUNET_FS_SearchContext *
1331 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1332                         const struct GNUNET_FS_Uri *uri,
1333                         uint32_t anonymity,
1334                         enum GNUNET_FS_SearchOptions options,
1335                         void *cctx)
1336 {
1337   struct GNUNET_FS_SearchContext *ret;
1338   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1339   if (ret == NULL)
1340     return NULL;
1341   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_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   if (NULL != sr->update_search)
1430     {
1431       GNUNET_FS_search_stop (sr->update_search);
1432       GNUNET_assert (sr->update_search == NULL);
1433     }
1434   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1435   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1436   pi.value.search.specifics.result_stopped.meta = sr->meta;
1437   pi.value.search.specifics.result_stopped.uri = sr->uri;
1438   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1439   GNUNET_break (NULL == sr->client_info);
1440   GNUNET_free_non_null (sr->serialization);
1441   GNUNET_FS_uri_destroy (sr->uri);
1442   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1443   if (sr->probe_ctx != NULL)
1444     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1445   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1446     GNUNET_SCHEDULER_cancel (h->sched,
1447                              sr->probe_cancel_task);    
1448   GNUNET_free (sr);
1449   return GNUNET_OK;
1450 }
1451
1452
1453 /**
1454  * Stop search for content.
1455  *
1456  * @param sc context for the search that should be stopped
1457  */
1458 void 
1459 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1460 {
1461   struct GNUNET_FS_ProgressInfo pi;
1462   unsigned int i;
1463
1464   if (sc->top != NULL)
1465     GNUNET_FS_end_top (sc->h, sc->top);
1466   if (sc->psearch_result != NULL)
1467     sc->psearch_result->update_search = NULL;
1468   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1469                                          &search_result_free,
1470                                          sc);
1471   if (sc->serialization != NULL)
1472     {
1473       GNUNET_FS_remove_sync_file_ (sc->h,
1474                                    (sc->psearch_result != NULL)  
1475                                    ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1476                                    : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1477                                    sc->serialization);
1478       GNUNET_FS_remove_sync_dir_ (sc->h,
1479                                   (sc->psearch_result != NULL)  
1480                                   ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1481                                   : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1482                                   sc->serialization);
1483       GNUNET_free (sc->serialization);
1484     }
1485   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1486   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1487   GNUNET_break (NULL == sc->client_info);
1488   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1489     GNUNET_SCHEDULER_cancel (sc->h->sched,
1490                              sc->task);
1491   if (NULL != sc->client)
1492     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1493   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1494   if (sc->requests != NULL)
1495     {
1496       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1497       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1498         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1499     }
1500   GNUNET_free_non_null (sc->requests);
1501   GNUNET_free_non_null (sc->emsg);
1502   GNUNET_FS_uri_destroy (sc->uri);
1503   GNUNET_free (sc);
1504 }
1505
1506 /* end of fs_search.c */