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