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