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