+ {
+ GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
+ dc->th = NULL;
+ }
+ if (NULL != dc->client)
+ {
+ GNUNET_CLIENT_disconnect (dc->client);
+ dc->in_receive = GNUNET_NO;
+ dc->client = NULL;
+ }
+ dc->pending_head = NULL;
+ dc->pending_tail = NULL;
+ pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
+ GNUNET_FS_download_make_status_ (&pi, dc);
+}
+
+
+/**
+ * (recursively) Create a download request structure.
+ *
+ * @param parent parent of the current entry
+ * @param chk_idx index of the chk for this block in the parent block
+ * @param depth depth of the current entry, 0 are the DBLOCKs,
+ * top level block is 'dc->treedepth - 1'
+ * @param dr_offset offset in the original file this block maps to
+ * (as in, offset of the first byte of the first DBLOCK
+ * in the subtree rooted in the returned download request tree)
+ * @param file_start_offset desired starting offset for the download
+ * in the original file; requesting tree should not contain
+ * DBLOCKs prior to the file_start_offset
+ * @param desired_length desired number of bytes the user wanted to access
+ * (from file_start_offset). Resulting tree should not contain
+ * DBLOCKs after file_start_offset + file_length.
+ * @return download request tree for the given range of DBLOCKs at
+ * the specified depth
+ */
+static struct DownloadRequest *
+create_download_request (struct DownloadRequest *parent,
+ unsigned int chk_idx,
+ unsigned int depth,
+ uint64_t dr_offset, uint64_t file_start_offset,
+ uint64_t desired_length)
+{
+ struct DownloadRequest *dr;
+ unsigned int i;
+ unsigned int head_skip;
+ uint64_t child_block_size;
+
+ dr = GNUNET_new (struct DownloadRequest);
+ dr->parent = parent;
+ dr->depth = depth;
+ dr->offset = dr_offset;
+ dr->chk_idx = chk_idx;
+ if (0 == depth)
+ return dr;
+ child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
+
+ /* calculate how many blocks at this level are not interesting
+ * from the start (rounded down), either because of the requested
+ * file offset or because this IBlock is further along */
+ if (dr_offset < file_start_offset)
+ {
+ head_skip = (file_start_offset - dr_offset) / child_block_size;
+ }
+ else
+ {
+ head_skip = 0;
+ }
+
+ /* calculate index of last block at this level that is interesting (rounded up) */
+ dr->num_children = (file_start_offset + desired_length - dr_offset) / child_block_size;
+ if (dr->num_children * child_block_size <
+ file_start_offset + desired_length - dr_offset)
+ dr->num_children++; /* round up */
+ GNUNET_assert (dr->num_children > head_skip);
+ dr->num_children -= head_skip;
+ if (dr->num_children > CHK_PER_INODE)
+ dr->num_children = CHK_PER_INODE; /* cap at max */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Block at offset %llu and depth %u has %u children\n",
+ (unsigned long long) dr_offset,
+ depth,
+ dr->num_children);
+
+ /* now we can get the total number of *interesting* children for this block */
+
+ /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
+ GNUNET_assert (dr->num_children > 0);
+
+ dr->children =
+ GNUNET_malloc (dr->num_children * sizeof (struct DownloadRequest *));
+ for (i = 0; i < dr->num_children; i++)
+ {
+ dr->children[i] =
+ create_download_request (dr, i + head_skip, depth - 1,
+ dr_offset + (i + head_skip) * child_block_size,
+ file_start_offset, desired_length);
+ }
+ return dr;
+}
+
+
+/**
+ * Continuation after a possible attempt to reconstruct
+ * the current IBlock from the existing file.
+ *
+ * @param cls the 'struct ReconstructContext'
+ * @param tc scheduler context
+ */
+static void
+reconstruct_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+
+ /* clean up state from tree encoder */
+ if (dc->task != NULL)
+ {
+ GNUNET_SCHEDULER_cancel (dc->task);
+ dc->task = NULL;
+ }
+ if (NULL != dc->rfh)
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
+ dc->rfh = NULL;
+ }
+ /* start "normal" download */
+ dc->issue_requests = GNUNET_YES;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting normal download\n");
+ schedule_block_download (dc, dc->top_request);
+}
+
+
+/**
+ * Task requesting the next block from the tree encoder.
+ *
+ * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
+ * @param tc task context
+ */
+static void
+get_next_block (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+
+ dc->task = NULL;
+ GNUNET_FS_tree_encoder_next (dc->te);
+}
+
+
+/**
+ * Function called asking for the current (encoded)
+ * block to be processed. After processing the
+ * client should either call "GNUNET_FS_tree_encode_next"
+ * or (on error) "GNUNET_FS_tree_encode_finish".
+ *
+ * This function checks if the content on disk matches
+ * the expected content based on the URI.
+ *
+ * @param cls closure
+ * @param chk content hash key for the block
+ * @param offset offset of the block
+ * @param depth depth of the block, 0 for DBLOCK
+ * @param type type of the block (IBLOCK or DBLOCK)
+ * @param block the (encrypted) block
+ * @param block_size size of block (in bytes)
+ */
+static void
+reconstruct_cb (void *cls, const struct ContentHashKey *chk, uint64_t offset,
+ unsigned int depth, enum GNUNET_BLOCK_Type type,
+ const void *block, uint16_t block_size)
+{
+ struct GNUNET_FS_DownloadContext *dc = cls;
+ struct GNUNET_FS_ProgressInfo pi;
+ struct DownloadRequest *dr;
+ uint64_t blen;
+ unsigned int chld;
+
+ /* find corresponding request entry */
+ dr = dc->top_request;
+ while (dr->depth > depth)
+ {
+ GNUNET_assert (dr->num_children > 0);
+ blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1);
+ chld = (offset - dr->offset) / blen;
+ if (chld < dr->children[0]->chk_idx)