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 * - add support for pushing "already seen" information
28 * to FS service for bloomfilter (can wait)
29 * - handle availability probes (can wait)
30 * - make operations persistent (can wait)
34 #include "gnunet_constants.h"
35 #include "gnunet_fs_service.h"
36 #include "gnunet_protocols.h"
39 #define DEBUG_SEARCH GNUNET_NO
44 * Fill in all of the generic fields for
47 * @param pi structure to fill in
48 * @param sc overall search context
49 * @return value returned by the callback
52 make_search_status (struct GNUNET_FS_ProgressInfo *pi,
53 struct GNUNET_FS_SearchContext *sc)
55 pi->value.search.sc = sc;
59 = (sc->parent == NULL) ? NULL : sc->parent->client_info;
60 pi->value.search.query
62 pi->value.search.duration = GNUNET_TIME_absolute_get_duration (sc->start_time);
63 pi->value.search.anonymity = sc->anonymity;
64 return sc->h->upcb (sc->h->upcb_cls,
70 * Check if the given result is identical
73 * @param cls points to the URI we check against
75 * @param value a "struct SearchResult" who's URI we
77 * @return GNUNET_SYSERR if the result is present,
81 test_result_present (void *cls,
82 const GNUNET_HashCode * key,
85 const struct GNUNET_FS_Uri *uri = cls;
86 struct SearchResult *sr = value;
88 if (GNUNET_FS_uri_test_equal (uri,
96 * We've found a new CHK result. Let the client
99 * @param sc the search context
100 * @param sr the specific result
103 notify_client_chk_result (struct GNUNET_FS_SearchContext *sc,
104 struct SearchResult *sr)
106 struct GNUNET_FS_ProgressInfo pi;
108 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT;
109 pi.value.search.specifics.result.meta = sr->meta;
110 pi.value.search.specifics.result.uri = sr->uri;
111 sr->client_info = make_search_status (&pi, sc);
116 * We've found new information about an existing CHK result. Let the
117 * client know about it.
119 * @param sc the search context
120 * @param sr the specific result
123 notify_client_chk_update (struct GNUNET_FS_SearchContext *sc,
124 struct SearchResult *sr)
126 struct GNUNET_FS_ProgressInfo pi;
128 pi.status = GNUNET_FS_STATUS_SEARCH_UPDATE;
129 pi.value.search.specifics.update.cctx = sr->client_info;
130 pi.value.search.specifics.update.meta = sr->meta;
131 pi.value.search.specifics.update.uri = sr->uri;
132 pi.value.search.specifics.update.availability_rank
133 = 2*sr->availability_success - sr->availability_trials;
134 pi.value.search.specifics.update.availability_certainty
135 = sr->availability_trials;
136 pi.value.search.specifics.update.applicability_rank
137 = sr->optional_support;
138 sr->client_info = make_search_status (&pi, sc);
143 * Context for "get_result_present".
145 struct GetResultContext
148 * The URI we're looking for.
150 const struct GNUNET_FS_Uri *uri;
153 * Where to store a pointer to the search
154 * result struct if we found a match.
156 struct SearchResult *sr;
161 * Check if the given result is identical to the given URI and if so
164 * @param cls a "struct GetResultContext"
165 * @param key not used
166 * @param value a "struct SearchResult" who's URI we
167 * should compare with
171 get_result_present (void *cls,
172 const GNUNET_HashCode * key,
175 struct GetResultContext *grc = cls;
176 struct SearchResult *sr = value;
178 if (GNUNET_FS_uri_test_equal (grc->uri,
186 * Start download probes for the given search result.
188 * @param sr the search result
191 start_probe (struct SearchResult *sr);
195 * Signal result of last probe to client and then schedule next
199 signal_probe_result (struct SearchResult *sr)
201 struct GNUNET_FS_ProgressInfo pi;
203 pi.status = GNUNET_FS_STATUS_SEARCH_START;
204 pi.value.search.specifics.update.cctx = sr->client_info;
205 pi.value.search.specifics.update.meta = sr->meta;
206 pi.value.search.specifics.update.uri = sr->uri;
207 pi.value.search.specifics.update.availability_rank = sr->availability_success;
208 pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
209 pi.value.search.specifics.update.applicability_rank = sr->optional_support;
210 sr->sc->client_info = make_search_status (&pi, sr->sc);
216 * Handle the case where we have failed to receive a response for our probe.
218 * @param cls our 'struct SearchResult*'
219 * @param tc scheduler context
222 probe_failure_handler (void *cls,
223 const struct GNUNET_SCHEDULER_TaskContext *tc)
225 struct SearchResult *sr = cls;
226 sr->availability_trials++;
227 signal_probe_result (sr);
232 * Handle the case where we have gotten a response for our probe.
234 * @param cls our 'struct SearchResult*'
235 * @param tc scheduler context
238 probe_success_handler (void *cls,
239 const struct GNUNET_SCHEDULER_TaskContext *tc)
241 struct SearchResult *sr = cls;
242 sr->availability_trials++;
243 sr->availability_success++;
244 signal_probe_result (sr);
249 * Notification of FS that a search probe has made progress.
250 * This function is used INSTEAD of the client's event handler
251 * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
253 * @param cls closure, always NULL (!), actual closure
254 * is in the client-context of the info struct
255 * @param info details about the event, specifying the event type
256 * and various bits about the event
257 * @return client-context (for the next progress call
258 * for this operation; should be set to NULL for
259 * SUSPEND and STOPPED events). The value returned
260 * will be passed to future callbacks in the respective
261 * field in the GNUNET_FS_ProgressInfo struct.
264 GNUNET_FS_search_probe_progress_ (void *cls,
265 const struct GNUNET_FS_ProgressInfo *info)
267 struct SearchResult *sr = info->value.download.cctx;
268 struct GNUNET_TIME_Relative dur;
270 switch (info->status)
272 case GNUNET_FS_STATUS_DOWNLOAD_START:
275 case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
276 /* probes should never be resumed */
279 case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
280 /* probes should never be suspended */
283 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
286 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
287 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
289 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
290 sr->probe_cancel_task);
291 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
293 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
294 sr->remaining_probe_time,
295 &probe_failure_handler,
298 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
299 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
301 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
302 sr->probe_cancel_task);
303 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
305 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
306 sr->remaining_probe_time,
307 &probe_success_handler,
310 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
311 /* FIXME: clean up? schedule next probe? or already done? */
314 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
315 GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
316 sr->probe_active_time = GNUNET_TIME_absolute_get ();
317 sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
318 sr->remaining_probe_time,
319 &probe_failure_handler,
322 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
323 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
325 GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
326 sr->probe_cancel_task);
327 sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
329 dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
330 sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
342 * Start download probes for the given search result.
344 * @param sr the search result
347 start_probe (struct SearchResult *sr)
352 if (sr->probe_ctx != NULL)
354 if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
356 if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
358 len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
361 if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
363 off = len / DBLOCK_SIZE;
365 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
367 if (len - off < DBLOCK_SIZE)
371 sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
372 2 * (1 + sr->availability_trials));
373 sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
379 GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
380 GNUNET_FS_DOWNLOAD_IS_PROBE,
386 * We have received a KSK result. Check how it fits in with the
387 * overall query and notify the client accordingly.
389 * @param sc context for the overall query
390 * @param ent entry for the specific keyword
391 * @param uri the URI that was found
392 * @param meta metadata associated with the URI
393 * under the "ent" keyword
396 process_ksk_result (struct GNUNET_FS_SearchContext *sc,
397 struct SearchRequestEntry *ent,
398 const struct GNUNET_FS_Uri *uri,
399 const struct GNUNET_CONTAINER_MetaData *meta)
402 struct SearchResult *sr;
403 struct GetResultContext grc;
407 GNUNET_FS_uri_to_key (uri, &key);
409 GNUNET_CONTAINER_multihashmap_get_multiple (ent->results,
411 &test_result_present,
413 return; /* duplicate result */
414 /* try to find search result in master map */
417 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
422 is_new = (NULL == sr) || (sr->mandatory_missing > 0);
425 sr = GNUNET_malloc (sizeof (struct SearchResult));
427 sr->uri = GNUNET_FS_uri_dup (uri);
428 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
429 sr->mandatory_missing = sc->mandatory_count;
430 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
433 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
437 GNUNET_CONTAINER_meta_data_merge (sr->meta, meta);
439 /* check if mandatory satisfied */
441 sr->mandatory_missing--;
443 sr->optional_support++;
444 if (0 != sr->mandatory_missing)
447 notify_client_chk_result (sc, sr);
449 notify_client_chk_update (sc, sr);
455 * Start search for content, internal API.
457 * @param h handle to the file sharing subsystem
458 * @param uri specifies the search parameters; can be
459 * a KSK URI or an SKS URI.
460 * @param anonymity desired level of anonymity
461 * @param cctx client context
462 * @param parent parent search (for namespace update searches)
463 * @return context that can be used to control the search
465 static struct GNUNET_FS_SearchContext *
466 search_start (struct GNUNET_FS_Handle *h,
467 const struct GNUNET_FS_Uri *uri,
470 struct GNUNET_FS_SearchContext *parent);
474 * We have received an SKS result. Start searching for updates and
475 * notify the client if it is a new result.
477 * @param sc context for the overall query
478 * @param id_update identifier for updates, NULL for none
479 * @param uri the URI that was found
480 * @param meta metadata associated with the URI
483 process_sks_result (struct GNUNET_FS_SearchContext *sc,
484 const char *id_update,
485 const struct GNUNET_FS_Uri *uri,
486 const struct GNUNET_CONTAINER_MetaData *meta)
488 struct GNUNET_FS_Uri uu;
490 struct SearchResult *sr;
493 GNUNET_FS_uri_to_key (uri, &key);
494 GNUNET_CRYPTO_hash_xor (&uri->data.chk.chk.key,
495 &uri->data.chk.chk.query,
498 GNUNET_CONTAINER_multihashmap_get_multiple (sc->master_result_map,
500 &test_result_present,
502 return; /* duplicate result */
503 sr = GNUNET_malloc (sizeof (struct SearchResult));
505 sr->uri = GNUNET_FS_uri_dup (uri);
506 sr->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
507 GNUNET_CONTAINER_multihashmap_put (sc->master_result_map,
510 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
513 notify_client_chk_result (sc, sr);
514 /* search for updates */
515 if (strlen (id_update) == 0)
516 return; /* no updates */
518 uu.data.sks.namespace = sc->uri->data.sks.namespace;
519 uu.data.sks.identifier = GNUNET_strdup (id_update);
520 /* FIXME: should attach update search
521 to the individual result, not
522 the entire SKS search! */
532 * Process a keyword-search result.
534 * @param sc our search context
535 * @param kb the kblock
536 * @param size size of kb
539 process_kblock (struct GNUNET_FS_SearchContext *sc,
540 const struct KBlock *kb,
546 char pt[size - sizeof (struct KBlock)];
547 struct GNUNET_CRYPTO_AesSessionKey skey;
548 struct GNUNET_CRYPTO_AesInitializationVector iv;
550 struct GNUNET_CONTAINER_MetaData *meta;
551 struct GNUNET_FS_Uri *uri;
554 GNUNET_CRYPTO_hash (&kb->keyspace,
555 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
558 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
560 &sc->requests[i].query,
561 sizeof (GNUNET_HashCode)))
563 if (i == sc->uri->data.ksk.keywordCount)
565 /* oops, does not match any of our keywords!? */
570 GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
571 GNUNET_CRYPTO_aes_decrypt (&kb[1],
572 size - sizeof (struct KBlock),
577 eos = memchr (pt, 0, sizeof (pt));
584 if (sizeof (pt) == j)
585 meta = GNUNET_CONTAINER_meta_data_create ();
587 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
591 GNUNET_break_op (0); /* kblock malformed */
594 uri = GNUNET_FS_uri_parse (pt, &emsg);
597 GNUNET_break_op (0); /* kblock malformed */
598 GNUNET_free_non_null (emsg);
599 GNUNET_CONTAINER_meta_data_destroy (meta);
603 process_ksk_result (sc, &sc->requests[i], uri, meta);
606 GNUNET_CONTAINER_meta_data_destroy (meta);
607 GNUNET_FS_uri_destroy (uri);
612 * Process a keyword-search result with a namespace advertisment.
614 * @param sc our search context
615 * @param nb the nblock
616 * @param size size of nb
619 process_nblock (struct GNUNET_FS_SearchContext *sc,
620 const struct NBlock *nb,
626 char pt[size - sizeof (struct NBlock)];
627 struct GNUNET_CRYPTO_AesSessionKey skey;
628 struct GNUNET_CRYPTO_AesInitializationVector iv;
630 struct GNUNET_CONTAINER_MetaData *meta;
631 struct GNUNET_FS_Uri *uri;
634 GNUNET_CRYPTO_hash (&nb->keyspace,
635 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
638 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
640 &sc->requests[i].query,
641 sizeof (GNUNET_HashCode)))
643 if (i == sc->uri->data.ksk.keywordCount)
645 /* oops, does not match any of our keywords!? */
650 GNUNET_CRYPTO_hash_to_aes_key (&sc->requests[i].key, &skey, &iv);
651 GNUNET_CRYPTO_aes_decrypt (&nb[1],
652 size - sizeof (struct NBlock),
657 eos = memchr (pt, 0, sizeof (pt));
664 if (sizeof (pt) == j)
665 meta = GNUNET_CONTAINER_meta_data_create ();
667 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[j],
671 GNUNET_break_op (0); /* nblock malformed */
675 uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
677 uri->data.sks.identifier = GNUNET_strdup (pt);
678 GNUNET_CRYPTO_hash (&nb->subspace,
679 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
680 &uri->data.sks.namespace);
681 uris = GNUNET_FS_uri_to_string (uri);
682 GNUNET_CONTAINER_meta_data_insert (meta,
684 EXTRACTOR_METATYPE_URI,
685 EXTRACTOR_METAFORMAT_UTF8,
690 GNUNET_PSEUDONYM_add (sc->h->cfg,
691 &uri->data.sks.namespace,
694 process_ksk_result (sc, &sc->requests[i], uri, meta);
697 GNUNET_CONTAINER_meta_data_destroy (meta);
698 GNUNET_FS_uri_destroy (uri);
703 * Process a namespace-search result.
705 * @param sc our search context
706 * @param sb the sblock
707 * @param size size of sb
710 process_sblock (struct GNUNET_FS_SearchContext *sc,
711 const struct SBlock *sb,
714 size_t len = size - sizeof (struct SBlock);
716 struct GNUNET_CRYPTO_AesSessionKey skey;
717 struct GNUNET_CRYPTO_AesInitializationVector iv;
718 struct GNUNET_FS_Uri *uri;
719 struct GNUNET_CONTAINER_MetaData *meta;
728 identifier = sc->uri->data.sks.identifier;
729 GNUNET_CRYPTO_hash (identifier,
732 GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
733 GNUNET_CRYPTO_aes_decrypt (&sb[1],
739 off = GNUNET_STRINGS_buffer_tokenize (pt,
746 GNUNET_break_op (0); /* sblock malformed */
749 meta = GNUNET_CONTAINER_meta_data_deserialize (&pt[off],
753 GNUNET_break_op (0); /* sblock malformed */
756 uri = GNUNET_FS_uri_parse (uris, &emsg);
759 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
760 "Failed to parse URI `%s': %s\n",
762 GNUNET_break_op (0); /* sblock malformed */
763 GNUNET_free_non_null (emsg);
764 GNUNET_CONTAINER_meta_data_destroy (meta);
768 process_sks_result (sc, id, uri, meta);
770 GNUNET_FS_uri_destroy (uri);
771 GNUNET_CONTAINER_meta_data_destroy (meta);
776 * Process a search result.
778 * @param sc our search context
779 * @param type type of the result
780 * @param expiration when it will expire
781 * @param data the (encrypted) response
782 * @param size size of data
785 process_result (struct GNUNET_FS_SearchContext *sc,
786 enum GNUNET_BLOCK_Type type,
787 struct GNUNET_TIME_Absolute expiration,
791 if (GNUNET_TIME_absolute_get_duration (expiration).value > 0)
793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
794 "Result received has already expired.\n");
795 return; /* result expired */
799 case GNUNET_BLOCK_TYPE_KBLOCK:
800 if (! GNUNET_FS_uri_test_ksk (sc->uri))
805 if (sizeof (struct KBlock) > size)
810 process_kblock (sc, data, size);
812 case GNUNET_BLOCK_TYPE_SBLOCK:
813 if (! GNUNET_FS_uri_test_sks (sc->uri))
818 if (sizeof (struct SBlock) > size)
823 process_sblock (sc, data, size);
825 case GNUNET_BLOCK_TYPE_NBLOCK:
826 if (! GNUNET_FS_uri_test_ksk (sc->uri))
831 if (sizeof (struct NBlock) > size)
836 process_nblock (sc, data, size);
838 case GNUNET_BLOCK_TYPE_ANY:
839 case GNUNET_BLOCK_TYPE_DBLOCK:
840 case GNUNET_BLOCK_TYPE_ONDEMAND:
841 case GNUNET_BLOCK_TYPE_IBLOCK:
845 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
846 _("Got result with unknown block type `%d', ignoring"),
854 * Shutdown any existing connection to the FS
855 * service and try to establish a fresh one
856 * (and then re-transmit our search request).
858 * @param sc the search to reconnec
861 try_reconnect (struct GNUNET_FS_SearchContext *sc);
865 * Type of a function to call when we receive a message
869 * @param msg message received, NULL on timeout or fatal error
872 receive_results (void *cls,
873 const struct GNUNET_MessageHeader * msg)
875 struct GNUNET_FS_SearchContext *sc = cls;
876 const struct PutMessage *cm;
879 if ( (NULL == msg) ||
880 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
881 (ntohs (msg->size) <= sizeof (struct PutMessage)) )
886 msize = ntohs (msg->size);
887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888 "Receiving %u bytes of result from fs service\n",
890 cm = (const struct PutMessage*) msg;
893 GNUNET_TIME_absolute_ntoh (cm->expiration),
895 msize - sizeof (struct PutMessage));
896 /* continue receiving */
897 GNUNET_CLIENT_receive (sc->client,
900 GNUNET_TIME_UNIT_FOREVER_REL);
905 * We're ready to transmit the search request to the
906 * file-sharing service. Do it.
909 * @param size number of bytes available in buf
910 * @param buf where the callee should write the message
911 * @return number of bytes written to buf
914 transmit_search_request (void *cls,
918 struct GNUNET_FS_SearchContext *sc = cls;
920 struct SearchMessage *sm;
922 const char *identifier;
931 if (GNUNET_FS_uri_test_ksk (sc->uri))
933 msize = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
934 GNUNET_assert (size >= msize);
936 memset (sm, 0, msize);
937 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
939 sm[i].header.size = htons (sizeof (struct SearchMessage));
940 sm[i].header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
941 sm[i].type = htonl (GNUNET_BLOCK_TYPE_ANY);
942 sm[i].anonymity_level = htonl (sc->anonymity);
943 sm[i].query = sc->requests[i].query;
948 GNUNET_assert (GNUNET_FS_uri_test_sks (sc->uri));
949 msize = sizeof (struct SearchMessage);
950 GNUNET_assert (size >= msize);
952 memset (sm, 0, msize);
953 sm->header.size = htons (sizeof (struct SearchMessage));
954 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
955 sm->type = htonl (GNUNET_BLOCK_TYPE_SBLOCK);
956 sm->anonymity_level = htonl (sc->anonymity);
957 sm->target = sc->uri->data.sks.namespace;
958 identifier = sc->uri->data.sks.identifier;
959 GNUNET_CRYPTO_hash (identifier,
962 GNUNET_CRYPTO_hash (&key,
963 sizeof (GNUNET_HashCode),
965 GNUNET_CRYPTO_hash_xor (&idh,
969 GNUNET_CLIENT_receive (sc->client,
972 GNUNET_TIME_UNIT_FOREVER_REL);
978 * Reconnect to the FS service and transmit
981 * @param cls our search context
985 do_reconnect (void *cls,
986 const struct GNUNET_SCHEDULER_TaskContext *tc)
988 struct GNUNET_FS_SearchContext *sc = cls;
989 struct GNUNET_CLIENT_Connection *client;
992 sc->task = GNUNET_SCHEDULER_NO_TASK;
993 client = GNUNET_CLIENT_connect (sc->h->sched,
1001 sc->client = client;
1002 if (GNUNET_FS_uri_test_ksk (sc->uri))
1003 size = sizeof (struct SearchMessage) * sc->uri->data.ksk.keywordCount;
1005 size = sizeof (struct SearchMessage);
1006 GNUNET_CLIENT_notify_transmit_ready (client,
1008 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1010 &transmit_search_request,
1016 * Shutdown any existing connection to the FS
1017 * service and try to establish a fresh one
1018 * (and then re-transmit our search request).
1020 * @param sc the search to reconnec
1023 try_reconnect (struct GNUNET_FS_SearchContext *sc)
1025 if (NULL != sc->client)
1027 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1031 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
1032 GNUNET_TIME_UNIT_SECONDS,
1039 * Start search for content, internal API.
1041 * @param h handle to the file sharing subsystem
1042 * @param uri specifies the search parameters; can be
1043 * a KSK URI or an SKS URI.
1044 * @param anonymity desired level of anonymity
1045 * @param cctx initial value for the client context
1046 * @param parent parent search (for namespace update searches)
1047 * @return context that can be used to control the search
1049 static struct GNUNET_FS_SearchContext *
1050 search_start (struct GNUNET_FS_Handle *h,
1051 const struct GNUNET_FS_Uri *uri,
1054 struct GNUNET_FS_SearchContext *parent)
1056 struct GNUNET_FS_SearchContext *sc;
1057 struct GNUNET_CLIENT_Connection *client;
1058 struct GNUNET_FS_ProgressInfo pi;
1061 const char *keyword;
1063 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
1064 struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1066 if (GNUNET_FS_uri_test_ksk (uri))
1068 size = sizeof (struct SearchMessage) * uri->data.ksk.keywordCount;
1072 GNUNET_assert (GNUNET_FS_uri_test_sks (uri));
1073 size = sizeof (struct SearchMessage);
1075 if (size >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
1077 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1078 _("Too many keywords specified for a single search."));
1081 client = GNUNET_CLIENT_connect (h->sched,
1086 sc = GNUNET_malloc (sizeof(struct GNUNET_FS_SearchContext));
1088 sc->uri = GNUNET_FS_uri_dup (uri);
1089 sc->anonymity = anonymity;
1090 sc->start_time = GNUNET_TIME_absolute_get ();
1091 sc->client = client;
1092 sc->parent = parent;
1093 sc->master_result_map = GNUNET_CONTAINER_multihashmap_create (16);
1094 sc->client_info = cctx;
1095 if (GNUNET_FS_uri_test_ksk (uri))
1097 GNUNET_assert (0 != sc->uri->data.ksk.keywordCount);
1098 sc->requests = GNUNET_malloc (sizeof (struct SearchRequestEntry) *
1099 sc->uri->data.ksk.keywordCount);
1100 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1102 keyword = &sc->uri->data.ksk.keywords[i][1];
1103 GNUNET_CRYPTO_hash (keyword, strlen (keyword), &hc);
1104 pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&hc);
1105 GNUNET_CRYPTO_rsa_key_get_public (pk, &pub);
1106 GNUNET_CRYPTO_rsa_key_free (pk);
1107 GNUNET_CRYPTO_hash (&pub,
1108 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1109 &sc->requests[i].query);
1110 sc->requests[i].mandatory = (sc->uri->data.ksk.keywords[i][0] == '+');
1111 if (sc->requests[i].mandatory)
1112 sc->mandatory_count++;
1113 sc->requests[i].results = GNUNET_CONTAINER_multihashmap_create (4);
1114 GNUNET_CRYPTO_hash (keyword,
1116 &sc->requests[i].key);
1120 GNUNET_CONTAINER_DLL_insert (parent->child_head,
1123 pi.status = GNUNET_FS_STATUS_SEARCH_START;
1124 sc->client_info = make_search_status (&pi, sc);
1125 GNUNET_CLIENT_notify_transmit_ready (client,
1127 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1129 &transmit_search_request,
1136 * Start search for content.
1138 * @param h handle to the file sharing subsystem
1139 * @param uri specifies the search parameters; can be
1140 * a KSK URI or an SKS URI.
1141 * @param anonymity desired level of anonymity
1142 * @param cctx initial value for the client context
1143 * @return context that can be used to control the search
1145 struct GNUNET_FS_SearchContext *
1146 GNUNET_FS_search_start (struct GNUNET_FS_Handle *h,
1147 const struct GNUNET_FS_Uri *uri,
1151 return search_start (h, uri, anonymity, cctx, NULL);
1158 * @param sc context for the search that should be paused
1161 GNUNET_FS_search_pause (struct GNUNET_FS_SearchContext *sc)
1163 struct GNUNET_FS_ProgressInfo pi;
1165 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1166 GNUNET_SCHEDULER_cancel (sc->h->sched,
1168 sc->task = GNUNET_SCHEDULER_NO_TASK;
1169 if (NULL != sc->client)
1170 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1172 // FIXME: make persistent!
1173 // FIXME: should this freeze all active probes?
1174 pi.status = GNUNET_FS_STATUS_SEARCH_PAUSED;
1175 sc->client_info = make_search_status (&pi, sc);
1180 * Continue paused search.
1182 * @param sc context for the search that should be resumed
1185 GNUNET_FS_search_continue (struct GNUNET_FS_SearchContext *sc)
1187 struct GNUNET_FS_ProgressInfo pi;
1189 GNUNET_assert (sc->client == NULL);
1190 GNUNET_assert (sc->task == GNUNET_SCHEDULER_NO_TASK);
1191 do_reconnect (sc, NULL);
1192 // FIXME: make persistent!
1193 pi.status = GNUNET_FS_STATUS_SEARCH_CONTINUED;
1194 sc->client_info = make_search_status (&pi, sc);
1199 * Free the given search result.
1201 * @param cls the global FS handle
1202 * @param key the key for the search result (unused)
1203 * @param value the search result to free
1207 search_result_free (void *cls,
1208 const GNUNET_HashCode * key,
1211 struct GNUNET_FS_SearchContext *sc = cls;
1212 struct GNUNET_FS_Handle *h = sc->h;
1213 struct SearchResult *sr = value;
1214 struct GNUNET_FS_ProgressInfo pi;
1216 pi.status = GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED;
1217 pi.value.search.specifics.result_stopped.cctx = sr->client_info;
1218 pi.value.search.specifics.result_stopped.meta = sr->meta;
1219 pi.value.search.specifics.result_stopped.uri = sr->uri;
1220 sr->client_info = make_search_status (&pi, sc);
1221 GNUNET_break (NULL == sr->client_info);
1223 GNUNET_FS_uri_destroy (sr->uri);
1224 GNUNET_CONTAINER_meta_data_destroy (sr->meta);
1225 if (sr->probe_ctx != NULL)
1226 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1227 if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
1228 GNUNET_SCHEDULER_cancel (h->sched,
1229 sr->probe_cancel_task);
1236 * Stop search for content.
1238 * @param sc context for the search that should be stopped
1241 GNUNET_FS_search_stop (struct GNUNET_FS_SearchContext *sc)
1243 struct GNUNET_FS_ProgressInfo pi;
1245 struct GNUNET_FS_SearchContext *parent;
1247 // FIXME: make un-persistent!
1248 if (NULL != (parent = sc->parent))
1250 GNUNET_CONTAINER_DLL_remove (parent->child_head,
1255 while (NULL != sc->child_head)
1256 GNUNET_FS_search_stop (sc->child_head);
1257 GNUNET_CONTAINER_multihashmap_iterate (sc->master_result_map,
1258 &search_result_free,
1260 pi.status = GNUNET_FS_STATUS_SEARCH_STOPPED;
1261 sc->client_info = make_search_status (&pi, sc);
1262 GNUNET_break (NULL == sc->client_info);
1263 if (sc->task != GNUNET_SCHEDULER_NO_TASK)
1264 GNUNET_SCHEDULER_cancel (sc->h->sched,
1266 if (NULL != sc->client)
1267 GNUNET_CLIENT_disconnect (sc->client, GNUNET_NO);
1268 GNUNET_CONTAINER_multihashmap_destroy (sc->master_result_map);
1269 if (sc->requests != NULL)
1271 GNUNET_assert (GNUNET_FS_uri_test_ksk (sc->uri));
1272 for (i=0;i<sc->uri->data.ksk.keywordCount;i++)
1273 GNUNET_CONTAINER_multihashmap_destroy (sc->requests[i].results);
1275 GNUNET_free_non_null (sc->requests);
1276 GNUNET_FS_uri_destroy (sc->uri);
1280 /* end of fs_search.c */