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