526e6513bdf1ec6d53e9fb3da5758c85e3b3e91b
[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       GNUNET_break (0);
847       break;
848     case GNUNET_BLOCK_TYPE_FS_DBLOCK:
849       GNUNET_break (0);
850       break;
851     case GNUNET_BLOCK_TYPE_FS_ONDEMAND:
852       GNUNET_break (0);
853       break;
854     case GNUNET_BLOCK_TYPE_FS_IBLOCK:
855       GNUNET_break (0);
856       break;
857     default:
858       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
859                   _("Got result with unknown block type `%d', ignoring"),
860                   type);
861       break;
862     }
863 }
864
865
866 /**
867  * Shutdown any existing connection to the FS
868  * service and try to establish a fresh one
869  * (and then re-transmit our search request).
870  *
871  * @param sc the search to reconnec
872  */
873 static void 
874 try_reconnect (struct GNUNET_FS_SearchContext *sc);
875
876
877 /**
878  * Type of a function to call when we receive a message
879  * from the service.
880  *
881  * @param cls closure
882  * @param msg message received, NULL on timeout or fatal error
883  */
884 static void 
885 receive_results (void *cls,
886                  const struct GNUNET_MessageHeader * msg)
887 {
888   struct GNUNET_FS_SearchContext *sc = cls;
889   const struct PutMessage *cm;
890   uint16_t msize;
891
892   if ( (NULL == msg) ||
893        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
894        (ntohs (msg->size) <= sizeof (struct PutMessage)) )
895     {
896       try_reconnect (sc);
897       return;
898     }
899   msize = ntohs (msg->size);
900   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
901               "Receiving %u bytes of result from fs service\n",
902               msize);
903   cm = (const struct PutMessage*) msg;
904   process_result (sc, 
905                   ntohl (cm->type),
906                   GNUNET_TIME_absolute_ntoh (cm->expiration),
907                   &cm[1],
908                   msize - sizeof (struct PutMessage));
909   /* continue receiving */
910   GNUNET_CLIENT_receive (sc->client,
911                          &receive_results,
912                          sc,
913                          GNUNET_TIME_UNIT_FOREVER_REL);
914 }
915
916
917 /**
918  * Schedule the transmission of the (next) search request
919  * to the service.
920  *
921  * @param sc context for the search
922  */
923 static void
924 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc);
925
926
927 /**
928  * Closure for 'build_result_set'.
929  */
930 struct MessageBuilderContext
931 {
932   /**
933    * How many entries can we store to xoff.
934    */
935   unsigned int put_cnt;
936
937   /**
938    * How many entries should we skip.
939    */
940   unsigned int skip_cnt;
941
942   /**
943    * Where to store the keys.
944    */
945   GNUNET_HashCode *xoff;
946
947   /**
948    * Search context we are iterating for.
949    */
950   struct GNUNET_FS_SearchContext *sc;
951
952   /**
953    * URI the search result must match, NULL for any
954    */
955   struct GNUNET_FS_Uri *uri;
956 };
957
958
959 /**
960  * Iterating over the known results, pick those
961  * matching the given result range and store
962  * their keys at 'xoff'.
963  *
964  * @param cls the 'struct MessageBuilderContext'
965  * @param key key for a result
966  * @param value the search result
967  * @return GNUNET_OK to continue iterating
968  */
969 static int
970 build_result_set (void *cls,
971                   const GNUNET_HashCode *key,
972                   void *value)
973 {
974   struct MessageBuilderContext *mbc = cls;
975   struct GNUNET_FS_SearchResult *sr = value;
976   
977   if ( (mbc->uri != NULL) &&
978        (GNUNET_YES != GNUNET_FS_uri_test_equal (mbc->uri,
979                                                 sr->uri)) )
980     return GNUNET_OK;
981   if (mbc->skip_cnt > 0)
982     {
983       mbc->skip_cnt--;
984       return GNUNET_OK;
985     }
986   if (mbc->put_cnt == 0)
987     return GNUNET_SYSERR;
988   mbc->sc->search_request_map_offset++;
989   mbc->xoff[--mbc->put_cnt] = *key;
990   return GNUNET_OK;
991 }
992
993
994 /**
995  * Iterating over the known results, count those
996  * matching the given result range and increment
997  * put count for each.
998  *
999  * @param cls the 'struct MessageBuilderContext'
1000  * @param key key for a result
1001  * @param value the search result
1002  * @return GNUNET_OK to continue iterating
1003  */
1004 static int
1005 find_result_set (void *cls,
1006                   const GNUNET_HashCode *key,
1007                   void *value)
1008 {
1009   struct MessageBuilderContext *mbc = cls;
1010   struct GNUNET_FS_SearchResult *sr = value;
1011   
1012   if ( (mbc->uri != NULL) &&
1013        (GNUNET_YES != GNUNET_FS_uri_test_equal (mbc->uri,
1014                                                 sr->uri)) )
1015     return GNUNET_OK;
1016   mbc->put_cnt++;
1017   return GNUNET_OK;
1018 }
1019
1020
1021 /**
1022  * We're ready to transmit the search request to the
1023  * file-sharing service.  Do it.
1024  *
1025  * @param cls closure
1026  * @param size number of bytes available in buf
1027  * @param buf where the callee should write the message
1028  * @return number of bytes written to buf
1029  */
1030 static size_t
1031 transmit_search_request (void *cls,
1032                          size_t size, 
1033                          void *buf)
1034 {
1035   struct GNUNET_FS_SearchContext *sc = cls;
1036   struct MessageBuilderContext mbc;
1037   size_t msize;
1038   struct SearchMessage *sm;
1039   const char *identifier;
1040   GNUNET_HashCode key;
1041   GNUNET_HashCode idh;
1042   unsigned int sqms;
1043
1044   if (NULL == buf)
1045     {
1046       try_reconnect (sc);
1047       return 0;
1048     }
1049   mbc.sc = sc;
1050   mbc.skip_cnt = sc->search_request_map_offset;
1051   sm = buf;
1052   sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1053   mbc.xoff = (GNUNET_HashCode* ) &sm[1];
1054   if (GNUNET_FS_uri_test_ksk (sc->uri))
1055     {
1056       msize = sizeof (struct SearchMessage);
1057       GNUNET_assert (size >= msize);
1058       mbc.uri = NULL;
1059       mbc.put_cnt = 0;
1060       GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1061                                              &find_result_set,
1062                                              &mbc);
1063       sqms = mbc.put_cnt;
1064       mbc.put_cnt = (size - msize) / sizeof (GNUNET_HashCode);
1065       mbc.put_cnt = GNUNET_MIN (mbc.put_cnt,
1066                                 sqms - mbc.skip_cnt);
1067       if (sc->search_request_map_offset < sqms)
1068         GNUNET_assert (mbc.put_cnt > 0);
1069
1070       sm->header.size = htons (msize);
1071       if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1072         sm->options = htonl (1);
1073       else
1074         sm->options = htonl (0);          
1075       sm->type = htonl (GNUNET_BLOCK_TYPE_ANY);
1076       sm->anonymity_level = htonl (sc->anonymity);
1077       memset (&sm->target, 0, sizeof (GNUNET_HashCode));
1078       sm->query = sc->requests[sc->keyword_offset].query;
1079       msize += sizeof (GNUNET_HashCode) * mbc.put_cnt;
1080       GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1081                                              &build_result_set,
1082                                              &mbc);
1083       sm->header.size = htons (msize);
1084       if (sqms != sc->search_request_map_offset)
1085         {
1086           /* more requesting to be done... */
1087           schedule_transmit_search_request (sc);
1088           return msize;
1089         }
1090       sc->keyword_offset++;
1091       if (sc->uri->data.ksk.keywordCount !=
1092           sc->keyword_offset)
1093         {
1094           /* more requesting to be done... */
1095           schedule_transmit_search_request (sc);
1096           return msize;
1097         }
1098     }
1099   else
1100     {
1101       GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1102       msize = sizeof (struct SearchMessage);
1103       GNUNET_assert (size >= msize);
1104       if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
1105         sm->options = htonl (1);
1106       else
1107         sm->options = htonl (0);      
1108       sm->type = htonl (GNUNET_BLOCK_TYPE_FS_SBLOCK);
1109       sm->anonymity_level = htonl (sc->anonymity);
1110       sm->target = sc->uri->data.sks.namespace;
1111       identifier = sc->uri->data.sks.identifier;
1112       GNUNET_CRYPTO_hash (identifier,
1113                           strlen (identifier),
1114                           &key);
1115       GNUNET_CRYPTO_hash (&key,
1116                           sizeof (GNUNET_HashCode),
1117                           &idh);
1118       GNUNET_CRYPTO_hash_xor (&idh,
1119                               &sm->target,
1120                               &sm->query);
1121       mbc.put_cnt = (size - msize) / sizeof (GNUNET_HashCode);
1122       sqms = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map);
1123       mbc.put_cnt = GNUNET_MIN (mbc.put_cnt,
1124                                 sqms - mbc.skip_cnt);
1125       mbc.uri = NULL;
1126       if (sc->search_request_map_offset < sqms)
1127         GNUNET_assert (mbc.put_cnt > 0);
1128       msize += sizeof (GNUNET_HashCode) * mbc.put_cnt;
1129       GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1130                                              &build_result_set,
1131                                              &mbc);
1132       sm->header.size = htons (msize);
1133       if (sqms != sc->search_request_map_offset)
1134         {
1135           /* more requesting to be done... */
1136           schedule_transmit_search_request (sc);
1137           return msize;
1138         }
1139     }
1140   GNUNET_CLIENT_receive (sc->client,
1141                          &receive_results,
1142                          sc,
1143                          GNUNET_TIME_UNIT_FOREVER_REL);
1144   return msize;
1145 }
1146
1147
1148 /**
1149  * Schedule the transmission of the (next) search request
1150  * to the service.
1151  *
1152  * @param sc context for the search
1153  */
1154 static void
1155 schedule_transmit_search_request (struct GNUNET_FS_SearchContext *sc)
1156 {
1157   size_t size;
1158   unsigned int sqms;
1159   unsigned int fit;
1160
1161   size = sizeof (struct SearchMessage);
1162   sqms = GNUNET_CONTAINER_multihashmap_size (sc->master_result_map) - sc->search_request_map_offset;  
1163   fit = (GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - size) / sizeof (GNUNET_HashCode);
1164   fit = GNUNET_MIN (fit, sqms);
1165   size += sizeof (GNUNET_HashCode) * fit;
1166   GNUNET_CLIENT_notify_transmit_ready (sc->client,
1167                                        size,
1168                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1169                                        GNUNET_NO,
1170                                        &transmit_search_request,
1171                                        sc);  
1172
1173 }
1174
1175
1176 /**
1177  * Reconnect to the FS service and transmit
1178  * our queries NOW.
1179  *
1180  * @param cls our search context
1181  * @param tc unused
1182  */
1183 static void
1184 do_reconnect (void *cls,
1185               const struct GNUNET_SCHEDULER_TaskContext *tc)
1186 {
1187   struct GNUNET_FS_SearchContext *sc = cls;
1188   struct GNUNET_CLIENT_Connection *client;
1189  
1190   sc->task = GNUNET_SCHEDULER_NO_TASK;
1191   client = GNUNET_CLIENT_connect ("fs",
1192                                   sc->h->cfg);
1193   if (NULL == client)
1194     {
1195       try_reconnect (sc);
1196       return;
1197     }
1198   sc->client = client;
1199   sc->search_request_map_offset = 0;
1200   sc->keyword_offset = 0;
1201   schedule_transmit_search_request (sc);
1202 }
1203
1204
1205 /**
1206  * Shutdown any existing connection to the FS
1207  * service and try to establish a fresh one
1208  * (and then re-transmit our search request).
1209  *
1210  * @param sc the search to reconnec
1211  */
1212 static void 
1213 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1214 {
1215   if (NULL != sc->client)
1216     {
1217       GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1218       sc->client = NULL;
1219     }
1220   sc->task
1221     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1222                                     &do_reconnect,
1223                                     sc);
1224 }
1225
1226
1227 /**
1228  * Start search for content, internal API.
1229  *
1230  * @param h handle to the file sharing subsystem
1231  * @param uri specifies the search parameters; can be
1232  *        a KSK URI or an SKS URI.
1233  * @param anonymity desired level of anonymity
1234  * @param options options for the search
1235  * @param cctx initial value for the client context
1236  * @param psearch parent search result (for namespace update searches)
1237  * @return context that can be used to control the search
1238  */
1239 static struct GNUNET_FS_SearchContext *
1240 search_start (struct GNUNET_FS_Handle *h,
1241               const struct GNUNET_FS_Uri *uri,
1242               uint32_t anonymity,
1243               enum GNUNET_FS_SearchOptions options,
1244               void *cctx,
1245               struct GNUNET_FS_SearchResult *psearch)
1246 {
1247   struct GNUNET_FS_SearchContext *sc;
1248   struct GNUNET_FS_ProgressInfo pi;
1249
1250   sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
1251   sc->h = h;
1252   sc->options = options;
1253   sc->uri = GNUNET_FS_uri_dup (uri);
1254   sc->anonymity = anonymity;
1255   sc->start_time = GNUNET_TIME_absolute_get ();
1256   if (psearch != NULL)
1257     {
1258       sc->psearch_result = psearch;  
1259       psearch->update_search = sc;
1260     }
1261   sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1262   sc->client_info = cctx;
1263   if (GNUNET_OK !=
1264       GNUNET_FS_search_start_searching_ (sc))
1265     {
1266       GNUNET_FS_uri_destroy (sc->uri);
1267       GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1268       GNUNET_free (sc);      
1269       return NULL;
1270     }
1271   GNUNET_FS_search_sync_ (sc);
1272   pi.status = GNUNET_FS_STATUS_SEARCH_START;
1273   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1274   return sc;
1275 }
1276
1277
1278 /**
1279  * Build the request and actually initiate the search using the
1280  * GNUnet FS service.
1281  *
1282  * @param sc search context
1283  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1284  */
1285 int
1286 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1287 {
1288   unsigned int i;
1289   const char *keyword;
1290   GNUNET_HashCode hc;
1291   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;  
1292   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1293
1294   GNUNET_assert (NULL == sc->client);
1295   if (GNUNET_FS_uri_test_ksk (sc->uri))
1296     {
1297       GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1298       sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1299                                     sc->uri->data.ksk.keywordCount);
1300       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1301         {
1302           keyword = &sc->uri->data.ksk.keywords[i][1];
1303           GNUNET_CRYPTO_hash (keyword, 
1304                               strlen (keyword), 
1305                               &hc);
1306           pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1307           GNUNET_assert (pk != NULL);
1308           GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1309           GNUNET_CRYPTO_rsa_key_free (pk);
1310           GNUNET_CRYPTO_hash (&pub,
1311                               sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), 
1312                               &sc->requests[i].query);
1313           sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1314           if (sc->requests[i].mandatory)
1315             sc->mandatory_count++;
1316           sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1317           GNUNET_CRYPTO_hash (keyword,
1318                               strlen (keyword),
1319                               &sc->requests[i].key);
1320         }
1321     }
1322   sc->client = GNUNET_CLIENT_connect ("fs",
1323                                       sc->h->cfg);
1324   if (NULL == sc->client)
1325     return GNUNET_SYSERR;
1326   schedule_transmit_search_request (sc);
1327   return GNUNET_OK;
1328 }
1329
1330
1331 /**
1332  * Freeze probes for the given search result.
1333  *
1334  * @param cls the global FS handle
1335  * @param key the key for the search result (unused)
1336  * @param value the search result to free
1337  * @return GNUNET_OK
1338  */
1339 static int
1340 search_result_freeze_probes (void *cls,
1341                              const GNUNET_HashCode * key,
1342                              void *value)
1343 {
1344   struct GNUNET_FS_SearchResult *sr = value;
1345
1346   if (sr->probe_ctx != NULL)
1347     {
1348       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1349       sr->probe_ctx = NULL;
1350     }
1351   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1352     {
1353       GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1354       sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1355     }
1356   if (sr->update_search != NULL)
1357     GNUNET_FS_search_pause (sr->update_search);
1358   return GNUNET_OK;
1359 }
1360
1361
1362 /**
1363  * Resume probes for the given search result.
1364  *
1365  * @param cls the global FS handle
1366  * @param key the key for the search result (unused)
1367  * @param value the search result to free
1368  * @return GNUNET_OK
1369  */
1370 static int
1371 search_result_resume_probes (void *cls,
1372                              const GNUNET_HashCode * key,
1373                              void *value)
1374 {
1375   struct GNUNET_FS_SearchResult *sr = value;
1376
1377   GNUNET_FS_search_start_probe_ (sr);
1378   if (sr->update_search != NULL)
1379     GNUNET_FS_search_continue (sr->update_search);
1380   return GNUNET_OK;
1381 }
1382
1383
1384 /**
1385  * Signal suspend and free the given search result.
1386  *
1387  * @param cls the global FS handle
1388  * @param key the key for the search result (unused)
1389  * @param value the search result to free
1390  * @return GNUNET_OK
1391  */
1392 static int
1393 search_result_suspend (void *cls,
1394                        const GNUNET_HashCode * key,
1395                        void *value)
1396 {
1397   struct GNUNET_FS_SearchContext *sc = cls;
1398   struct GNUNET_FS_SearchResult *sr = value;
1399   struct GNUNET_FS_ProgressInfo pi;
1400
1401   if (sr->download != NULL)
1402     GNUNET_FS_download_signal_suspend_ (sr->download);
1403   if (sr->update_search != NULL)
1404     GNUNET_FS_search_signal_suspend_ (sr->update_search);
1405   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1406   pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1407   pi.value.search.specifics.result_suspend.meta = sr->meta;
1408   pi.value.search.specifics.result_suspend.uri = sr->uri;
1409   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1410   GNUNET_break (NULL == sr->client_info);
1411   GNUNET_free_non_null (sr->serialization);
1412   GNUNET_FS_uri_destroy (sr->uri);
1413   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1414   if (sr->probe_ctx != NULL)
1415     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1416   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1417     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1418   GNUNET_free (sr);
1419   return GNUNET_OK;
1420 }
1421
1422
1423 /**
1424  * Create SUSPEND event for the given search operation
1425  * and then clean up our state (without stop signal).
1426  *
1427  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1428  */
1429 void
1430 GNUNET_FS_search_signal_suspend_ (void *cls)
1431 {
1432   struct GNUNET_FS_SearchContext *sc = cls;
1433   struct GNUNET_FS_ProgressInfo pi;
1434   unsigned int i;
1435
1436   GNUNET_FS_end_top (sc->h, sc->top);
1437   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1438                                          &search_result_suspend,
1439                                          sc);
1440   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1441   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1442   GNUNET_break (NULL == sc->client_info);
1443   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1444     GNUNET_SCHEDULER_cancel (sc->task);
1445   if (NULL != sc->client)
1446     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1447   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1448   if (sc->requests != NULL)
1449     {
1450       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1451       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1452         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1453     }
1454   GNUNET_free_non_null (sc->requests);
1455   GNUNET_free_non_null (sc->emsg);
1456   GNUNET_FS_uri_destroy (sc->uri);
1457   GNUNET_free_non_null (sc->serialization);
1458   GNUNET_free (sc);
1459 }
1460
1461
1462 /**
1463  * Start search for content.
1464  *
1465  * @param h handle to the file sharing subsystem
1466  * @param uri specifies the search parameters; can be
1467  *        a KSK URI or an SKS URI.
1468  * @param anonymity desired level of anonymity
1469  * @param options options for the search
1470  * @param cctx initial value for the client context
1471  * @return context that can be used to control the search
1472  */
1473 struct GNUNET_FS_SearchContext *
1474 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1475                         const struct GNUNET_FS_Uri *uri,
1476                         uint32_t anonymity,
1477                         enum GNUNET_FS_SearchOptions options,
1478                         void *cctx)
1479 {
1480   struct GNUNET_FS_SearchContext *ret;
1481   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1482   if (ret == NULL)
1483     return NULL;
1484   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1485   return ret;
1486 }
1487
1488
1489 /**
1490  * Pause search.  
1491  *
1492  * @param sc context for the search that should be paused
1493  */
1494 void 
1495 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1496 {
1497   struct GNUNET_FS_ProgressInfo pi;
1498
1499   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1500     GNUNET_SCHEDULER_cancel (sc->task);
1501   sc->task = GNUNET_SCHEDULER_NO_TASK;
1502   if (NULL != sc->client)
1503     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1504   sc->client = NULL;
1505   GNUNET_FS_search_sync_ (sc);
1506   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1507                                          &search_result_freeze_probes,
1508                                          sc);
1509   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1510   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1511 }
1512
1513
1514 /**
1515  * Continue paused search.
1516  *
1517  * @param sc context for the search that should be resumed
1518  */
1519 void 
1520 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1521 {
1522   struct GNUNET_FS_ProgressInfo pi;
1523
1524   GNUNET_assert (sc->client == NULL);
1525   GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
1526   do_reconnect (sc, NULL);
1527   GNUNET_FS_search_sync_ (sc);
1528   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1529   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1530   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1531                                          &search_result_resume_probes,
1532                                          sc);
1533 }
1534
1535
1536 /**
1537  * Free the given search result.
1538  *
1539  * @param cls the global FS handle
1540  * @param key the key for the search result (unused)
1541  * @param value the search result to free
1542  * @return GNUNET_OK
1543  */
1544 static int
1545 search_result_free (void *cls,
1546                     const GNUNET_HashCode * key,
1547                     void *value)
1548 {
1549   struct GNUNET_FS_SearchContext *sc = cls;
1550   struct GNUNET_FS_SearchResult *sr = value;
1551   struct GNUNET_FS_ProgressInfo pi;
1552
1553   if (NULL != sr->download)
1554     {
1555       sr->download->search = NULL;
1556       sr->download->top = GNUNET_FS_make_top (sr->download->h,
1557                                               &GNUNET_FS_download_signal_suspend_,
1558                                               sr->download);
1559       if (NULL != sr->download->serialization)
1560         {
1561           GNUNET_FS_remove_sync_file_ (sc->h,
1562                                        GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1563                                        sr->download->serialization);
1564           GNUNET_free (sr->download->serialization);
1565           sr->download->serialization = NULL;
1566         }
1567       pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1568       GNUNET_FS_download_make_status_ (&pi,
1569                                        sr->download);      
1570       GNUNET_FS_download_sync_ (sr->download);
1571       sr->download = NULL;     
1572     }
1573   if (NULL != sr->update_search)
1574     {
1575       GNUNET_FS_search_stop (sr->update_search);
1576       GNUNET_assert (sr->update_search == NULL);
1577     }
1578   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1579   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1580   pi.value.search.specifics.result_stopped.meta = sr->meta;
1581   pi.value.search.specifics.result_stopped.uri = sr->uri;
1582   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1583   GNUNET_break (NULL == sr->client_info);
1584   GNUNET_free_non_null (sr->serialization);
1585   GNUNET_FS_uri_destroy (sr->uri);
1586   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1587   if (sr->probe_ctx != NULL)
1588     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);    
1589   if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1590     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1591   GNUNET_free (sr);
1592   return GNUNET_OK;
1593 }
1594
1595
1596 /**
1597  * Stop search for content.
1598  *
1599  * @param sc context for the search that should be stopped
1600  */
1601 void 
1602 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1603 {
1604   struct GNUNET_FS_ProgressInfo pi;
1605   unsigned int i;
1606
1607   if (sc->top != NULL)
1608     GNUNET_FS_end_top (sc->h, sc->top);
1609   if (sc->psearch_result != NULL)
1610     sc->psearch_result->update_search = NULL;
1611   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1612                                          &search_result_free,
1613                                          sc);
1614   if (sc->serialization != NULL)
1615     {
1616       GNUNET_FS_remove_sync_file_ (sc->h,
1617                                    (sc->psearch_result != NULL)  
1618                                    ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1619                                    : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1620                                    sc->serialization);
1621       GNUNET_FS_remove_sync_dir_ (sc->h,
1622                                   (sc->psearch_result != NULL)  
1623                                   ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH 
1624                                   : GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1625                                   sc->serialization);
1626       GNUNET_free (sc->serialization);
1627     }
1628   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1629   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1630   GNUNET_break (NULL == sc->client_info);
1631   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1632     GNUNET_SCHEDULER_cancel (sc->task);
1633   if (NULL != sc->client)
1634     GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1635   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1636   if (sc->requests != NULL)
1637     {
1638       GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1639       for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1640         GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1641     }
1642   GNUNET_free_non_null (sc->requests);
1643   GNUNET_free_non_null (sc->emsg);
1644   GNUNET_FS_uri_destroy (sc->uri);
1645   GNUNET_free (sc);
1646 }
1647
1648 /* end of fs_search.c */