diff options
Diffstat (limited to 'src/fs/fs_download.c')
-rw-r--r-- | src/fs/fs_download.c | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c new file mode 100644 index 0000000000..cb3b39995f --- /dev/null +++ b/src/fs/fs_download.c @@ -0,0 +1,923 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file applications/fs/ecrs/download.c + * @brief DOWNLOAD helper methods (which do the real work). + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_ecrs_lib.h" +#include "gnunet_fs_lib.h" +#include "gnunet_identity_lib.h" +#include "ecrs_core.h" +#include "ecrs.h" +#include "fs.h" +#include "tree.h" + +#define DEBUG_DOWNLOAD GNUNET_NO + +/** + * Node-specific data (not shared, keep small!). 152 bytes. + * Nodes are kept in a doubly-linked list. + */ +struct Node +{ + /** + * Pointer to shared data between all nodes (request manager, + * progress data, etc.). + */ + struct GNUNET_ECRS_DownloadContext *ctx; + + /** + * Previous entry in DLL. + */ + struct Node *prev; + + /** + * Next entry in DLL. + */ + struct Node *next; + + /** + * What is the GNUNET_EC_ContentHashKey for this block? + */ + GNUNET_EC_ContentHashKey chk; + + /** + * At what offset (on the respective level!) is this + * block? + */ + unsigned long long offset; + + /** + * 0 for dblocks, >0 for iblocks. + */ + unsigned int level; + +}; + +/** + * @brief structure that keeps track of currently pending requests for + * a download + * + * Handle to the state of a request manager. Here we keep track of + * which queries went out with which priorities and which nodes in + * the merkle-tree are waiting for the replies. + */ +struct GNUNET_ECRS_DownloadContext +{ + + /** + * Total number of bytes in the file. + */ + unsigned long long total; + + /** + * Number of bytes already obtained + */ + unsigned long long completed; + + /** + * Starting-offset in file (for partial download) + */ + unsigned long long offset; + + /** + * Length of the download (starting at offset). + */ + unsigned long long length; + + /** + * Time download was started. + */ + GNUNET_CronTime startTime; + + /** + * Doubly linked list of all pending requests (head) + */ + struct Node *head; + + /** + * Doubly linked list of all pending requests (tail) + */ + struct Node *tail; + + /** + * FSLIB context for issuing requests. + */ + struct GNUNET_FS_SearchContext *sctx; + + /** + * Context for error reporting. + */ + struct GNUNET_GE_Context *ectx; + + /** + * Configuration information. + */ + struct GNUNET_GC_Configuration *cfg; + + /** + * The file handle. + */ + int handle; + + /** + * Do we exclusively own this sctx? + */ + int my_sctx; + + /** + * The base-filename + */ + char *filename; + + /** + * Main thread running the operation. + */ + struct GNUNET_ThreadHandle *main; + + /** + * Function to call when we make progress. + */ + GNUNET_ECRS_DownloadProgressCallback dpcb; + + /** + * Extra argument to dpcb. + */ + void *dpcbClosure; + + /** + * Identity of the peer having the content, or all-zeros + * if we don't know of such a peer. + */ + GNUNET_PeerIdentity target; + + /** + * Abort? Flag that can be set at any time + * to abort the RM as soon as possible. Set + * to GNUNET_YES during orderly shutdown, + * set to GNUNET_SYSERR on error. + */ + int abortFlag; + + /** + * Do we have a specific peer from which we download + * from? + */ + int have_target; + + /** + * Desired anonymity level for the download. + */ + unsigned int anonymityLevel; + + /** + * The depth of the file-tree. + */ + unsigned int treedepth; + +}; + +static int +content_receive_callback (const GNUNET_HashCode * query, + const GNUNET_DatastoreValue * reply, void *cls, + unsigned long long uid); + + +/** + * Close the files and free the associated resources. + * + * @param self reference to the download context + */ +static void +free_request_manager (struct GNUNET_ECRS_DownloadContext *rm) +{ + struct Node *pos; + + if (rm->abortFlag == GNUNET_NO) + rm->abortFlag = GNUNET_YES; + if (rm->my_sctx == GNUNET_YES) + GNUNET_FS_destroy_search_context (rm->sctx); + else + GNUNET_FS_suspend_search_context (rm->sctx); + while (rm->head != NULL) + { + pos = rm->head; + GNUNET_DLL_remove (rm->head, rm->tail, pos); + if (rm->my_sctx != GNUNET_YES) + GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos); + GNUNET_free (pos); + } + if (rm->my_sctx != GNUNET_YES) + GNUNET_FS_resume_search_context (rm->sctx); + GNUNET_GE_ASSERT (NULL, rm->tail == NULL); + if (rm->handle >= 0) + CLOSE (rm->handle); + if (rm->main != NULL) + GNUNET_thread_release_self (rm->main); + GNUNET_free_non_null (rm->filename); + rm->sctx = NULL; + GNUNET_free (rm); +} + +/** + * Read method. + * + * @param self reference to the download context + * @param level level in the tree to read/write at + * @param pos position where to read or write + * @param buf where to read from or write to + * @param len how many bytes to read or write + * @return number of bytes read, GNUNET_SYSERR on error + */ +static int +read_from_files (struct GNUNET_ECRS_DownloadContext *self, + unsigned int level, + unsigned long long pos, void *buf, unsigned int len) +{ + if ((level > 0) || (self->handle == -1)) + return GNUNET_SYSERR; + LSEEK (self->handle, pos, SEEK_SET); + return READ (self->handle, buf, len); +} + +/** + * Write method. + * + * @param self reference to the download context + * @param level level in the tree to write to + * @param pos position where to write + * @param buf where to write to + * @param len how many bytes to write + * @return number of bytes written, GNUNET_SYSERR on error + */ +static int +write_to_files (struct GNUNET_ECRS_DownloadContext *self, + unsigned int level, + unsigned long long pos, void *buf, unsigned int len) +{ + int ret; + + if (level > 0) + return len; /* lie -- no more temps */ + if (self->handle == -1) + return len; + LSEEK (self->handle, pos, SEEK_SET); + ret = WRITE (self->handle, buf, len); + if (ret != len) + GNUNET_GE_LOG_STRERROR_FILE (self->ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | + GNUNET_GE_USER, "write", self->filename); + return ret; +} + +/** + * Queue a request for execution. + * + * @param rm the request manager struct from createRequestManager + * @param node the node to call once a reply is received + */ +static void +add_request (struct Node *node) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + + GNUNET_DLL_insert (rm->head, rm->tail, node); + GNUNET_FS_start_search (rm->sctx, + rm->have_target == GNUNET_NO ? NULL : &rm->target, + GNUNET_ECRS_BLOCKTYPE_DATA, 1, + &node->chk.query, + rm->anonymityLevel, + &content_receive_callback, node); +} + +static void +signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg) +{ + rm->abortFlag = GNUNET_SYSERR; + if ((rm->head != NULL) && (rm->dpcb != NULL)) + rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure); + GNUNET_thread_stop_sleep (rm->main); +} + +/** + * Dequeue a request. + * + * @param self the request manager struct from createRequestManager + * @param node the block for which the request is canceled + */ +static void +delete_node (struct Node *node) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + + GNUNET_DLL_remove (rm->head, rm->tail, node); + GNUNET_free (node); + if (rm->head == NULL) + GNUNET_thread_stop_sleep (rm->main); +} + +/** + * Compute how many bytes of data are stored in + * this node. + */ +static unsigned int +get_node_size (const struct Node *node) +{ + unsigned int i; + unsigned int ret; + unsigned long long rsize; + unsigned long long spos; + unsigned long long epos; + + GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total); + if (node->level == 0) + { + ret = GNUNET_ECRS_DBLOCK_SIZE; + if (node->offset + (unsigned long long) ret > node->ctx->total) + ret = (unsigned int) (node->ctx->total - node->offset); +#if DEBUG_DOWNLOAD + GNUNET_GE_LOG (node->ctx->rm->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Node at offset %llu and level %d has size %u\n", + node->offset, node->level, ret); +#endif + return ret; + } + rsize = GNUNET_ECRS_DBLOCK_SIZE; + for (i = 0; i < node->level - 1; i++) + rsize *= GNUNET_ECRS_CHK_PER_INODE; + spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey)); + epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE; + if (epos > node->ctx->total) + epos = node->ctx->total; + ret = (epos - spos) / rsize; + if (ret * rsize < epos - spos) + ret++; /* need to round up! */ +#if DEBUG_DOWNLOAD + GNUNET_GE_LOG (node->ctx->rm->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "Node at offset %llu and level %d has size %u\n", + node->offset, node->level, + ret * sizeof (GNUNET_EC_ContentHashKey)); +#endif + return ret * sizeof (GNUNET_EC_ContentHashKey); +} + +/** + * Notify client about progress. + */ +static void +notify_client_about_progress (const struct Node *node, + const char *data, unsigned int size) +{ + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + GNUNET_CronTime eta; + + if ((rm->abortFlag != GNUNET_NO) || (node->level != 0)) + return; + rm->completed += size; + eta = GNUNET_get_time (); + if (rm->completed > 0) + eta = (GNUNET_CronTime) (rm->startTime + + (((double) (eta - rm->startTime) / + (double) rm->completed)) * + (double) rm->length); + if (rm->dpcb != NULL) + rm->dpcb (rm->length, + rm->completed, eta, node->offset, data, size, rm->dpcbClosure); +} + + +/** + * DOWNLOAD children of this GNUNET_EC_IBlock. + * + * @param node the node for which the children should be downloaded + * @param data data for the node + * @param size size of data + */ +static void iblock_download_children (const struct Node *node, + const char *data, unsigned int size); + +/** + * Check if self block is already present on the drive. If the block + * is a dblock and present, the ProgressModel is notified. If the + * block is present and it is an iblock, downloading the children is + * triggered. + * + * Also checks if the block is within the range of blocks + * that we are supposed to download. If not, the method + * returns as if the block is present but does NOT signal + * progress. + * + * @param node that is checked for presence + * @return GNUNET_YES if present, GNUNET_NO if not. + */ +static int +check_node_present (const struct Node *node) +{ + int res; + int ret; + char *data; + unsigned int size; + GNUNET_HashCode hc; + + size = get_node_size (node); + /* first check if node is within range. + For now, keeping it simple, we only do + this for level-0 nodes */ + if ((node->level == 0) && + ((node->offset + size < node->ctx->offset) || + (node->offset >= node->ctx->offset + node->ctx->length))) + return GNUNET_YES; + data = GNUNET_malloc (size); + ret = GNUNET_NO; + res = read_from_files (node->ctx, node->level, node->offset, data, size); + if (res == size) + { + GNUNET_hash (data, size, &hc); + if (0 == memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode))) + { + notify_client_about_progress (node, data, size); + if (node->level > 0) + iblock_download_children (node, data, size); + ret = GNUNET_YES; + } + } + GNUNET_free (data); + return ret; +} + +/** + * DOWNLOAD children of this GNUNET_EC_IBlock. + * + * @param node the node that should be downloaded + */ +static void +iblock_download_children (const struct Node *node, + const char *data, unsigned int size) +{ + struct GNUNET_GE_Context *ectx = node->ctx->ectx; + int i; + struct Node *child; + unsigned int childcount; + const GNUNET_EC_ContentHashKey *chks; + unsigned int levelSize; + unsigned long long baseOffset; + + GNUNET_GE_ASSERT (ectx, node->level > 0); + childcount = size / sizeof (GNUNET_EC_ContentHashKey); + if (size != childcount * sizeof (GNUNET_EC_ContentHashKey)) + { + GNUNET_GE_BREAK (ectx, 0); + return; + } + if (node->level == 1) + { + levelSize = GNUNET_ECRS_DBLOCK_SIZE; + baseOffset = + node->offset / sizeof (GNUNET_EC_ContentHashKey) * + GNUNET_ECRS_DBLOCK_SIZE; + } + else + { + levelSize = + sizeof (GNUNET_EC_ContentHashKey) * GNUNET_ECRS_CHK_PER_INODE; + baseOffset = node->offset * GNUNET_ECRS_CHK_PER_INODE; + } + chks = (const GNUNET_EC_ContentHashKey *) data; + for (i = 0; i < childcount; i++) + { + child = GNUNET_malloc (sizeof (struct Node)); + child->ctx = node->ctx; + child->chk = chks[i]; + child->offset = baseOffset + i * levelSize; + GNUNET_GE_ASSERT (ectx, child->offset < node->ctx->total); + child->level = node->level - 1; + GNUNET_GE_ASSERT (ectx, (child->level != 0) || + ((child->offset % GNUNET_ECRS_DBLOCK_SIZE) == 0)); + if (GNUNET_NO == check_node_present (child)) + add_request (child); + else + GNUNET_free (child); /* done already! */ + } +} + + +/** + * Decrypts a given data block + * + * @param data represents the data block + * @param hashcode represents the key concatenated with the initial + * value used in the alg + * @param result where to store the result (encrypted block) + * @returns GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +decrypt_content (const char *data, + unsigned int size, const GNUNET_HashCode * hashcode, + char *result) +{ + GNUNET_AES_InitializationVector iv; + GNUNET_AES_SessionKey skey; + + /* get key and init value from the GNUNET_HashCode */ + GNUNET_hash_to_AES_key (hashcode, &skey, &iv); + return GNUNET_AES_decrypt (&skey, data, size, &iv, result); +} + +/** + * We received a GNUNET_EC_ContentHashKey reply for a block. Decrypt. Note + * that the caller (fslib) has already aquired the + * RM lock (we sometimes aquire it again in callees, + * mostly because our callees could be also be theoretically + * called from elsewhere). + * + * @param cls the node for which the reply is given, freed in + * the function! + * @param query the query for which reply is the answer + * @param reply the reply + * @return GNUNET_OK if the reply was valid, GNUNET_SYSERR on error + */ +static int +content_receive_callback (const GNUNET_HashCode * query, + const GNUNET_DatastoreValue * reply, void *cls, + unsigned long long uid) +{ + struct Node *node = cls; + struct GNUNET_ECRS_DownloadContext *rm = node->ctx; + struct GNUNET_GE_Context *ectx = rm->ectx; + GNUNET_HashCode hc; + unsigned int size; + char *data; + + if (rm->abortFlag != GNUNET_NO) + return GNUNET_SYSERR; + GNUNET_GE_ASSERT (ectx, + 0 == memcmp (query, &node->chk.query, + sizeof (GNUNET_HashCode))); + size = ntohl (reply->size) - sizeof (GNUNET_DatastoreValue); + if ((size <= sizeof (GNUNET_EC_DBlock)) || + (size - sizeof (GNUNET_EC_DBlock) != get_node_size (node))) + { + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; /* invalid size! */ + } + size -= sizeof (GNUNET_EC_DBlock); + data = GNUNET_malloc (size); + if (GNUNET_SYSERR == + decrypt_content ((const char *) + &((const GNUNET_EC_DBlock *) &reply[1])[1], size, + &node->chk.key, data)) + GNUNET_GE_ASSERT (ectx, 0); + GNUNET_hash (data, size, &hc); + if (0 != memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode))) + { + GNUNET_free (data); + GNUNET_GE_BREAK (ectx, 0); + signal_abort (rm, + _("Decrypted content does not match key. " + "This is either a bug or a maliciously inserted " + "file. Download aborted.\n")); + return GNUNET_SYSERR; + } + if (size != write_to_files (rm, node->level, node->offset, data, size)) + { + GNUNET_GE_LOG_STRERROR (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_USER | GNUNET_GE_BULK, "WRITE"); + signal_abort (rm, _("IO error.")); + return GNUNET_SYSERR; + } + notify_client_about_progress (node, data, size); + if (node->level > 0) + iblock_download_children (node, data, size); + GNUNET_free (data); + /* request satisfied, stop requesting! */ + delete_node (node); + return GNUNET_OK; +} + + +/** + * Helper function to sanitize filename + * and create necessary directories. + */ +static char * +get_real_download_filename (struct GNUNET_GE_Context *ectx, + const char *filename) +{ + struct stat buf; + char *realFN; + char *path; + char *pos; + + if ((filename[strlen (filename) - 1] == '/') || + (filename[strlen (filename) - 1] == '\\')) + { + realFN = + GNUNET_malloc (strlen (filename) + strlen (GNUNET_DIRECTORY_EXT)); + strcpy (realFN, filename); + realFN[strlen (filename) - 1] = '\0'; + strcat (realFN, GNUNET_DIRECTORY_EXT); + } + else + { + realFN = GNUNET_strdup (filename); + } + path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1); + strcpy (path, realFN); + pos = path; + while (*pos != '\0') + { + if (*pos == DIR_SEPARATOR) + { + *pos = '\0'; + if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode))) + { + *pos = DIR_SEPARATOR; + memmove (pos + strlen (GNUNET_DIRECTORY_EXT), + pos, strlen (pos)); + memcpy (pos, + GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT)); + pos += strlen (GNUNET_DIRECTORY_EXT); + } + else + { + *pos = DIR_SEPARATOR; + } + } + pos++; + } + GNUNET_free (realFN); + return path; +} + +/* ***************** main method **************** */ + + +/** + * Download parts of a file. Note that this will store + * the blocks at the respective offset in the given file. + * Also, the download is still using the blocking of the + * underlying ECRS encoding. As a result, the download + * may *write* outside of the given boundaries (if offset + * and length do not match the 32k ECRS block boundaries). + * <p> + * + * This function should be used to focus a download towards a + * particular portion of the file (optimization), not to strictly + * limit the download to exactly those bytes. + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files + * @param start starting offset + * @param length length of the download (starting at offset) + */ +struct GNUNET_ECRS_DownloadContext * +GNUNET_ECRS_file_download_partial_start (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + struct GNUNET_FS_SearchContext *sc, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned long long offset, + unsigned long long length, + unsigned int anonymityLevel, + int no_temporaries, + GNUNET_ECRS_DownloadProgressCallback + dpcb, void *dpcbClosure) +{ + struct GNUNET_ECRS_DownloadContext *rm; + struct stat buf; + struct Node *top; + int ret; + + if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri))) + { + GNUNET_GE_BREAK (ectx, 0); + return NULL; + } + rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext)); + memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext)); + if (sc == NULL) + { + rm->sctx = GNUNET_FS_create_search_context (ectx, cfg); + if (rm->sctx == NULL) + { + GNUNET_free (rm); + return NULL; + } + rm->my_sctx = GNUNET_YES; + } + else + { + rm->sctx = sc; + rm->my_sctx = GNUNET_NO; + } + rm->ectx = ectx; + rm->cfg = cfg; + rm->startTime = GNUNET_get_time (); + rm->anonymityLevel = anonymityLevel; + rm->offset = offset; + rm->length = length; + rm->dpcb = dpcb; + rm->dpcbClosure = dpcbClosure; + rm->main = GNUNET_thread_get_self (); + rm->total = GNUNET_ntohll (uri->data.fi.file_length); + rm->filename = + filename != NULL ? get_real_download_filename (ectx, filename) : NULL; + + if ((rm->filename != NULL) && + (GNUNET_SYSERR == + GNUNET_disk_directory_create_for_file (ectx, rm->filename))) + { + free_request_manager (rm); + return NULL; + } + if (0 == rm->total) + { + if (rm->filename != NULL) + { + ret = GNUNET_disk_file_open (ectx, + rm->filename, + O_CREAT | O_WRONLY | O_TRUNC, + S_IRUSR | S_IWUSR); + if (ret == -1) + { + free_request_manager (rm); + return NULL; + } + CLOSE (ret); + } + dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure); + free_request_manager (rm); + return NULL; + } + rm->treedepth = GNUNET_ECRS_compute_depth (rm->total); + if ((NULL != rm->filename) && + ((0 == STAT (rm->filename, &buf)) + && ((size_t) buf.st_size > rm->total))) + { + /* if exists and oversized, truncate */ + if (truncate (rm->filename, rm->total) != 0) + { + GNUNET_GE_LOG_STRERROR_FILE (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_BULK, "truncate", + rm->filename); + free_request_manager (rm); + return NULL; + } + } + if (rm->filename != NULL) + { + rm->handle = GNUNET_disk_file_open (ectx, + rm->filename, + O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR); + if (rm->handle < 0) + { + free_request_manager (rm); + return NULL; + } + } + else + rm->handle = -1; + if (GNUNET_ECRS_uri_test_loc (uri)) + { + GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey), + &rm->target.hashPubKey); + rm->have_target = GNUNET_YES; + } + top = GNUNET_malloc (sizeof (struct Node)); + memset (top, 0, sizeof (struct Node)); + top->ctx = rm; + top->chk = uri->data.fi.chk; + top->offset = 0; + top->level = rm->treedepth; + if (GNUNET_NO == check_node_present (top)) + add_request (top); + else + GNUNET_free (top); + return rm; +} + +int +GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext + *rm) +{ + int ret; + + ret = rm->abortFlag; + free_request_manager (rm); + if (ret == GNUNET_NO) + ret = GNUNET_OK; /* normal termination */ + return ret; +} + +/** + * Download parts of a file. Note that this will store + * the blocks at the respective offset in the given file. + * Also, the download is still using the blocking of the + * underlying ECRS encoding. As a result, the download + * may *write* outside of the given boundaries (if offset + * and length do not match the 32k ECRS block boundaries). + * <p> + * + * This function should be used to focus a download towards a + * particular portion of the file (optimization), not to strictly + * limit the download to exactly those bytes. + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files + * @param start starting offset + * @param length length of the download (starting at offset) + */ +int +GNUNET_ECRS_file_download_partial (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned long long offset, + unsigned long long length, + unsigned int anonymityLevel, + int no_temporaries, + GNUNET_ECRS_DownloadProgressCallback dpcb, + void *dpcbClosure, + GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + struct GNUNET_ECRS_DownloadContext *rm; + int ret; + + if (length == 0) + return GNUNET_OK; + rm = GNUNET_ECRS_file_download_partial_start (ectx, + cfg, + NULL, + uri, + filename, + offset, + length, + anonymityLevel, + no_temporaries, + dpcb, dpcbClosure); + if (rm == NULL) + return GNUNET_SYSERR; + while ((GNUNET_OK == tt (ttClosure)) && + (GNUNET_YES != GNUNET_shutdown_test ()) && + (rm->abortFlag == GNUNET_NO) && (rm->head != NULL)) + GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); + ret = GNUNET_ECRS_file_download_partial_stop (rm); + return ret; +} + +/** + * Download a file (simplified API). + * + * @param uri the URI of the file (determines what to download) + * @param filename where to store the file + */ +int +GNUNET_ECRS_file_download (struct GNUNET_GE_Context *ectx, + struct GNUNET_GC_Configuration *cfg, + const struct GNUNET_ECRS_URI *uri, + const char *filename, + unsigned int anonymityLevel, + GNUNET_ECRS_DownloadProgressCallback dpcb, + void *dpcbClosure, GNUNET_ECRS_TestTerminate tt, + void *ttClosure) +{ + return GNUNET_ECRS_file_download_partial (ectx, + cfg, + uri, + filename, + 0, + GNUNET_ECRS_uri_get_file_size + (uri), anonymityLevel, GNUNET_NO, + dpcb, dpcbClosure, tt, ttClosure); +} + +/* end of download.c */ |