2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
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.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file fs/fs_search.c
23 * @brief Helper functions for searching.
24 * @author Christian Grothoff
27 * - insert code for serialization where needed
28 * - remove *directory* with search results upon completion
29 * - centralize code that sprintf's the 'pbuf[32]' strings
30 * - add support for pushing "already seen" information
31 * to FS service for bloomfilter (can wait)
35 #include "gnunet_constants.h"
36 #include "gnunet_fs_service.h"
37 #include "gnunet_protocols.h"
40 #define DEBUG_SEARCH GNUNET_NO
45 * Fill in all of the generic fields for a search event and
48 * @param pi structure to fill in
49 * @param sc overall search context
50 * @return value returned by the callback
53 GNUNET_FS_search_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
54 struct GNUNET_FS_SearchContext *sc)
56 pi->value.search.sc = sc;
60 = (sc->parent == NULL) ? NULL : sc->parent->client_info;
61 pi->value.search.query
63 pi->value.search.duration = GNUNET_TIME_absolute_get_duration (sc->start_time);
64 pi->value.search.anonymity = sc->anonymity;
65 return sc->h->upcb (sc->h->upcb_cls,
71 * Check if the given result is identical
74 * @param cls points to the URI we check against
76 * @param value a "struct SearchResult" who's URI we
78 * @return GNUNET_SYSERR if the result is present,
82 test_result_present (void *cls,
83 const GNUNET_HashCode * key,
86 const struct GNUNET_FS_Uri *uri = cls;
87 struct SearchResult *sr = value;
89 if (GNUNET_FS_uri_test_equal (uri,
97 * We've found a new CHK result. Let the client
100 * @param sc the search context
101 * @param sr the specific result
104 notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
105 struct SearchResult *sr)
107 struct GNUNET_FS_ProgressInfo pi;
109 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
110 pi.value.search.specifics.result.meta = sr->meta;
111 pi.value.search.specifics.result.uri = sr->uri;
112 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
117 * We've found new information about an existing CHK result. Let the
118 * client know about it.
120 * @param sc the search context
121 * @param sr the specific result
124 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
125 struct SearchResult *sr)
127 struct GNUNET_FS_ProgressInfo pi;
129 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
130 pi.value.search.specifics.update.cctx = sr->client_info;
131 pi.value.search.specifics.update.meta = sr->meta;
132 pi.value.search.specifics.update.uri = sr->uri;
133 pi.value.search.specifics.update.availability_rank
134 = 2*sr->availability_success - sr->availability_trials;
135 pi.value.search.specifics.update.availability_certainty
136 = sr->availability_trials;
137 pi.value.search.specifics.update.applicability_rank
138 = sr->optional_support;
139 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
144 * Context for "get_result_present".
146 struct GetResultContext
149 * The URI we're looking for.
151 const struct GNUNET_FS_Uri *uri;
154 * Where to store a pointer to the search
155 * result struct if we found a match.
157 struct SearchResult *sr;
162 * Check if the given result is identical to the given URI and if so
165 * @param cls a "struct GetResultContext"
166 * @param key not used
167 * @param value a "struct SearchResult" who's URI we
168 * should compare with
172 get_result_present (void *cls,
173 const GNUNET_HashCode * key,
176 struct GetResultContext *grc = cls;
177 struct SearchResult *sr = value;
179 if (GNUNET_FS_uri_test_equal (grc->uri,
187 * Signal result of last probe to client and then schedule next
191 signal_probe_result (struct SearchResult *sr)
193 struct GNUNET_FS_ProgressInfo pi;
195 pi.status = GNUNET_FS_STATUS_SEARCH_START;
196 pi.value.search.specifics.update.cctx = sr->client_info;
197 pi.value.search.specifics.update.meta = sr->meta;
198 pi.value.search.specifics.update.uri = sr->uri;
199 pi.value.search.specifics.update.availability_rank = sr->availability_success;
200 pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
201 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
202 sr->sc->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
203 GNUNET_FS_search_start_probe_ (sr);
208 * Handle the case where we have failed to receive a response for our probe.
210 * @param cls our 'struct SearchResult*'
211 * @param tc scheduler context
214 probe_failure_handler (void *cls,
215 const struct GNUNET_SCHEDULER_TaskContext *tc)
217 struct SearchResult *sr = cls;
218 sr->availability_trials++;
219 signal_probe_result (sr);
224 * Handle the case where we have gotten a response for our probe.
226 * @param cls our 'struct SearchResult*'
227 * @param tc scheduler context
230 probe_success_handler (void *cls,
231 const struct GNUNET_SCHEDULER_TaskContext *tc)
233 struct SearchResult *sr = cls;
234 sr->availability_trials++;
235 sr->availability_success++;
236 signal_probe_result (sr);
241 * Notification of FS that a search probe has made progress.
242 * This function is used INSTEAD of the client's event handler
243 * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
245 * @param cls closure, always NULL (!), actual closure
246 * is in the client-context of the info struct
247 * @param info details about the event, specifying the event type
248 * and various bits about the event
249 * @return client-context (for the next progress call
250 * for this operation; should be set to NULL for
251 * SUSPEND and STOPPED events). The value returned
252 * will be passed to future callbacks in the respective
253 * field in the GNUNET_FS_ProgressInfo struct.
256 GNUNET_FS_search_probe_progress_ (void *cls,
257 const struct GNUNET_FS_ProgressInfo *info)
259 struct SearchResult *sr = info->value.download.cctx;
260 struct GNUNET_TIME_Relative dur;
262 switch (info->status)
264 case GNUNET_FS_STATUS_DOWNLOAD_START:
267 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
268 /* probes should never be resumed */
271 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
272 /* probes should never be suspended */
275 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
278 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
279 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
281 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
282 sr->probe_cancel_task);
283 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
285 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
286 sr->remaining_probe_time,
287 &probe_failure_handler,
290 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
291 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
293 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
294 sr->probe_cancel_task);
295 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
297 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
298 sr->remaining_probe_time,
299 &probe_success_handler,
302 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
303 /* FIXME: clean up? schedule next probe? or already done? */
306 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
307 GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
308 sr->probe_active_time = GNUNET_TIME_absolute_get ();
309 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
310 sr->remaining_probe_time,
311 &probe_failure_handler,
314 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
315 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
317 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
318 sr->probe_cancel_task);
319 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
321 dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
322 sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
334 * Start download probes for the given search result.
336 * @param sr the search result
339 GNUNET_FS_search_start_probe_ (struct SearchResult *sr)
344 if (sr->probe_ctx != NULL)
346 if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
348 if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
350 len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
353 if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
355 off = len / DBLOCK_SIZE;
357 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
359 if (len - off < DBLOCK_SIZE)
363 sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
364 2 * (1 + sr->availability_trials));
365 sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
371 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
372 GNUNET_FS_DOWNLOAD_IS_PROBE,
378 * We have received a KSK result. Check how it fits in with the
379 * overall query and notify the client accordingly.
381 * @param sc context for the overall query
382 * @param ent entry for the specific keyword
383 * @param uri the URI that was found
384 * @param meta metadata associated with the URI
385 * under the "ent" keyword
388 process_ksk_result (struct GNUNET_FS_SearchContext *sc,
389 struct SearchRequestEntry *ent,
390 const struct GNUNET_FS_Uri *uri,
391 const struct GNUNET_CONTAINER_MetaData *meta)
394 struct SearchResult *sr;
395 struct GetResultContext grc;
399 GNUNET_FS_uri_to_key (uri, &key);
401 GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
403 &test_result_present,
405 return; /* duplicate result */
406 /* try to find search result in master map */
409 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
414 is_new = (NULL == sr) || (sr->mandatory_missing > 0);
417 sr = GNUNET_malloc (sizeof (struct SearchResult));
419 sr->uri = GNUNET_FS_uri_dup (uri);
420 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
421 sr->mandatory_missing = sc->mandatory_count;
422 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
425 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
429 GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
431 /* check if mandatory satisfied */
433 sr->mandatory_missing--;
435 sr->optional_support++;
436 if (0 != sr->mandatory_missing)
439 notify_client_chk_result (sc, sr);
441 notify_client_chk_update (sc, sr);
442 GNUNET_FS_search_start_probe_ (sr);
447 * Start search for content, internal API.
449 * @param h handle to the file sharing subsystem
450 * @param uri specifies the search parameters; can be
451 * a KSK URI or an SKS URI.
452 * @param anonymity desired level of anonymity
453 * @param options options for the search
454 * @param cctx client context
455 * @param parent parent search (for namespace update searches)
456 * @return context that can be used to control the search
458 static struct GNUNET_FS_SearchContext *
459 search_start (struct GNUNET_FS_Handle *h,
460 const struct GNUNET_FS_Uri *uri,
462 enum GNUNET_FS_SearchOptions options,
464 struct GNUNET_FS_SearchContext *parent);
468 * We have received an SKS result. Start searching for updates and
469 * notify the client if it is a new result.
471 * @param sc context for the overall query
472 * @param id_update identifier for updates, NULL for none
473 * @param uri the URI that was found
474 * @param meta metadata associated with the URI
477 process_sks_result (struct GNUNET_FS_SearchContext *sc,
478 const char *id_update,
479 const struct GNUNET_FS_Uri *uri,
480 const struct GNUNET_CONTAINER_MetaData *meta)
482 struct GNUNET_FS_Uri uu;
484 struct SearchResult *sr;
487 GNUNET_FS_uri_to_key (uri, &key);
488 GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
489 &uri->data.chk.chk.query,
492 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
494 &test_result_present,
496 return; /* duplicate result */
497 sr = GNUNET_malloc (sizeof (struct SearchResult));
499 sr->uri = GNUNET_FS_uri_dup (uri);
500 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
501 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
504 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
505 GNUNET_FS_search_start_probe_ (sr);
507 notify_client_chk_result (sc, sr);
508 /* search for updates */
509 if (strlen (id_update) == 0)
510 return; /* no updates */
512 uu.data.sks.namespace = sc->uri->data.sks.namespace;
513 uu.data.sks.identifier = GNUNET_strdup (id_update);
514 /* FIXME: should attach update search
515 to the individual result, not
516 the entire SKS search! */
527 * Process a keyword-search result.
529 * @param sc our search context
530 * @param kb the kblock
531 * @param size size of kb
534 process_kblock (struct GNUNET_FS_SearchContext *sc,
535 const struct KBlock *kb,
541 char pt[size - sizeof (struct KBlock)];
542 struct GNUNET_CRYPTO_AesSessionKey skey;
543 struct GNUNET_CRYPTO_AesInitializationVector iv;
545 struct GNUNET_CONTAINER_MetaData *meta;
546 struct GNUNET_FS_Uri *uri;
549 GNUNET_CRYPTO_hash (&kb->keyspace,
550 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
553 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
555 &sc->requests[i].query,
556 sizeof (GNUNET_HashCode)))
558 if (i == sc->uri->data.ksk.keywordCount)
560 /* oops, does not match any of our keywords!? */
565 GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
566 GNUNET_CRYPTO_aes_decrypt (&kb[1],
567 size - sizeof (struct KBlock),
572 eos = memchr (pt, 0, sizeof (pt));
579 if (sizeof (pt) == j)
580 meta = GNUNET_CONTAINER_meta_data_create ();
582 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
586 GNUNET_break_op (0); /* kblock malformed */
589 uri = GNUNET_FS_uri_parse (pt, &emsg);
592 GNUNET_break_op (0); /* kblock malformed */
593 GNUNET_free_non_null (emsg);
594 GNUNET_CONTAINER_meta_data_destroy (meta);
598 process_ksk_result (sc, &sc->requests[i], uri, meta);
601 GNUNET_CONTAINER_meta_data_destroy (meta);
602 GNUNET_FS_uri_destroy (uri);
607 * Process a keyword-search result with a namespace advertisment.
609 * @param sc our search context
610 * @param nb the nblock
611 * @param size size of nb
614 process_nblock (struct GNUNET_FS_SearchContext *sc,
615 const struct NBlock *nb,
621 char pt[size - sizeof (struct NBlock)];
622 struct GNUNET_CRYPTO_AesSessionKey skey;
623 struct GNUNET_CRYPTO_AesInitializationVector iv;
625 struct GNUNET_CONTAINER_MetaData *meta;
626 struct GNUNET_FS_Uri *uri;
629 GNUNET_CRYPTO_hash (&nb->keyspace,
630 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
633 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
635 &sc->requests[i].query,
636 sizeof (GNUNET_HashCode)))
638 if (i == sc->uri->data.ksk.keywordCount)
640 /* oops, does not match any of our keywords!? */
645 GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
646 GNUNET_CRYPTO_aes_decrypt (&nb[1],
647 size - sizeof (struct NBlock),
652 eos = memchr (pt, 0, sizeof (pt));
659 if (sizeof (pt) == j)
660 meta = GNUNET_CONTAINER_meta_data_create ();
662 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
666 GNUNET_break_op (0); /* nblock malformed */
670 uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
672 uri->data.sks.identifier = GNUNET_strdup (pt);
673 GNUNET_CRYPTO_hash (&nb->subspace,
674 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
675 &uri->data.sks.namespace);
676 uris = GNUNET_FS_uri_to_string (uri);
677 GNUNET_CONTAINER_meta_data_insert (meta,
679 EXTRACTOR_METATYPE_URI,
680 EXTRACTOR_METAFORMAT_UTF8,
685 GNUNET_PSEUDONYM_add (sc->h->cfg,
686 &uri->data.sks.namespace,
689 process_ksk_result (sc, &sc->requests[i], uri, meta);
692 GNUNET_CONTAINER_meta_data_destroy (meta);
693 GNUNET_FS_uri_destroy (uri);
698 * Process a namespace-search result.
700 * @param sc our search context
701 * @param sb the sblock
702 * @param size size of sb
705 process_sblock (struct GNUNET_FS_SearchContext *sc,
706 const struct SBlock *sb,
709 size_t len = size - sizeof (struct SBlock);
711 struct GNUNET_CRYPTO_AesSessionKey skey;
712 struct GNUNET_CRYPTO_AesInitializationVector iv;
713 struct GNUNET_FS_Uri *uri;
714 struct GNUNET_CONTAINER_MetaData *meta;
723 identifier = sc->uri->data.sks.identifier;
724 GNUNET_CRYPTO_hash (identifier,
727 GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
728 GNUNET_CRYPTO_aes_decrypt (&sb[1],
734 off = GNUNET_STRINGS_buffer_tokenize (pt,
741 GNUNET_break_op (0); /* sblock malformed */
744 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off],
748 GNUNET_break_op (0); /* sblock malformed */
751 uri = GNUNET_FS_uri_parse (uris, &emsg);
754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755 "Failed to parse URI `%s': %s\n",
757 GNUNET_break_op (0); /* sblock malformed */
758 GNUNET_free_non_null (emsg);
759 GNUNET_CONTAINER_meta_data_destroy (meta);
763 process_sks_result (sc, id, uri, meta);
765 GNUNET_FS_uri_destroy (uri);
766 GNUNET_CONTAINER_meta_data_destroy (meta);
771 * Process a search result.
773 * @param sc our search context
774 * @param type type of the result
775 * @param expiration when it will expire
776 * @param data the (encrypted) response
777 * @param size size of data
780 process_result (struct GNUNET_FS_SearchContext *sc,
781 enum GNUNET_BLOCK_Type type,
782 struct GNUNET_TIME_Absolute expiration,
786 if (GNUNET_TIME_absolute_get_duration (expiration).value > 0)
788 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
789 "Result received has already expired.\n");
790 return; /* result expired */
794 case GNUNET_BLOCK_TYPE_KBLOCK:
795 if (! GNUNET_FS_uri_test_ksk (sc->uri))
800 if (sizeof (struct KBlock) > size)
805 process_kblock (sc, data, size);
807 case GNUNET_BLOCK_TYPE_SBLOCK:
808 if (! GNUNET_FS_uri_test_sks (sc->uri))
813 if (sizeof (struct SBlock) > size)
818 process_sblock (sc, data, size);
820 case GNUNET_BLOCK_TYPE_NBLOCK:
821 if (! GNUNET_FS_uri_test_ksk (sc->uri))
826 if (sizeof (struct NBlock) > size)
831 process_nblock (sc, data, size);
833 case GNUNET_BLOCK_TYPE_ANY:
834 case GNUNET_BLOCK_TYPE_DBLOCK:
835 case GNUNET_BLOCK_TYPE_ONDEMAND:
836 case GNUNET_BLOCK_TYPE_IBLOCK:
840 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
841 _("Got result with unknown block type `%d', ignoring"),
849 * Shutdown any existing connection to the FS
850 * service and try to establish a fresh one
851 * (and then re-transmit our search request).
853 * @param sc the search to reconnec
856 try_reconnect (struct GNUNET_FS_SearchContext *sc);
860 * Type of a function to call when we receive a message
864 * @param msg message received, NULL on timeout or fatal error
867 receive_results (void *cls,
868 const struct GNUNET_MessageHeader * msg)
870 struct GNUNET_FS_SearchContext *sc = cls;
871 const struct PutMessage *cm;
874 if ( (NULL == msg) ||
875 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
876 (ntohs (msg->size) <= sizeof (struct PutMessage)) )
881 msize = ntohs (msg->size);
882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
883 "Receiving %u bytes of result from fs service\n",
885 cm = (const struct PutMessage*) msg;
888 GNUNET_TIME_absolute_ntoh (cm->expiration),
890 msize - sizeof (struct PutMessage));
891 /* continue receiving */
892 GNUNET_CLIENT_receive (sc->client,
895 GNUNET_TIME_UNIT_FOREVER_REL);
900 * We're ready to transmit the search request to the
901 * file-sharing service. Do it.
904 * @param size number of bytes available in buf
905 * @param buf where the callee should write the message
906 * @return number of bytes written to buf
909 transmit_search_request (void *cls,
913 struct GNUNET_FS_SearchContext *sc = cls;
915 struct SearchMessage *sm;
917 const char *identifier;
926 if (GNUNET_FS_uri_test_ksk (sc->uri))
928 msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
929 GNUNET_assert (size >= msize);
931 memset (sm, 0, msize);
932 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
934 sm[i].header.size = htons (sizeof (struct SearchMessage));
935 sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
936 if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
937 sm[i].options = htonl (1);
939 sm[i].options = htonl (0);
940 sm[i].type = htonl (GNUNET_BLOCK_TYPE_ANY);
941 sm[i].anonymity_level = htonl (sc->anonymity);
942 sm[i].query = sc->requests[i].query;
947 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
948 msize = sizeof (struct SearchMessage);
949 GNUNET_assert (size >= msize);
951 memset (sm, 0, msize);
952 sm->header.size = htons (sizeof (struct SearchMessage));
953 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
954 if (0 != (sc->options & GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY))
955 sm->options = htonl (1);
957 sm->options = htonl (0);
958 sm->type = htonl (GNUNET_BLOCK_TYPE_SBLOCK);
959 sm->anonymity_level = htonl (sc->anonymity);
960 sm->target = sc->uri->data.sks.namespace;
961 identifier = sc->uri->data.sks.identifier;
962 GNUNET_CRYPTO_hash (identifier,
965 GNUNET_CRYPTO_hash (&key,
966 sizeof (GNUNET_HashCode),
968 GNUNET_CRYPTO_hash_xor (&idh,
972 GNUNET_CLIENT_receive (sc->client,
975 GNUNET_TIME_UNIT_FOREVER_REL);
981 * Reconnect to the FS service and transmit
984 * @param cls our search context
988 do_reconnect (void *cls,
989 const struct GNUNET_SCHEDULER_TaskContext *tc)
991 struct GNUNET_FS_SearchContext *sc = cls;
992 struct GNUNET_CLIENT_Connection *client;
995 sc->task = GNUNET_SCHEDULER_NO_TASK;
996 client = GNUNET_CLIENT_connect (sc->h->sched,
1004 sc->client = client;
1005 if (GNUNET_FS_uri_test_ksk (sc->uri))
1006 size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1008 size = sizeof (struct SearchMessage);
1009 GNUNET_CLIENT_notify_transmit_ready (client,
1011 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1013 &transmit_search_request,
1019 * Shutdown any existing connection to the FS
1020 * service and try to establish a fresh one
1021 * (and then re-transmit our search request).
1023 * @param sc the search to reconnec
1026 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1028 if (NULL != sc->client)
1030 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1034 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
1035 GNUNET_TIME_UNIT_SECONDS,
1042 * Start search for content, internal API.
1044 * @param h handle to the file sharing subsystem
1045 * @param uri specifies the search parameters; can be
1046 * a KSK URI or an SKS URI.
1047 * @param anonymity desired level of anonymity
1048 * @param options options for the search
1049 * @param cctx initial value for the client context
1050 * @param parent parent search (for namespace update searches)
1051 * @return context that can be used to control the search
1053 static struct GNUNET_FS_SearchContext *
1054 search_start (struct GNUNET_FS_Handle *h,
1055 const struct GNUNET_FS_Uri *uri,
1057 enum GNUNET_FS_SearchOptions options,
1059 struct GNUNET_FS_SearchContext *parent)
1061 struct GNUNET_FS_SearchContext *sc;
1062 struct GNUNET_FS_ProgressInfo pi;
1064 sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
1066 sc->options = options;
1067 sc->uri = GNUNET_FS_uri_dup (uri);
1068 sc->anonymity = anonymity;
1069 sc->start_time = GNUNET_TIME_absolute_get ();
1070 sc->parent = parent;
1071 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1072 sc->client_info = cctx;
1074 GNUNET_CONTAINER_DLL_insert (parent->child_head,
1078 GNUNET_FS_search_start_searching_ (sc))
1080 GNUNET_FS_uri_destroy (sc->uri);
1081 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1085 pi.status = GNUNET_FS_STATUS_SEARCH_START;
1086 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1092 * Build the request and actually initiate the search using the
1093 * GNUnet FS service.
1095 * @param sc search context
1096 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1099 GNUNET_FS_search_start_searching_ (struct GNUNET_FS_SearchContext *sc)
1102 const char *keyword;
1104 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
1105 struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1108 GNUNET_assert (NULL == sc->client);
1109 if (GNUNET_FS_uri_test_ksk (sc->uri))
1111 size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1115 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
1116 size = sizeof (struct SearchMessage);
1118 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1121 _("Too many keywords specified for a single search."));
1122 return GNUNET_SYSERR;
1124 if (GNUNET_FS_uri_test_ksk (sc->uri))
1126 GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1127 sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1128 sc->uri->data.ksk.keywordCount);
1129 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1131 keyword = &sc->uri->data.ksk.keywords[i][1];
1132 GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
1133 pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1134 GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1135 GNUNET_CRYPTO_rsa_key_free (pk);
1136 GNUNET_CRYPTO_hash (&pub,
1137 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1138 &sc->requests[i].query);
1139 sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1140 if (sc->requests[i].mandatory)
1141 sc->mandatory_count++;
1142 sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1143 GNUNET_CRYPTO_hash (keyword,
1145 &sc->requests[i].key);
1148 sc->client = GNUNET_CLIENT_connect (sc->h->sched,
1151 if (NULL == sc->client)
1152 return GNUNET_SYSERR;
1153 GNUNET_CLIENT_notify_transmit_ready (sc->client,
1155 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1157 &transmit_search_request,
1166 * Start search for content.
1168 * @param h handle to the file sharing subsystem
1169 * @param uri specifies the search parameters; can be
1170 * a KSK URI or an SKS URI.
1171 * @param anonymity desired level of anonymity
1172 * @param options options for the search
1173 * @param cctx initial value for the client context
1174 * @return context that can be used to control the search
1176 struct GNUNET_FS_SearchContext *
1177 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1178 const struct GNUNET_FS_Uri *uri,
1180 enum GNUNET_FS_SearchOptions options,
1183 return search_start (h, uri, anonymity, options, cctx, NULL);
1190 * @param sc context for the search that should be paused
1193 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1195 struct GNUNET_FS_ProgressInfo pi;
1197 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1198 GNUNET_SCHEDULER_cancel (sc->h->sched,
1200 sc->task = GNUNET_SCHEDULER_NO_TASK;
1201 if (NULL != sc->client)
1202 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1204 // FIXME: make persistent!
1205 // FIXME: should this freeze all active probes?
1206 pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1207 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1212 * Continue paused search.
1214 * @param sc context for the search that should be resumed
1217 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1219 struct GNUNET_FS_ProgressInfo pi;
1221 GNUNET_assert (sc->client == NULL);
1222 GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
1223 do_reconnect (sc, NULL);
1224 // FIXME: make persistent!
1225 pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1226 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1231 * Free the given search result.
1233 * @param cls the global FS handle
1234 * @param key the key for the search result (unused)
1235 * @param value the search result to free
1239 search_result_free (void *cls,
1240 const GNUNET_HashCode * key,
1243 struct GNUNET_FS_SearchContext *sc = cls;
1244 struct GNUNET_FS_Handle *h = sc->h;
1246 struct SearchResult *sr = value;
1247 struct GNUNET_FS_ProgressInfo pi;
1249 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1250 pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1251 pi.value.search.specifics.result_stopped.meta = sr->meta;
1252 pi.value.search.specifics.result_stopped.uri = sr->uri;
1253 sr->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1254 GNUNET_break (NULL == sr->client_info);
1255 if (sr->serialization != NULL)
1257 GNUNET_snprintf (pbuf,
1263 GNUNET_FS_remove_sync_file_ (sc->h,
1266 GNUNET_free (sr->serialization);
1268 GNUNET_FS_uri_destroy (sr->uri);
1269 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1270 if (sr->probe_ctx != NULL)
1271 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1272 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1273 GNUNET_SCHEDULER_cancel (h->sched,
1274 sr->probe_cancel_task);
1281 * Stop search for content.
1283 * @param sc context for the search that should be stopped
1286 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1288 struct GNUNET_FS_ProgressInfo pi;
1290 struct GNUNET_FS_SearchContext *parent;
1292 // FIXME: make un-persistent!
1293 if (NULL != (parent = sc->parent))
1295 GNUNET_CONTAINER_DLL_remove (parent->child_head,
1300 while (NULL != sc->child_head)
1301 GNUNET_FS_search_stop (sc->child_head);
1302 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1303 &search_result_free,
1305 pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1306 sc->client_info = GNUNET_FS_search_make_status_ (&pi, sc);
1307 GNUNET_break (NULL == sc->client_info);
1308 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1309 GNUNET_SCHEDULER_cancel (sc->h->sched,
1311 if (NULL != sc->client)
1312 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1313 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1314 if (sc->requests != NULL)
1316 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1317 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1318 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1320 GNUNET_free_non_null (sc->requests);
1321 GNUNET_free_non_null (sc->emsg);
1322 GNUNET_FS_uri_destroy (sc->uri);
1326 /* end of fs_search.c */