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