+ * Signal result of last probe to client and then schedule next
+ * probe.
+ */
+static void
+signal_probe_result (struct GNUNET_FS_SearchResult *sr)
+{
+ struct GNUNET_FS_ProgressInfo pi;
+
+ pi.status = GNUNET_FS_STATUS_SEARCH_START;
+ pi.value.search.specifics.update.cctx = sr->client_info;
+ pi.value.search.specifics.update.meta = sr->meta;
+ pi.value.search.specifics.update.uri = sr->uri;
+ pi.value.search.specifics.update.availability_rank = sr->availability_success;
+ pi.value.search.specifics.update.availability_certainty = sr->availability_trials;
+ pi.value.search.specifics.update.applicability_rank = sr->optional_support;
+ sr->sc->client_info = GNUNET_FS_search_make_status_ (&pi, sr->sc);
+ GNUNET_FS_search_start_probe_ (sr);
+}
+
+
+/**
+ * Handle the case where we have failed to receive a response for our probe.
+ *
+ * @param cls our 'struct GNUNET_FS_SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_failure_handler (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_SearchResult *sr = cls;
+ sr->availability_trials++;
+ GNUNET_FS_search_result_sync_ (sr);
+ signal_probe_result (sr);
+}
+
+
+/**
+ * Handle the case where we have gotten a response for our probe.
+ *
+ * @param cls our 'struct GNUNET_FS_SearchResult*'
+ * @param tc scheduler context
+ */
+static void
+probe_success_handler (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_SearchResult *sr = cls;
+ sr->availability_trials++;
+ sr->availability_success++;
+ GNUNET_FS_search_result_sync_ (sr);
+ signal_probe_result (sr);
+}
+
+
+/**
+ * Notification of FS that a search probe has made progress.
+ * This function is used INSTEAD of the client's event handler
+ * for downloads where the GNUNET_FS_DOWNLOAD_IS_PROBE flag is set.
+ *
+ * @param cls closure, always NULL (!), actual closure
+ * is in the client-context of the info struct
+ * @param info details about the event, specifying the event type
+ * and various bits about the event
+ * @return client-context (for the next progress call
+ * for this operation; should be set to NULL for
+ * SUSPEND and STOPPED events). The value returned
+ * will be passed to future callbacks in the respective
+ * field in the GNUNET_FS_ProgressInfo struct.
+ */
+void*
+GNUNET_FS_search_probe_progress_ (void *cls,
+ const struct GNUNET_FS_ProgressInfo *info)
+{
+ struct GNUNET_FS_SearchResult *sr = info->value.download.cctx;
+ struct GNUNET_TIME_Relative dur;
+
+ switch (info->status)
+ {
+ case GNUNET_FS_STATUS_DOWNLOAD_START:
+ /* ignore */
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_RESUME:
+ /* probes should never be resumed */
+ GNUNET_assert (0);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_SUSPEND:
+ /* probes should never be suspended */
+ GNUNET_break (0);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
+ /* ignore */
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_failure_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_success_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ sr = NULL;
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
+ GNUNET_assert (sr->probe_cancel_task == GNUNET_SCHEDULER_NO_TASK);
+ sr->probe_active_time = GNUNET_TIME_absolute_get ();
+ sr->probe_cancel_task = GNUNET_SCHEDULER_add_delayed (sr->sc->h->sched,
+ sr->remaining_probe_time,
+ &probe_failure_handler,
+ sr);
+ break;
+ case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
+ if (sr->probe_cancel_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sr->sc->h->sched,
+ sr->probe_cancel_task);
+ sr->probe_cancel_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ dur = GNUNET_TIME_absolute_get_duration (sr->probe_active_time);
+ sr->remaining_probe_time = GNUNET_TIME_relative_subtract (sr->remaining_probe_time,
+ dur);
+ GNUNET_FS_search_result_sync_ (sr);
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+ return sr;
+}
+
+
+/**
+ * Start download probes for the given search result.
+ *
+ * @param sr the search result
+ */
+void
+GNUNET_FS_search_start_probe_ (struct GNUNET_FS_SearchResult *sr)
+{
+ uint64_t off;
+ uint64_t len;
+
+ if (sr->probe_ctx != NULL)
+ return;
+ if (sr->download != NULL)
+ return;
+ if (0 == (sr->sc->h->flags & GNUNET_FS_FLAGS_DO_PROBES))
+ return;
+ if (sr->availability_trials > AVAILABILITY_TRIALS_MAX)
+ return;
+ len = GNUNET_FS_uri_chk_get_file_size (sr->uri);
+ if (len == 0)
+ return;
+ if ( (len <= DBLOCK_SIZE) && (sr->availability_success > 0))
+ return;
+ off = len / DBLOCK_SIZE;
+ if (off > 0)
+ off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, off);
+ off *= DBLOCK_SIZE;
+ if (len - off < DBLOCK_SIZE)
+ len = len - off;
+ else
+ len = DBLOCK_SIZE;
+ sr->remaining_probe_time = GNUNET_TIME_relative_multiply (sr->sc->h->avg_block_latency,
+ 2 * (1 + sr->availability_trials));
+ sr->probe_ctx = GNUNET_FS_download_start (sr->sc->h,
+ sr->uri,
+ sr->meta,
+ NULL, NULL,
+ off, len,
+ sr->sc->anonymity,
+ GNUNET_FS_DOWNLOAD_NO_TEMPORARIES |
+ GNUNET_FS_DOWNLOAD_IS_PROBE,
+ sr, NULL);
+}
+
+
+/**
+ * We have received a KSK result. Check how it fits in with the
+ * overall query and notify the client accordingly.