(no commit message)
[oweals/gnunet.git] / src / fs / fs_search.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file fs/fs_search.c
23  * @brief Helper functions for searching.
24  * @author Christian Grothoff
25  *
26  * 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->probe_cancel_task);
283           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
284         }     
285       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
286                                                             &probe_failure_handler,
287                                                             sr);
288       break;
289     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
290       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
291         {
292           GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
293           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
294         }     
295       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
296                                                             &probe_success_handler,
297                                                             sr);
298       break;
299     case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
300       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
301         {
302           GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
303           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
304         }     
305       sr = NULL;
306       break;
307     case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
308       GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
309       sr->probe_active_time = GNUNET_TIME_absolute_get ();
310       sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->remaining_probe_time,
311                                                             &probe_failure_handler,
312                                                             sr);
313       break;
314     case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
315       if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
316         {
317           GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
318           sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
319         }
320       dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
321       sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
322                                                                 dur);
323       GNUNET_FS_search_result_sync_ (sr);
324       break;
325     default:
326       GNUNET_break (0);
327       return NULL;
328     }
329   return sr;
330 }
331
332
333 /**
334  * Start download probes for the given search result.
335  *
336  * @param sr the search result
337  */
338 void
339 GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
340 {
341   uint64_t off;
342   uint64_t len;
343   
344   if (sr->probe_ctx != NULL)
345     return;
346   if (sr->download != NULL)
347     return;
348   if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
349     return;
350   if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
351     return;
352   len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
353   if (len == 0)
354     return;
355   if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
356     return;
357   off = len / DBLOCK_SIZE;
358   if (off > 0)
359     off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
360   off *= DBLOCK_SIZE;
361   if (len - off < DBLOCK_SIZE)
362     len = len - off;
363   else
364     len = DBLOCK_SIZE;
365   sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
366                                                             2 * (1 + sr->availability_trials));
367   sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
368                                             sr->uri,
369                                             sr->meta,
370                                             NULL, NULL,
371                                             off, len, 
372                                             sr->sc->anonymity,
373                                             GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
374                                             GNUNET_FS_DOWNLOAD_IS_PROBE,
375                                             sr, NULL);    
376 }
377
378
379 /**
380  * We have received a KSK result.  Check how it fits in with the
381  * overall query and notify the client accordingly.
382  *
383  * @param sc context for the overall query
384  * @param ent entry for the specific keyword
385  * @param uri the URI that was found
386  * @param meta metadata associated with the URI
387  *        under the "ent" keyword
388  */
389 static void
390 process_ksk_result (struct GNUNET_FS_SearchContext *sc, 
391                     struct SearchRequestEntry *ent,
392                     const struct GNUNET_FS_Uri *uri,
393                     const struct GNUNET_CONTAINER_MetaData *meta)
394 {
395   GNUNET_HashCode key;
396   struct GNUNET_FS_SearchResult *sr;
397   struct GetResultContext grc;
398   int is_new;
399
400   /* check if new */
401   GNUNET_FS_uri_to_key (uri, &key);
402   if (GNUNET_SYSERR ==
403       GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
404                                                   &key,
405                                                   &test_result_present,
406                                                   (void*) uri))
407     return; /* duplicate result */
408   /* try to find search result in master map */
409   grc.sr = NULL;
410   grc.uri = uri;
411   GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
412                                               &key,
413                                               &get_result_present,
414                                               &grc);
415   sr = grc.sr;
416   is_new = (NULL == sr) || (sr->mandatory_missing > 0);
417   if (NULL == sr)
418     {
419       sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
420       sr->sc = sc;
421       sr->uri = GNUNET_FS_uri_dup (uri);
422       sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
423       sr->mandatory_missing = sc->mandatory_count;
424       sr->key = key;
425       GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
426                                          &key,
427                                          sr,
428                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
429     }
430   else
431     {
432       GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
433     }
434   /* check if mandatory satisfied */
435   if (ent->mandatory)
436     sr->mandatory_missing--;
437   else
438     sr->optional_support++;
439   if (0 != sr->mandatory_missing)
440     return;
441   if (is_new)
442     notify_client_chk_result (sc, sr);
443   else
444     notify_client_chk_update (sc, sr);
445   GNUNET_FS_search_result_sync_ (sr);
446   GNUNET_FS_search_start_probe_ (sr);
447 }
448
449
450 /**
451  * Start search for content, internal API.
452  *
453  * @param h handle to the file sharing subsystem
454  * @param uri specifies the search parameters; can be
455  *        a KSK URI or an SKS URI.
456  * @param anonymity desired level of anonymity
457  * @param options options for the search
458  * @param cctx client context
459  * @param psearch parent search result (for namespace update searches)
460  * @return context that can be used to control the search
461  */
462 static struct GNUNET_FS_SearchContext *
463 search_start (struct GNUNET_FS_Handle *h,
464               const struct GNUNET_FS_Uri *uri,
465               uint32_t anonymity,
466               enum GNUNET_FS_SearchOptions options,
467               void *cctx,
468               struct GNUNET_FS_SearchResult *psearch);
469
470
471 /**
472  * We have received an SKS result.  Start searching for updates and
473  * notify the client if it is a new result.
474  *
475  * @param sc context for the overall query
476  * @param id_update identifier for updates, NULL for none
477  * @param uri the URI that was found
478  * @param meta metadata associated with the URI
479   */
480 static void
481 process_sks_result (struct GNUNET_FS_SearchContext *sc, 
482                     const char *id_update,
483                     const struct GNUNET_FS_Uri *uri,
484                     const struct GNUNET_CONTAINER_MetaData *meta)
485 {
486   struct GNUNET_FS_Uri uu;
487   GNUNET_HashCode key;
488   struct GNUNET_FS_SearchResult *sr;
489
490   /* check if new */
491   GNUNET_FS_uri_to_key (uri, &key);
492   GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
493                           &uri->data.chk.chk.query,
494                           &key);
495   if (GNUNET_SYSERR ==
496       GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
497                                                   &key,
498                                                   &test_result_present,
499                                                   (void*) uri))
500     return; /* duplicate result */
501   sr = GNUNET_malloc (sizeof (struct GNUNET_FS_SearchResult));
502   sr->sc = sc;
503   sr->uri = GNUNET_FS_uri_dup (uri);
504   sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
505   sr->key = key;
506   GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
507                                      &key,
508                                      sr,
509                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
510   GNUNET_FS_search_result_sync_ (sr);
511   GNUNET_FS_search_start_probe_ (sr);
512   /* notify client */
513   notify_client_chk_result (sc, sr);
514   /* search for updates */
515   if (strlen (id_update) == 0)
516     return; /* no updates */
517   uu.type = sks;
518   uu.data.sks.namespace = sc->uri->data.sks.namespace;
519   uu.data.sks.identifier = GNUNET_strdup (id_update);
520   (void) search_start (sc->h,
521                        &uu,
522                        sc->anonymity,
523                        sc->options,
524                        NULL,
525                        sr);
526   GNUNET_free (uu.data.sks.identifier);
527 }
528
529
530 /**
531  * Process a keyword-search result.
532  *
533  * @param sc our search context
534  * @param kb the kblock
535  * @param size size of kb
536  */
537 static void
538 process_kblock (struct GNUNET_FS_SearchContext *sc,
539                 const struct KBlock *kb,
540                 size_t size)
541 {
542   unsigned int i;
543   size_t j;
544   GNUNET_HashCode q;
545   char pt[size - sizeof (struct KBlock)];
546   struct GNUNET_CRYPTO_AesSessionKey skey;
547   struct GNUNET_CRYPTO_AesInitializationVector iv;
548   const char *eos;
549   struct GNUNET_CONTAINER_MetaData *meta;
550   struct GNUNET_FS_Uri *uri;
551   char *emsg;
552   
553   GNUNET_CRYPTO_hash (&kb->keyspace,
554                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
555                       &q);
556   /* find key */
557   for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
558     if (0 == memcmp (&q,
559                      &sc->requests[i].query,
560                      sizeof (GNUNET_HashCode)))
561       break;
562   if (i == sc->uri->data.ksk.keywordCount)
563     {
564       /* oops, does not match any of our keywords!? */
565       GNUNET_break (0);
566       return;
567     }
568   /* decrypt */
569   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
570   if (-1 == GNUNET_CRYPTO_aes_decrypt (&kb[1],
571                                        size - sizeof (struct KBlock),
572                                        &skey,
573                                        &iv,
574                                        pt))
575     {
576       GNUNET_break (0);
577       return;
578     }
579   /* parse */
580   eos = memchr (pt, 0, sizeof (pt));
581   if (NULL == eos)
582     {
583       GNUNET_break_op (0);
584       return;
585     }
586   j = eos - pt + 1;
587   if (sizeof (pt) == j)
588     meta = GNUNET_CONTAINER_meta_data_create ();
589   else
590     meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
591                                                    sizeof (pt) - j);
592   if (meta == NULL)
593     {
594       GNUNET_break_op (0);       /* kblock malformed */
595       return;
596     }
597   uri = GNUNET_FS_uri_parse (pt, &emsg);
598   if (uri == NULL)
599     {
600       GNUNET_break_op (0);       /* kblock malformed */
601       GNUNET_free_non_null (emsg);
602       GNUNET_CONTAINER_meta_data_destroy (meta);
603       return;
604     }
605   /* process */
606   process_ksk_result (sc, &sc->requests[i], uri, meta);
607
608   /* clean up */
609   GNUNET_CONTAINER_meta_data_destroy (meta);
610   GNUNET_FS_uri_destroy (uri);
611 }
612
613
614 /**
615  * Process a keyword-search result with a namespace advertisment.
616  *
617  * @param sc our search context
618  * @param nb the nblock
619  * @param size size of nb
620  */
621 static void
622 process_nblock (struct GNUNET_FS_SearchContext *sc,
623                 const struct NBlock *nb,
624                 size_t size)
625 {
626   unsigned int i;
627   size_t j;
628   GNUNET_HashCode q;
629   char pt[size - sizeof (struct NBlock)];
630   struct GNUNET_CRYPTO_AesSessionKey skey;
631   struct GNUNET_CRYPTO_AesInitializationVector iv;
632   const char *eos;
633   struct GNUNET_CONTAINER_MetaData *meta;
634   struct GNUNET_FS_Uri *uri;
635   char *uris;
636   
637   GNUNET_CRYPTO_hash (&nb->keyspace,
638                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
639                       &q);
640   /* find key */
641   for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
642     if (0 == memcmp (&q,
643                      &sc->requests[i].query,
644                      sizeof (GNUNET_HashCode)))
645       break;
646   if (i == sc->uri->data.ksk.keywordCount)
647     {
648       /* oops, does not match any of our keywords!? */
649       GNUNET_break (0);
650       return;
651     }
652   /* decrypt */
653   GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
654   if (-1 == GNUNET_CRYPTO_aes_decrypt (&nb[1],
655                                        size - sizeof (struct NBlock),
656                                        &skey,
657                                        &iv,
658                                        pt))
659     {
660       GNUNET_break (0);
661       return;
662     }
663   /* parse */
664   eos = memchr (pt, 0, sizeof (pt));
665   if (NULL == eos)
666     {
667       GNUNET_break_op (0);
668       return;
669     }
670   j = eos - pt + 1;
671   if (sizeof (pt) == j)
672     meta = GNUNET_CONTAINER_meta_data_create ();
673   else
674     meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
675                                                    sizeof (pt) - j);
676   if (meta == NULL)
677     {
678       GNUNET_break_op (0);       /* nblock malformed */
679       return;
680     }
681
682   uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
683   uri->type = sks;
684   uri->data.sks.identifier = GNUNET_strdup (pt);
685   GNUNET_CRYPTO_hash (&nb->subspace,
686                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
687                       &uri->data.sks.namespace);
688   uris = GNUNET_FS_uri_to_string (uri);
689   GNUNET_CONTAINER_meta_data_insert (meta,
690                                      "<gnunet>",
691                                      EXTRACTOR_METATYPE_URI,
692                                      EXTRACTOR_METAFORMAT_UTF8,
693                                      "text/plain",
694                                      uris,
695                                      strlen (uris)+1);
696   GNUNET_free (uris);
697   GNUNET_PSEUDONYM_add (sc->h->cfg,
698                         &uri->data.sks.namespace,
699                         meta);
700   /* process */
701   process_ksk_result (sc, &sc->requests[i], uri, meta);
702
703   /* clean up */
704   GNUNET_CONTAINER_meta_data_destroy (meta);
705   GNUNET_FS_uri_destroy (uri);
706 }
707
708
709 /**
710  * Process a namespace-search result.
711  *
712  * @param sc our search context
713  * @param sb the sblock
714  * @param size size of sb
715  */
716 static void
717 process_sblock (struct GNUNET_FS_SearchContext *sc,
718                 const struct SBlock *sb,
719                 size_t size)
720 {
721   size_t len = size - sizeof (struct SBlock);
722   char pt[len];
723   struct GNUNET_CRYPTO_AesSessionKey skey;
724   struct GNUNET_CRYPTO_AesInitializationVector iv;
725   struct GNUNET_FS_Uri *uri;
726   struct GNUNET_CONTAINER_MetaData *meta;
727   const char *id;
728   const char *uris;
729   size_t off;
730   char *emsg;
731   GNUNET_HashCode key;
732   char *identifier;
733
734   /* decrypt */
735   identifier = sc->uri->data.sks.identifier;
736   GNUNET_CRYPTO_hash (identifier, 
737                       strlen (identifier), 
738                       &key);
739   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
740   if (-1 == GNUNET_CRYPTO_aes_decrypt (&sb[1],
741                                        len,
742                                        &skey,
743                                        &iv,
744                                        pt))
745     {
746       GNUNET_break (0);
747       return;
748     }
749   /* parse */
750   off = GNUNET_STRINGS_buffer_tokenize (pt,
751                                         len, 
752                                         2, 
753                                         &id, 
754                                         &uris);
755   if (off == 0)
756     {
757       GNUNET_break_op (0);     /* sblock malformed */
758       return;
759     }
760   meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off], 
761                                                  len - off);
762   if (meta == NULL)
763     {
764       GNUNET_break_op (0);     /* sblock malformed */
765       return;
766     }
767   uri = GNUNET_FS_uri_parse (uris, &emsg);
768   if (uri == NULL)
769     {
770       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
771                   "Failed to parse URI `%s': %s\n",
772                   uris, emsg);
773       GNUNET_break_op (0);     /* sblock malformed */
774       GNUNET_free_non_null (emsg);
775       GNUNET_CONTAINER_meta_data_destroy (meta);
776       return;
777     }
778   /* process */
779   process_sks_result (sc, id, uri, meta);
780   /* clean up */
781   GNUNET_FS_uri_destroy (uri);
782   GNUNET_CONTAINER_meta_data_destroy (meta);
783 }
784
785
786 /**
787  * Process a search result.
788  *
789  * @param sc our search context
790  * @param type type of the result
791  * @param expiration when it will expire
792  * @param data the (encrypted) response
793  * @param size size of data
794  */
795 static void
796 process_result (struct GNUNET_FS_SearchContext *sc,
797                 enum GNUNET_BLOCK_Type type,
798                 struct GNUNET_TIME_Absolute expiration,
799                 const void *data,
800                 size_t size)
801 {
802   if (GNUNET_TIME_absolute_get_duration (expiration).rel_value > 0)
803     {
804       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
805                   "Result received has already expired.\n");
806       return; /* result expired */
807     }
808   switch (type)
809     {
810     case GNUNET_BLOCK_TYPE_FS_KBLOCK:
811       if (! GNUNET_FS_uri_test_ksk (sc->uri))
812         {
813           GNUNET_break (0);
814           return;
815         }
816       if (sizeof (struct KBlock) > size)
817         {
818           GNUNET_break_op (0);
819           return;
820         }
821       process_kblock (sc, data, size);
822       break;
823     case GNUNET_BLOCK_TYPE_FS_SBLOCK:
824       if (! GNUNET_FS_uri_test_sks (sc->uri))
825         {
826           GNUNET_break (0);
827           return;
828         }
829       if (sizeof (struct SBlock) > size)
830         {
831           GNUNET_break_op (0);
832           return;
833         }
834       process_sblock (sc, data, size);
835       break;
836     case GNUNET_BLOCK_TYPE_FS_NBLOCK:
837       if (! GNUNET_FS_uri_test_ksk (sc->uri))
838         {
839           GNUNET_break (0);
840           return;
841         }
842       if (sizeof (struct NBlock) > size)
843         {
844           GNUNET_break_op (0);
845           return;
846         }
847       process_nblock (sc, data, size);
848       break;
849     case GNUNET_BLOCK_TYPE_ANY:
850     case GNUNET_BLOCK_TYPE_FS_DBLOCK:
851     case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
852     case GNUNET_BLOCK_TYPE_FS_IBLOCK:
853       GNUNET_break (0);
854       break;
855     default:
856       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
857                   _("Got result with unknown block type `%d', ignoring"),
858                   type);
859       break;
860     }
861 }
862
863
864 /**
865  * Shutdown any existing connection to the FS
866  * service and try to establish a fresh one
867  * (and then re-transmit our search request).
868  *
869  * @param sc the search to reconnec
870  */
871 static void 
872 try_reconnect (struct GNUNET_FS_SearchContext *sc);
873
874
875 /**
876  * Type of a function to call when we receive a message
877  * from the service.
878  *
879  * @param cls closure
880  * @param msg message received, NULL on timeout or fatal error
881  */
882 static void 
883 receive_results (void *cls,
884                  const struct GNUNET_MessageHeader * msg)
885 {
886   struct GNUNET_FS_SearchContext *sc = cls;
887   const struct PutMessage *cm;
888   uint16_t msize;
889
890   if ( (NULL == msg) ||
891        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
892        (ntohs (msg->size) <= sizeof (struct PutMessage)) )
893     {
894       try_reconnect (sc);
895       return;
896     }
897   msize = ntohs (msg->size);
898   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
899               "Receiving %u bytes of result from fs service\n",
900               msize);
901   cm = (const struct PutMessage*) msg;
902   process_result (sc, 
903                   ntohl (cm->type),
904                   GNUNET_TIME_absolute_ntoh (cm->expiration),
905                   &cm[1],
906                   msize - sizeof (struct PutMessage));
907   /* continue receiving */
908   GNUNET_CLIENT_receive (sc->client,
909                          &receive_results,
910                          sc,
911                          GNUNET_TIME_UNIT_FOREVER_REL);
912 }
913
914
915 /**
916  * We're ready to transmit the search request to the
917  * file-sharing service.  Do it.
918  *
919  * @param cls closure
920  * @param size number of bytes available in buf
921  * @param buf where the callee should write the message
922  * @return number of bytes written to buf
923  */
924 static size_t
925 transmit_search_request (void *cls,
926                          size_t size, 
927                          void *buf)
928 {
929   struct GNUNET_FS_SearchContext *sc = cls;
930   size_t msize;
931   struct SearchMessage *sm;
932   unsigned int i;
933   const char *identifier;
934   GNUNET_HashCode key;
935   GNUNET_HashCode idh;
936
937   if (NULL == buf)
938     {
939       try_reconnect (sc);
940       return 0;
941     }
942   if (GNUNET_FS_uri_test_ksk (sc->uri))
943     {
944       msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
945       GNUNET_assert (size >= msize);
946       sm = buf;
947       memset (sm, 0, msize);
948       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
949         {
950           sm[i].header.size = htons (sizeof (struct SearchMessage));
951           sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
952           if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
953             sm[i].options = htonl (1);
954           else
955             sm[i].options = htonl (0);            
956           sm[i].type = htonl (GNUNET_BLOCK_TYPE_ANY);
957           sm[i].anonymity_level = htonl (sc->anonymity);
958           sm[i].query = sc->requests[i].query;
959           /* FIXME: should transmit hash codes of all already-known results here! 
960              (and if they do not fit, add another message with the same 
961              header and additional already-seen results!) */
962         }
963     }
964   else
965     {
966       GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
967       msize = sizeof (struct SearchMessage);
968       GNUNET_assert (size >= msize);
969       sm = buf;
970       memset (sm, 0, msize);
971       sm->header.size = htons (sizeof (struct SearchMessage));
972       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
973       if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
974         sm->options = htonl (1);
975       else
976         sm->options = htonl (0);      
977       sm->type = htonl (GNUNET_BLOCK_TYPE_FS_SBLOCK);
978       sm->anonymity_level = htonl (sc->anonymity);
979       sm->target = sc->uri->data.sks.namespace;
980       identifier = sc->uri->data.sks.identifier;
981       GNUNET_CRYPTO_hash (identifier,
982                           strlen (identifier),
983                           &key);
984       GNUNET_CRYPTO_hash (&key,
985                           sizeof (GNUNET_HashCode),
986                           &idh);
987       GNUNET_CRYPTO_hash_xor (&idh,
988                               &sm->target,
989                               &sm->query);
990       /* FIXME: should transmit hash codes of all already-known results here!
991          (and if they do not fit, add another message with the same 
992          header and additional already-seen results!) */      
993    }
994   GNUNET_CLIENT_receive (sc->client,
995                          &receive_results,
996                          sc,
997                          GNUNET_TIME_UNIT_FOREVER_REL);
998   return msize;
999 }
1000
1001
1002 /**
1003  * Reconnect to the FS service and transmit
1004  * our queries NOW.
1005  *
1006  * @param cls our search context
1007  * @param tc unused
1008  */
1009 static void
1010 do_reconnect (void *cls,
1011               const struct GNUNET_SCHEDULER_TaskContext *tc)
1012 {
1013   struct GNUNET_FS_SearchContext *sc = cls;
1014   struct GNUNET_CLIENT_Connection *client;
1015   size_t size;
1016   
1017   sc->task = GNUNET_SCHEDULER_NO_TASK;
1018   client = GNUNET_CLIENT_connect ("fs",
1019                                   sc->h->cfg);
1020   if (NULL == client)
1021     {
1022       try_reconnect (sc);
1023       return;
1024     }
1025   sc->client = client;
1026   if (GNUNET_FS_uri_test_ksk (sc->uri))
1027     size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1028   else
1029     size = sizeof (struct SearchMessage);
1030   GNUNET_CLIENT_notify_transmit_ready (client,
1031                                        size,
1032                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1033                                        GNUNET_NO,
1034                                        &transmit_search_request,
1035                                        sc);  
1036 }
1037
1038
1039 /**
1040  * Shutdown any existing connection to the FS
1041  * service and try to establish a fresh one
1042  * (and then re-transmit our search request).
1043  *
1044  * @param sc the search to reconnec
1045  */
1046 static void 
1047 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1048 {
1049   if (NULL != sc->client)
1050     {
1051       GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1052       sc->client = NULL;
1053     }
1054   sc->task
1055     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1056                                     &do_reconnect,
1057                                     sc);
1058 }
1059
1060
1061 /**
1062  * Start search for content, internal API.
1063  *
1064  * @param h handle to the file sharing subsystem
1065  * @param uri specifies the search parameters; can be
1066  *        a KSK URI or an SKS URI.
1067  * @param anonymity desired level of anonymity
1068  * @param options options for the search
1069  * @param cctx initial value for the client context
1070  * @param psearch parent search result (for namespace update searches)
1071  * @return context that can be used to control the search
1072  */
1073 static struct GNUNET_FS_SearchContext *
1074 search_start (struct GNUNET_FS_Handle *h,
1075               const struct GNUNET_FS_Uri *uri,
1076               uint32_t anonymity,
1077               enum GNUNET_FS_SearchOptions options,
1078               void *cctx,
1079               struct GNUNET_FS_SearchResult *psearch)
1080 {
1081   struct GNUNET_FS_SearchContext *sc;
1082   struct GNUNET_FS_ProgressInfo pi;
1083
1084   sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
1085   sc->h = h;
1086   sc->options = options;
1087   sc->uri = GNUNET_FS_uri_dup (uri);
1088   sc->anonymity = anonymity;
1089   sc->start_time = GNUNET_TIME_absolute_get ();
1090   if (psearch != NULL)
1091     {
1092       sc->psearch_result = psearch;  
1093       psearch->update_search = sc;
1094     }
1095   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1096   sc->client_info = cctx;
1097   if (GNUNET_OK !=
1098       GNUNET_FS_search_start_searching_ (sc))
1099     {
1100       GNUNET_FS_uri_destroy (sc->uri);
1101       GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1102       GNUNET_free (sc);      
1103       return NULL;
1104     }
1105   GNUNET_FS_search_sync_ (sc);
1106   pi.status = GNUNET_FS_STATUS_SEARCH_START;
1107   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1108   return sc;
1109 }
1110
1111
1112 /**
1113  * Build the request and actually initiate the search using the
1114  * GNUnet FS service.
1115  *
1116  * @param sc search context
1117  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1118  */
1119 int
1120 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1121 {
1122   unsigned int i;
1123   const char *keyword;
1124   GNUNET_HashCode hc;
1125   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;  
1126   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1127   size_t size;
1128
1129   GNUNET_assert (NULL == sc->client);
1130   if (GNUNET_FS_uri_test_ksk (sc->uri))
1131     {
1132       size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1133     }
1134   else
1135     {
1136       GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1137       size = sizeof (struct SearchMessage);
1138     }
1139   if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1140     {
1141       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1142                   _("Too many keywords specified for a single search."));
1143       return GNUNET_SYSERR;
1144     }
1145   if (GNUNET_FS_uri_test_ksk (sc->uri))
1146     {
1147       GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1148       sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1149                                     sc->uri->data.ksk.keywordCount);
1150       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1151         {
1152           keyword = &sc->uri->data.ksk.keywords[i][1];
1153           GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
1154           pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1155           GNUNET_assert (pk != NULL);
1156           GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1157           GNUNET_CRYPTO_rsa_key_free (pk);
1158           GNUNET_CRYPTO_hash (&pub,
1159                               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
1160                               &sc->requests[i].query);
1161           sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1162           if (sc->requests[i].mandatory)
1163             sc->mandatory_count++;
1164           sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1165           GNUNET_CRYPTO_hash (keyword,
1166                               strlen (keyword),
1167                               &sc->requests[i].key);
1168         }
1169     }
1170   sc->client = GNUNET_CLIENT_connect ("fs",
1171                                       sc->h->cfg);
1172   if (NULL == sc->client)
1173     return GNUNET_SYSERR;
1174   GNUNET_CLIENT_notify_transmit_ready (sc->client,
1175                                        size,
1176                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1177                                        GNUNET_NO,
1178                                        &transmit_search_request,
1179                                        sc);
1180   return GNUNET_OK;
1181 }
1182
1183
1184 /**
1185  * Freeze probes for the given search result.
1186  *
1187  * @param cls the global FS handle
1188  * @param key the key for the search result (unused)
1189  * @param value the search result to free
1190  * @return GNUNET_OK
1191  */
1192 static int
1193 search_result_freeze_probes (void *cls,
1194                              const GNUNET_HashCode * key,
1195                              void *value)
1196 {
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 (sr->probe_cancel_task);
1207       sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1208     }
1209   if (sr->update_search != NULL)
1210     GNUNET_FS_search_pause (sr->update_search);
1211   return GNUNET_OK;
1212 }
1213
1214
1215 /**
1216  * Resume probes for the given search result.
1217  *
1218  * @param cls the global FS handle
1219  * @param key the key for the search result (unused)
1220  * @param value the search result to free
1221  * @return GNUNET_OK
1222  */
1223 static int
1224 search_result_resume_probes (void *cls,
1225                              const GNUNET_HashCode * key,
1226                              void *value)
1227 {
1228   struct GNUNET_FS_SearchResult *sr = value;
1229
1230   GNUNET_FS_search_start_probe_ (sr);
1231   if (sr->update_search != NULL)
1232     GNUNET_FS_search_continue (sr->update_search);
1233   return GNUNET_OK;
1234 }
1235
1236
1237 /**
1238  * Signal suspend and free the given search result.
1239  *
1240  * @param cls the global FS handle
1241  * @param key the key for the search result (unused)
1242  * @param value the search result to free
1243  * @return GNUNET_OK
1244  */
1245 static int
1246 search_result_suspend (void *cls,
1247                        const GNUNET_HashCode * key,
1248                        void *value)
1249 {
1250   struct GNUNET_FS_SearchContext *sc = cls;
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 (sr->probe_cancel_task);
1271   GNUNET_free (sr);
1272   return GNUNET_OK;
1273 }
1274
1275
1276 /**
1277  * Create SUSPEND event for the given search operation
1278  * and then clean up our state (without stop signal).
1279  *
1280  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1281  */
1282 void
1283 GNUNET_FS_search_signal_suspend_ (void *cls)
1284 {
1285   struct GNUNET_FS_SearchContext *sc = cls;
1286   struct GNUNET_FS_ProgressInfo pi;
1287   unsigned int i;
1288
1289   GNUNET_FS_end_top (sc->h, sc->top);
1290   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1291                                          &search_result_suspend,
1292                                          sc);
1293   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1294   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1295   GNUNET_break (NULL == sc->client_info);
1296   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1297     GNUNET_SCHEDULER_cancel (sc->task);
1298   if (NULL != sc->client)
1299     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1300   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1301   if (sc->requests != NULL)
1302     {
1303       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1304       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1305         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1306     }
1307   GNUNET_free_non_null (sc->requests);
1308   GNUNET_free_non_null (sc->emsg);
1309   GNUNET_FS_uri_destroy (sc->uri);
1310   GNUNET_free_non_null (sc->serialization);
1311   GNUNET_free (sc);
1312 }
1313
1314
1315 /**
1316  * Start search for content.
1317  *
1318  * @param h handle to the file sharing subsystem
1319  * @param uri specifies the search parameters; can be
1320  *        a KSK URI or an SKS URI.
1321  * @param anonymity desired level of anonymity
1322  * @param options options for the search
1323  * @param cctx initial value for the client context
1324  * @return context that can be used to control the search
1325  */
1326 struct GNUNET_FS_SearchContext *
1327 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1328                         const struct GNUNET_FS_Uri *uri,
1329                         uint32_t anonymity,
1330                         enum GNUNET_FS_SearchOptions options,
1331                         void *cctx)
1332 {
1333   struct GNUNET_FS_SearchContext *ret;
1334   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1335   if (ret == NULL)
1336     return 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->task);
1354   sc->task = GNUNET_SCHEDULER_NO_TASK;
1355   if (NULL != sc->client)
1356     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1357   sc->client = NULL;
1358   GNUNET_FS_search_sync_ (sc);
1359   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1360                                          &search_result_freeze_probes,
1361                                          sc);
1362   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1363   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1364 }
1365
1366
1367 /**
1368  * Continue paused search.
1369  *
1370  * @param sc context for the search that should be resumed
1371  */
1372 void 
1373 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1374 {
1375   struct GNUNET_FS_ProgressInfo pi;
1376
1377   GNUNET_assert (sc->client == NULL);
1378   GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
1379   do_reconnect (sc, NULL);
1380   GNUNET_FS_search_sync_ (sc);
1381   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1382   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1383   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1384                                          &search_result_resume_probes,
1385                                          sc);
1386 }
1387
1388
1389 /**
1390  * Free the given search result.
1391  *
1392  * @param cls the global FS handle
1393  * @param key the key for the search result (unused)
1394  * @param value the search result to free
1395  * @return GNUNET_OK
1396  */
1397 static int
1398 search_result_free (void *cls,
1399                     const GNUNET_HashCode * key,
1400                     void *value)
1401 {
1402   struct GNUNET_FS_SearchContext *sc = cls;
1403   struct GNUNET_FS_SearchResult *sr = value;
1404   struct GNUNET_FS_ProgressInfo pi;
1405
1406   if (NULL != sr->download)
1407     {
1408       sr->download->search = NULL;
1409       sr->download->top = GNUNET_FS_make_top (sr->download->h,
1410                                               &GNUNET_FS_download_signal_suspend_,
1411                                               sr->download);
1412       if (NULL != sr->download->serialization)
1413         {
1414           GNUNET_FS_remove_sync_file_ (sc->h,
1415                                        GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1416                                        sr->download->serialization);
1417           GNUNET_free (sr->download->serialization);
1418           sr->download->serialization = NULL;
1419         }
1420       pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1421       GNUNET_FS_download_make_status_ (&pi,
1422                                        sr->download);      
1423       GNUNET_FS_download_sync_ (sr->download);
1424       sr->download = NULL;     
1425     }
1426   if (NULL != sr->update_search)
1427     {
1428       GNUNET_FS_search_stop (sr->update_search);
1429       GNUNET_assert (sr->update_search == NULL);
1430     }
1431   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1432   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1433   pi.value.search.specifics.result_stopped.meta = sr->meta;
1434   pi.value.search.specifics.result_stopped.uri = sr->uri;
1435   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1436   GNUNET_break (NULL == sr->client_info);
1437   GNUNET_free_non_null (sr->serialization);
1438   GNUNET_FS_uri_destroy (sr->uri);
1439   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1440   if (sr->probe_ctx != NULL)
1441     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1442   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1443     GNUNET_SCHEDULER_cancel (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->task);
1486   if (NULL != sc->client)
1487     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1488   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1489   if (sc->requests != NULL)
1490     {
1491       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1492       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1493         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1494     }
1495   GNUNET_free_non_null (sc->requests);
1496   GNUNET_free_non_null (sc->emsg);
1497   GNUNET_FS_uri_destroy (sc->uri);
1498   GNUNET_free (sc);
1499 }
1500
1501 /* end of fs_search.c */