-options to play with
[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 ( (chk != sr->uri->type) && (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 = 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 = 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_RsaPublicKeyBinaryEncoded pub;
1292   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1293
1294   GNUNET_assert (NULL == sc->client);
1295   if (GNUNET_FS_uri_test_ksk (sc->uri))
1296   {
1297     GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1298     sc->requests =
1299         GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1300                        sc->uri->data.ksk.keywordCount);
1301     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1302     {
1303       keyword = &sc->uri->data.ksk.keywords[i][1];
1304       GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
1305       pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1306       GNUNET_assert (NULL != pk);
1307       GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1308       GNUNET_CRYPTO_rsa_key_free (pk);
1309       GNUNET_CRYPTO_hash (&pub,
1310                           sizeof (struct
1311                                   GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1312                           &sc->requests[i].query);
1313       sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1314       if (sc->requests[i].mandatory)
1315         sc->mandatory_count++;
1316       sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO);
1317       GNUNET_CRYPTO_hash (keyword, strlen (keyword), &sc->requests[i].key);
1318     }
1319   }
1320   sc->client = GNUNET_CLIENT_connect ("fs", sc->h->cfg);
1321   if (NULL == sc->client)
1322     return GNUNET_SYSERR;
1323   schedule_transmit_search_request (sc);
1324   return GNUNET_OK;
1325 }
1326
1327
1328 /**
1329  * Freeze probes for the given search result.
1330  *
1331  * @param cls the global FS handle
1332  * @param key the key for the search result (unused)
1333  * @param value the search result to free
1334  * @return GNUNET_OK
1335  */
1336 static int
1337 search_result_freeze_probes (void *cls, const struct GNUNET_HashCode * key,
1338                              void *value)
1339 {
1340   struct GNUNET_FS_SearchResult *sr = value;
1341
1342   if (NULL != sr->probe_ctx)
1343   {
1344     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1345     sr->probe_ctx = NULL;
1346   }
1347   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
1348   {
1349     GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
1350     sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
1351   }
1352   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_cancel_task)
1353   {
1354     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1355     sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1356   }
1357   if (NULL != sr->update_search)
1358     GNUNET_FS_search_pause (sr->update_search);
1359   return GNUNET_OK;
1360 }
1361
1362
1363 /**
1364  * Resume probes for the given search result.
1365  *
1366  * @param cls the global FS handle
1367  * @param key the key for the search result (unused)
1368  * @param value the search result to free
1369  * @return GNUNET_OK
1370  */
1371 static int
1372 search_result_resume_probes (void *cls, const struct GNUNET_HashCode * key,
1373                              void *value)
1374 {
1375   struct GNUNET_FS_SearchResult *sr = value;
1376
1377   GNUNET_FS_search_start_probe_ (sr);
1378   if (NULL != sr->update_search)
1379     GNUNET_FS_search_continue (sr->update_search);
1380   return GNUNET_OK;
1381 }
1382
1383
1384 /**
1385  * Signal suspend and free the given search result.
1386  *
1387  * @param cls the global FS handle
1388  * @param key the key for the search result (unused)
1389  * @param value the search result to free
1390  * @return GNUNET_OK
1391  */
1392 static int
1393 search_result_suspend (void *cls, const struct GNUNET_HashCode * key, void *value)
1394 {
1395   struct GNUNET_FS_SearchContext *sc = cls;
1396   struct GNUNET_FS_SearchResult *sr = value;
1397   struct GNUNET_FS_ProgressInfo pi;
1398
1399   if (NULL != sr->download)
1400   {
1401     GNUNET_FS_download_signal_suspend_ (sr->download);
1402     sr->download = NULL;
1403   }
1404   if (NULL != sr->probe_ctx)
1405   {
1406     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1407     sr->probe_ctx = NULL;
1408   }
1409   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
1410   {
1411     GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
1412     sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
1413   }
1414   if (NULL != sr->update_search)
1415   {
1416     GNUNET_FS_search_signal_suspend_ (sr->update_search);
1417     sr->update_search = NULL;
1418   }
1419   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_SUSPEND;
1420   pi.value.search.specifics.result_suspend.cctx = sr->client_info;
1421   pi.value.search.specifics.result_suspend.meta = sr->meta;
1422   pi.value.search.specifics.result_suspend.uri = sr->uri;
1423   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1424   GNUNET_break (NULL == sr->client_info);
1425   GNUNET_free_non_null (sr->serialization);
1426   GNUNET_FS_uri_destroy (sr->uri);
1427   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1428   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_cancel_task)
1429   {
1430     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1431     sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1432   }
1433   GNUNET_free_non_null (sr->keyword_bitmap);
1434   GNUNET_free (sr);
1435   return GNUNET_OK;
1436 }
1437
1438
1439 /**
1440  * Create SUSPEND event for the given search operation
1441  * and then clean up our state (without stop signal).
1442  *
1443  * @param cls the 'struct GNUNET_FS_SearchContext' to signal for
1444  */
1445 void
1446 GNUNET_FS_search_signal_suspend_ (void *cls)
1447 {
1448   struct GNUNET_FS_SearchContext *sc = cls;
1449   struct GNUNET_FS_ProgressInfo pi;
1450   unsigned int i;
1451
1452   GNUNET_FS_end_top (sc->h, sc->top);
1453   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1454                                          &search_result_suspend, sc);
1455   pi.status = GNUNET_FS_STATUS_SEARCH_SUSPEND;
1456   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1457   GNUNET_break (NULL == sc->client_info);
1458   if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1459     GNUNET_SCHEDULER_cancel (sc->task);
1460   if (NULL != sc->client)
1461     GNUNET_CLIENT_disconnect (sc->client);
1462   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1463   if (NULL != sc->requests)
1464   {
1465     GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1466     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1467       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1468   }
1469   GNUNET_free_non_null (sc->requests);
1470   GNUNET_free_non_null (sc->emsg);
1471   GNUNET_FS_uri_destroy (sc->uri);
1472   GNUNET_free_non_null (sc->serialization);
1473   GNUNET_free (sc);
1474 }
1475
1476
1477 /**
1478  * Start search for content.
1479  *
1480  * @param h handle to the file sharing subsystem
1481  * @param uri specifies the search parameters; can be
1482  *        a KSK URI or an SKS URI.
1483  * @param anonymity desired level of anonymity
1484  * @param options options for the search
1485  * @param cctx initial value for the client context
1486  * @return context that can be used to control the search
1487  */
1488 struct GNUNET_FS_SearchContext *
1489 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1490                         const struct GNUNET_FS_Uri *uri, uint32_t anonymity,
1491                         enum GNUNET_FS_SearchOptions options, void *cctx)
1492 {
1493   struct GNUNET_FS_SearchContext *ret;
1494
1495   ret = search_start (h, uri, anonymity, options, cctx, NULL);
1496   if (NULL == ret)
1497     return NULL;
1498   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_search_signal_suspend_, ret);
1499   return ret;
1500 }
1501
1502
1503 /**
1504  * Pause search.
1505  *
1506  * @param sc context for the search that should be paused
1507  */
1508 void
1509 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1510 {
1511   struct GNUNET_FS_ProgressInfo pi;
1512
1513   if (GNUNET_SCHEDULER_NO_TASK != sc->task)
1514     GNUNET_SCHEDULER_cancel (sc->task);
1515   sc->task = GNUNET_SCHEDULER_NO_TASK;
1516   if (NULL != sc->client)
1517     GNUNET_CLIENT_disconnect (sc->client);
1518   sc->client = NULL;
1519   GNUNET_FS_search_sync_ (sc);
1520   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1521                                          &search_result_freeze_probes, sc);
1522   pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1523   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1524 }
1525
1526
1527 /**
1528  * Continue paused search.
1529  *
1530  * @param sc context for the search that should be resumed
1531  */
1532 void
1533 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1534 {
1535   struct GNUNET_FS_ProgressInfo pi;
1536
1537   GNUNET_assert (NULL == sc->client);
1538   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sc->task);
1539   do_reconnect (sc, NULL);
1540   GNUNET_FS_search_sync_ (sc);
1541   pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1542   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1543   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1544                                          &search_result_resume_probes, sc);
1545 }
1546
1547
1548 /**
1549  * Signal stop for the given search result.
1550  *
1551  * @param cls the global FS handle
1552  * @param key the key for the search result (unused)
1553  * @param value the search result to free
1554  * @return GNUNET_OK
1555  */
1556 static int
1557 search_result_stop (void *cls, const struct GNUNET_HashCode * key, void *value)
1558 {
1559   struct GNUNET_FS_SearchContext *sc = cls;
1560   struct GNUNET_FS_SearchResult *sr = value;
1561   struct GNUNET_FS_ProgressInfo pi;
1562
1563   if (NULL != sr->probe_ctx)
1564   {
1565     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1566     sr->probe_ctx = NULL;
1567   }
1568   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_ping_task)
1569   {
1570     GNUNET_SCHEDULER_cancel (sr->probe_ping_task);
1571     sr->probe_ping_task = GNUNET_SCHEDULER_NO_TASK;
1572   }
1573   if (GNUNET_SCHEDULER_NO_TASK != sr->probe_cancel_task)
1574   {
1575     GNUNET_SCHEDULER_cancel (sr->probe_cancel_task);
1576     sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
1577   }
1578
1579   if (NULL != sr->download)
1580   {
1581     sr->download->search = NULL;
1582     sr->download->top =
1583         GNUNET_FS_make_top (sr->download->h,
1584                             &GNUNET_FS_download_signal_suspend_, sr->download);
1585     if (NULL != sr->download->serialization)
1586     {
1587       GNUNET_FS_remove_sync_file_ (sc->h, GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD,
1588                                    sr->download->serialization);
1589       GNUNET_free (sr->download->serialization);
1590       sr->download->serialization = NULL;
1591     }
1592     pi.status = GNUNET_FS_STATUS_DOWNLOAD_LOST_PARENT;
1593     GNUNET_FS_download_make_status_ (&pi, sr->download);
1594     GNUNET_FS_download_sync_ (sr->download);
1595     sr->download = NULL;
1596   }
1597   pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1598   pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1599   pi.value.search.specifics.result_stopped.meta = sr->meta;
1600   pi.value.search.specifics.result_stopped.uri = sr->uri;
1601   sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1602   return GNUNET_OK;
1603 }
1604
1605
1606 /**
1607  * Free the given search result.
1608  *
1609  * @param cls the global FS handle
1610  * @param key the key for the search result (unused)
1611  * @param value the search result to free
1612  * @return GNUNET_OK
1613  */
1614 static int
1615 search_result_free (void *cls, const struct GNUNET_HashCode * key, void *value)
1616 {
1617   struct GNUNET_FS_SearchResult *sr = value;
1618
1619   if (NULL != sr->update_search)
1620   {
1621     GNUNET_FS_search_stop (sr->update_search);
1622     GNUNET_assert (NULL == sr->update_search);
1623   }
1624   GNUNET_break (NULL == sr->probe_ctx);
1625   GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sr->probe_cancel_task);
1626   GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sr->probe_ping_task);
1627   GNUNET_break (NULL == sr->client_info);
1628   GNUNET_free_non_null (sr->serialization);
1629   GNUNET_FS_uri_destroy (sr->uri);
1630   GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1631   GNUNET_free_non_null (sr->keyword_bitmap);
1632   GNUNET_free (sr);
1633   return GNUNET_OK;
1634 }
1635
1636
1637 /**
1638  * Stop search for content.
1639  *
1640  * @param sc context for the search that should be stopped
1641  */
1642 void
1643 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1644 {
1645   struct GNUNET_FS_ProgressInfo pi;
1646   unsigned int i;
1647
1648   if (NULL != sc->top)
1649     GNUNET_FS_end_top (sc->h, sc->top);
1650   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1651                                          &search_result_stop, sc);
1652   if (NULL != sc->psearch_result)
1653     sc->psearch_result->update_search = NULL;
1654   if (NULL != sc->serialization)
1655   {
1656     GNUNET_FS_remove_sync_file_ (sc->h,
1657                                  (sc->psearch_result !=
1658                                   NULL) ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH :
1659                                  GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1660                                  sc->serialization);
1661     GNUNET_FS_remove_sync_dir_ (sc->h,
1662                                 (sc->psearch_result !=
1663                                  NULL) ? GNUNET_FS_SYNC_PATH_CHILD_SEARCH :
1664                                 GNUNET_FS_SYNC_PATH_MASTER_SEARCH,
1665                                 sc->serialization);
1666     GNUNET_free (sc->serialization);
1667   }
1668   pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1669   sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1670   GNUNET_break (NULL == sc->client_info);
1671   if (GNUNET_SCHEDULER_NO_TASK != sc->task)
1672     GNUNET_SCHEDULER_cancel (sc->task);
1673   if (NULL != sc->client)
1674     GNUNET_CLIENT_disconnect (sc->client);
1675   GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1676                                          &search_result_free, sc);
1677   GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1678   if (NULL != sc->requests)
1679   {
1680     GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1681     for (i = 0; i < sc->uri->data.ksk.keywordCount; i++)
1682       GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1683   }
1684   GNUNET_free_non_null (sc->requests);
1685   GNUNET_free_non_null (sc->emsg);
1686   GNUNET_FS_uri_destroy (sc->uri);
1687   GNUNET_free (sc);
1688 }
1689
1690 /* end of fs_search.c */