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