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