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