towards having download
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file fs/fs_download.c
22  * @brief download methods
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - process replies
27  * - callback signaling
28  * - location URI suppport (can wait)
29  * - persistence (can wait)
30  */
31 #include "platform.h"
32 #include "gnunet_constants.h"
33 #include "gnunet_fs_service.h"
34 #include "fs.h"
35
36 #define DEBUG_DOWNLOAD GNUNET_YES
37
38
39 /**
40  * Schedule the download of the specified
41  * block in the tree.
42  *
43  * @param dc overall download this block belongs to
44  * @param chk content-hash-key of the block
45  * @param offset offset of the block in the file
46  *         (for IBlocks, the offset is the lowest
47  *          offset of any DBlock in the subtree under
48  *          the IBlock)
49  * @param depth depth of the block, 0 is the root of the tree
50  */
51 static void
52 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
53                          const struct ContentHashKey *chk,
54                          uint64_t offset,
55                          unsigned int depth)
56 {
57   struct DownloadRequest *sm;
58
59   sm = GNUNET_malloc (sizeof (struct DownloadRequest));
60   sm->chk = *chk;
61   sm->offset = offset;
62   sm->depth = depth;
63   sm->is_pending = GNUNET_YES;
64   sm->next = dc->pending;
65   dc->pending = sm;
66   GNUNET_CONTAINER_multihashmap_put (dc->active,
67                                      &chk->query,
68                                      sm,
69                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
70 }
71
72
73 /**
74  * We've lost our connection with the FS service.
75  * Re-establish it and re-transmit all of our
76  * pending requests.
77  *
78  * @param dc download context that is having trouble
79  */
80 static void
81 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
82
83
84 /**
85  * Process a search result.
86  *
87  * @param sc our search context
88  * @param type type of the result
89  * @param data the (encrypted) response
90  * @param size size of data
91  */
92 static void
93 process_result (struct GNUNET_FS_DownloadContext *dc,
94                 uint32_t type,
95                 const void *data,
96                 size_t size)
97 {
98   GNUNET_HashCode query;
99   struct DownloadRequest *sm;
100   struct GNUNET_CRYPTO_AesSessionKey skey;
101   struct GNUNET_CRYPTO_AesInitializationVector iv;
102   char pt[size];
103
104   GNUNET_CRYPTO_hash (data, size, &query);
105   sm = GNUNET_CONTAINER_multihashmap_get (dc->active,
106                                           &query);
107   if (NULL == sm)
108     {
109       GNUNET_break (0);
110       return;
111     }
112   GNUNET_assert (GNUNET_YES ==
113                  GNUNET_CONTAINER_multihashmap_remove (dc->active,
114                                                        &query,
115                                                        sm));
116   GNUNET_CRYPTO_hash_to_aes_key (&sm->chk.key, &skey, &iv);
117   GNUNET_CRYPTO_aes_decrypt (data,
118                              size,
119                              &skey,
120                              &iv,
121                              pt);
122   // FIXME: save to disk
123   // FIXME: make persistent
124   // FIXME: call progress callback
125   // FIXME: trigger next block (if applicable)  
126 }
127
128
129 /**
130  * Type of a function to call when we receive a message
131  * from the service.
132  *
133  * @param cls closure
134  * @param msg message received, NULL on timeout or fatal error
135  */
136 static void 
137 receive_results (void *cls,
138                  const struct GNUNET_MessageHeader * msg)
139 {
140   struct GNUNET_FS_DownloadContext *dc = cls;
141   const struct ContentMessage *cm;
142   uint16_t msize;
143
144   if ( (NULL == msg) ||
145        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_CONTENT) ||
146        (ntohs (msg->size) <= sizeof (struct ContentMessage)) )
147     {
148       try_reconnect (dc);
149       return;
150     }
151   msize = ntohs (msg->size);
152   cm = (const struct ContentMessage*) msg;
153   process_result (dc, 
154                   ntohl (cm->type),
155                   &cm[1],
156                   msize - sizeof (struct ContentMessage));
157   /* continue receiving */
158   GNUNET_CLIENT_receive (dc->client,
159                          &receive_results,
160                          dc,
161                          GNUNET_TIME_UNIT_FOREVER_REL);
162 }
163
164
165
166 /**
167  * We're ready to transmit a search request to the
168  * file-sharing service.  Do it.  If there is 
169  * more than one request pending, try to send 
170  * multiple or request another transmission.
171  *
172  * @param cls closure
173  * @param size number of bytes available in buf
174  * @param buf where the callee should write the message
175  * @return number of bytes written to buf
176  */
177 static size_t
178 transmit_download_request (void *cls,
179                            size_t size, 
180                            void *buf)
181 {
182   struct GNUNET_FS_DownloadContext *dc = cls;
183   size_t msize;
184   struct SearchMessage *sm;
185
186   if (NULL == buf)
187     {
188       try_reconnect (dc);
189       return 0;
190     }
191   GNUNET_assert (size >= sizeof (struct SearchMessage));
192   msize = 0;
193   sm = buf;
194   while ( (dc->pending == NULL) &&
195           (size > msize + sizeof (struct SearchMessage)) )
196     {
197       memset (sm, 0, sizeof (struct SearchMessage));
198       sm->header.size = htons (sizeof (struct SearchMessage));
199       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
200       sm->anonymity_level = htonl (dc->anonymity);
201       // FIXME: support 'loc' URIs (set sm->target) 
202       sm->query = dc->pending->chk.query;
203       dc->pending->is_pending = GNUNET_NO;
204       dc->pending = dc->pending->next;
205       msize += sizeof (struct SearchMessage);
206       sm++;
207     }
208   return msize;
209 }
210
211
212 /**
213  * Reconnect to the FS service and transmit
214  * our queries NOW.
215  *
216  * @param cls our download context
217  * @param tc unused
218  */
219 static void
220 do_reconnect (void *cls,
221               const struct GNUNET_SCHEDULER_TaskContext *tc)
222 {
223   struct GNUNET_FS_DownloadContext *dc = cls;
224   struct GNUNET_CLIENT_Connection *client;
225   
226   dc->task = GNUNET_SCHEDULER_NO_TASK;
227   client = GNUNET_CLIENT_connect (dc->h->sched,
228                                   "fs",
229                                   dc->h->cfg);
230   if (NULL == client)
231     {
232       try_reconnect (dc);
233       return;
234     }
235   dc->client = client;
236   GNUNET_CLIENT_notify_transmit_ready (client,
237                                        sizeof (struct SearchMessage),
238                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
239                                        &transmit_download_request,
240                                        dc);  
241   GNUNET_CLIENT_receive (client,
242                          &receive_results,
243                          dc,
244                          GNUNET_TIME_UNIT_FOREVER_REL);
245 }
246
247
248 /**
249  * Add entries that are not yet pending back to
250  * the pending list.
251  *
252  * @param cls our download context
253  * @param key unused
254  * @param entry entry of type "struct DownloadRequest"
255  * @return GNUNET_OK
256  */
257 static int
258 retry_entry (void *cls,
259              const GNUNET_HashCode *key,
260              void *entry)
261 {
262   struct GNUNET_FS_DownloadContext *dc = cls;
263   struct DownloadRequest *dr = entry;
264
265   if (! dr->is_pending)
266     {
267       dr->next = dc->pending;
268       dr->is_pending = GNUNET_YES;
269       dc->pending = entry;
270     }
271   return GNUNET_OK;
272 }
273
274
275 /**
276  * We've lost our connection with the FS service.
277  * Re-establish it and re-transmit all of our
278  * pending requests.
279  *
280  * @param dc download context that is having trouble
281  */
282 static void
283 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
284 {
285   
286   if (NULL != dc->client)
287     {
288       GNUNET_CONTAINER_multihashmap_iterate (dc->active,
289                                              &retry_entry,
290                                              dc);
291       GNUNET_CLIENT_disconnect (dc->client);
292       dc->client = NULL;
293     }
294   dc->task
295     = GNUNET_SCHEDULER_add_delayed (dc->h->sched,
296                                     GNUNET_NO,
297                                     GNUNET_SCHEDULER_PRIORITY_IDLE,
298                                     GNUNET_SCHEDULER_NO_TASK,
299                                     GNUNET_TIME_UNIT_SECONDS,
300                                     &do_reconnect,
301                                     dc);
302 }
303
304
305 /**
306  * Download parts of a file.  Note that this will store
307  * the blocks at the respective offset in the given file.  Also, the
308  * download is still using the blocking of the underlying FS
309  * encoding.  As a result, the download may *write* outside of the
310  * given boundaries (if offset and length do not match the 32k FS
311  * block boundaries). <p>
312  *
313  * This function should be used to focus a download towards a
314  * particular portion of the file (optimization), not to strictly
315  * limit the download to exactly those bytes.
316  *
317  * @param h handle to the file sharing subsystem
318  * @param uri the URI of the file (determines what to download); CHK or LOC URI
319  * @param filename where to store the file, maybe NULL (then no file is
320  *        created on disk and data must be grabbed from the callbacks)
321  * @param offset at what offset should we start the download (typically 0)
322  * @param length how many bytes should be downloaded starting at offset
323  * @param anonymity anonymity level to use for the download
324  * @param options various options
325  * @param parent parent download to associate this download with (use NULL
326  *        for top-level downloads; useful for manually-triggered recursive downloads)
327  * @return context that can be used to control this download
328  */
329 struct GNUNET_FS_DownloadContext *
330 GNUNET_FS_file_download_start (struct GNUNET_FS_Handle *h,
331                                const struct GNUNET_FS_Uri *uri,
332                                const char *filename,
333                                uint64_t offset,
334                                uint64_t length,
335                                uint32_t anonymity,
336                                enum GNUNET_FS_DownloadOptions options,
337                                struct GNUNET_FS_DownloadContext *parent)
338 {
339   struct GNUNET_FS_DownloadContext *dc;
340   struct GNUNET_CLIENT_Connection *client;
341
342   client = GNUNET_CLIENT_connect (h->sched,
343                                   "fs",
344                                   h->cfg);
345   if (NULL == client)
346     return NULL;
347   // FIXME: add support for "loc" URIs!
348   GNUNET_assert (GNUNET_FS_uri_test_chk (uri));
349   if ( (dc->offset + dc->length < dc->offset) ||
350        (dc->offset + dc->length > uri->data.chk.file_length) )
351     {
352       GNUNET_break (0);
353       return NULL;
354     }
355   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
356   dc->h = h;
357   dc->client = client;
358   dc->parent = parent;
359   dc->uri = GNUNET_FS_uri_dup (uri);
360   dc->filename = (NULL == filename) ? NULL : GNUNET_strdup (filename);
361   dc->offset = offset;
362   dc->length = length;
363   dc->anonymity = anonymity;
364   dc->options = options;
365   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + (length / DBLOCK_SIZE));
366   // FIXME: make persistent
367   schedule_block_download (dc, 
368                            &dc->uri->data.chk.chk,
369                            0, 
370                            0);
371   GNUNET_CLIENT_notify_transmit_ready (client,
372                                        sizeof (struct SearchMessage),
373                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
374                                        &transmit_download_request,
375                                        dc);  
376   GNUNET_CLIENT_receive (client,
377                          &receive_results,
378                          dc,
379                          GNUNET_TIME_UNIT_FOREVER_REL);
380   // FIXME: signal download start
381   return dc;
382 }
383
384
385 /**
386  * Free entries in the map.
387  *
388  * @param cls unused (NULL)
389  * @param key unused
390  * @param entry entry of type "struct DownloadRequest" which is freed
391  * @return GNUNET_OK
392  */
393 static int
394 free_entry (void *cls,
395             const GNUNET_HashCode *key,
396             void *entry)
397 {
398   GNUNET_free (entry);
399   return GNUNET_OK;
400 }
401
402
403 /**
404  * Stop a download (aborts if download is incomplete).
405  *
406  * @param dc handle for the download
407  * @param do_delete delete files of incomplete downloads
408  */
409 void
410 GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *dc,
411                               int do_delete)
412 {
413   // FIXME: make unpersistent
414   // FIXME: signal download end
415   
416   if (GNUNET_SCHEDULER_NO_TASK != dc->task)
417     GNUNET_SCHEDULER_cancel (dc->h->sched,
418                              dc->task);
419   if (NULL != dc->client)
420     GNUNET_CLIENT_disconnect (dc->client);
421   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
422                                          &free_entry,
423                                          NULL);
424   GNUNET_CONTAINER_multihashmap_destroy (dc->active);
425   GNUNET_FS_uri_destroy (dc->uri);
426   GNUNET_free_non_null (dc->filename);
427   GNUNET_free (dc);
428 }
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447 #if 0
448
449 /**
450  * Node-specific data (not shared, keep small!). 152 bytes.
451  * Nodes are kept in a doubly-linked list.
452  */
453 struct Node
454 {
455   /**
456    * Pointer to shared data between all nodes (request manager,
457    * progress data, etc.).
458    */
459   struct GNUNET_ECRS_DownloadContext *ctx;
460
461   /**
462    * Previous entry in DLL.
463    */
464   struct Node *prev;
465
466   /**
467    * Next entry in DLL.
468    */
469   struct Node *next;
470
471   /**
472    * What is the GNUNET_EC_ContentHashKey for this block?
473    */
474   GNUNET_EC_ContentHashKey chk;
475
476   /**
477    * At what offset (on the respective level!) is this
478    * block?
479    */
480   unsigned long long offset;
481
482   /**
483    * 0 for dblocks, >0 for iblocks.
484    */
485   unsigned int level;
486
487 };
488
489 /**
490  * @brief structure that keeps track of currently pending requests for
491  *        a download
492  *
493  * Handle to the state of a request manager.  Here we keep track of
494  * which queries went out with which priorities and which nodes in
495  * the merkle-tree are waiting for the replies.
496  */
497 struct GNUNET_ECRS_DownloadContext
498 {
499
500   /**
501    * Total number of bytes in the file.
502    */
503   unsigned long long total;
504
505   /**
506    * Number of bytes already obtained
507    */
508   unsigned long long completed;
509
510   /**
511    * Starting-offset in file (for partial download)
512    */
513   unsigned long long offset;
514
515   /**
516    * Length of the download (starting at offset).
517    */
518   unsigned long long length;
519
520   /**
521    * Time download was started.
522    */
523   GNUNET_CronTime startTime;
524
525   /**
526    * Doubly linked list of all pending requests (head)
527    */
528   struct Node *head;
529
530   /**
531    * Doubly linked list of all pending requests (tail)
532    */
533   struct Node *tail;
534
535   /**
536    * FSLIB context for issuing requests.
537    */
538   struct GNUNET_FS_SearchContext *sctx;
539
540   /**
541    * Context for error reporting.
542    */
543   struct GNUNET_GE_Context *ectx;
544
545   /**
546    * Configuration information.
547    */
548   struct GNUNET_GC_Configuration *cfg;
549
550   /**
551    * The file handle.
552    */
553   int handle;
554
555   /**
556    * Do we exclusively own this sctx?
557    */
558   int my_sctx;
559
560   /**
561    * The base-filename
562    */
563   char *filename;
564
565   /**
566    * Main thread running the operation.
567    */
568   struct GNUNET_ThreadHandle *main;
569
570   /**
571    * Function to call when we make progress.
572    */
573   GNUNET_ECRS_DownloadProgressCallback dpcb;
574
575   /**
576    * Extra argument to dpcb.
577    */
578   void *dpcbClosure;
579
580   /**
581    * Identity of the peer having the content, or all-zeros
582    * if we don't know of such a peer.
583    */
584   GNUNET_PeerIdentity target;
585
586   /**
587    * Abort?  Flag that can be set at any time
588    * to abort the RM as soon as possible.  Set
589    * to GNUNET_YES during orderly shutdown,
590    * set to GNUNET_SYSERR on error.
591    */
592   int abortFlag;
593
594   /**
595    * Do we have a specific peer from which we download
596    * from?
597    */
598   int have_target;
599
600   /**
601    * Desired anonymity level for the download.
602    */
603   unsigned int anonymityLevel;
604
605   /**
606    * The depth of the file-tree.
607    */
608   unsigned int treedepth;
609
610 };
611
612 static int
613 content_receive_callback (const GNUNET_HashCode * query,
614                           const GNUNET_DatastoreValue * reply, void *cls,
615                           unsigned long long uid);
616
617
618 /**
619  * Close the files and free the associated resources.
620  *
621  * @param self reference to the download context
622  */
623 static void
624 free_request_manager (struct GNUNET_ECRS_DownloadContext *rm)
625 {
626   struct Node *pos;
627
628   if (rm->abortFlag == GNUNET_NO)
629     rm->abortFlag = GNUNET_YES;
630   if (rm->my_sctx == GNUNET_YES)
631     GNUNET_FS_destroy_search_context (rm->sctx);
632   else
633     GNUNET_FS_suspend_search_context (rm->sctx);
634   while (rm->head != NULL)
635     {
636       pos = rm->head;
637       GNUNET_DLL_remove (rm->head, rm->tail, pos);
638       if (rm->my_sctx != GNUNET_YES)
639         GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos);
640       GNUNET_free (pos);
641     }
642   if (rm->my_sctx != GNUNET_YES)
643     GNUNET_FS_resume_search_context (rm->sctx);
644   GNUNET_GE_ASSERT (NULL, rm->tail == NULL);
645   if (rm->handle >= 0)
646     CLOSE (rm->handle);
647   if (rm->main != NULL)
648     GNUNET_thread_release_self (rm->main);
649   GNUNET_free_non_null (rm->filename);
650   rm->sctx = NULL;
651   GNUNET_free (rm);
652 }
653
654 /**
655  * Read method.
656  *
657  * @param self reference to the download context
658  * @param level level in the tree to read/write at
659  * @param pos position where to read or write
660  * @param buf where to read from or write to
661  * @param len how many bytes to read or write
662  * @return number of bytes read, GNUNET_SYSERR on error
663  */
664 static int
665 read_from_files (struct GNUNET_ECRS_DownloadContext *self,
666                  unsigned int level,
667                  unsigned long long pos, void *buf, unsigned int len)
668 {
669   if ((level > 0) || (self->handle == -1))
670     return GNUNET_SYSERR;
671   LSEEK (self->handle, pos, SEEK_SET);
672   return READ (self->handle, buf, len);
673 }
674
675 /**
676  * Write method.
677  *
678  * @param self reference to the download context
679  * @param level level in the tree to write to
680  * @param pos position where to  write
681  * @param buf where to write to
682  * @param len how many bytes to write
683  * @return number of bytes written, GNUNET_SYSERR on error
684  */
685 static int
686 write_to_files (struct GNUNET_ECRS_DownloadContext *self,
687                 unsigned int level,
688                 unsigned long long pos, void *buf, unsigned int len)
689 {
690   int ret;
691
692   if (level > 0)
693     return len;                 /* lie -- no more temps */
694   if (self->handle == -1)
695     return len;
696   LSEEK (self->handle, pos, SEEK_SET);
697   ret = WRITE (self->handle, buf, len);
698   if (ret != len)
699     GNUNET_GE_LOG_STRERROR_FILE (self->ectx,
700                                  GNUNET_GE_ERROR | GNUNET_GE_BULK |
701                                  GNUNET_GE_USER, "write", self->filename);
702   return ret;
703 }
704
705 /**
706  * Queue a request for execution.
707  *
708  * @param rm the request manager struct from createRequestManager
709  * @param node the node to call once a reply is received
710  */
711 static void
712 add_request (struct Node *node)
713 {
714   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
715
716   GNUNET_DLL_insert (rm->head, rm->tail, node);
717   GNUNET_FS_start_search (rm->sctx,
718                           rm->have_target == GNUNET_NO ? NULL : &rm->target,
719                           GNUNET_ECRS_BLOCKTYPE_DATA, 1,
720                           &node->chk.query,
721                           rm->anonymityLevel,
722                           &content_receive_callback, node);
723 }
724
725 static void
726 signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg)
727 {
728   rm->abortFlag = GNUNET_SYSERR;
729   if ((rm->head != NULL) && (rm->dpcb != NULL))
730     rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure);
731   GNUNET_thread_stop_sleep (rm->main);
732 }
733
734 /**
735  * Dequeue a request.
736  *
737  * @param self the request manager struct from createRequestManager
738  * @param node the block for which the request is canceled
739  */
740 static void
741 delete_node (struct Node *node)
742 {
743   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
744
745   GNUNET_DLL_remove (rm->head, rm->tail, node);
746   GNUNET_free (node);
747   if (rm->head == NULL)
748     GNUNET_thread_stop_sleep (rm->main);
749 }
750
751 /**
752  * Compute how many bytes of data are stored in
753  * this node.
754  */
755 static unsigned int
756 get_node_size (const struct Node *node)
757 {
758   unsigned int i;
759   unsigned int ret;
760   unsigned long long rsize;
761   unsigned long long spos;
762   unsigned long long epos;
763
764   GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total);
765   if (node->level == 0)
766     {
767       ret = GNUNET_ECRS_DBLOCK_SIZE;
768       if (node->offset + (unsigned long long) ret > node->ctx->total)
769         ret = (unsigned int) (node->ctx->total - node->offset);
770 #if DEBUG_DOWNLOAD
771       GNUNET_GE_LOG (node->ctx->rm->ectx,
772                      GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
773                      "Node at offset %llu and level %d has size %u\n",
774                      node->offset, node->level, ret);
775 #endif
776       return ret;
777     }
778   rsize = GNUNET_ECRS_DBLOCK_SIZE;
779   for (i = 0; i < node->level - 1; i++)
780     rsize *= GNUNET_ECRS_CHK_PER_INODE;
781   spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey));
782   epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE;
783   if (epos > node->ctx->total)
784     epos = node->ctx->total;
785   ret = (epos - spos) / rsize;
786   if (ret * rsize < epos - spos)
787     ret++;                      /* need to round up! */
788 #if DEBUG_DOWNLOAD
789   GNUNET_GE_LOG (node->ctx->rm->ectx,
790                  GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
791                  "Node at offset %llu and level %d has size %u\n",
792                  node->offset, node->level,
793                  ret * sizeof (GNUNET_EC_ContentHashKey));
794 #endif
795   return ret * sizeof (GNUNET_EC_ContentHashKey);
796 }
797
798 /**
799  * Notify client about progress.
800  */
801 static void
802 notify_client_about_progress (const struct Node *node,
803                               const char *data, unsigned int size)
804 {
805   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
806   GNUNET_CronTime eta;
807
808   if ((rm->abortFlag != GNUNET_NO) || (node->level != 0))
809     return;
810   rm->completed += size;
811   eta = GNUNET_get_time ();
812   if (rm->completed > 0)
813     eta = (GNUNET_CronTime) (rm->startTime +
814                              (((double) (eta - rm->startTime) /
815                                (double) rm->completed)) *
816                              (double) rm->length);
817   if (rm->dpcb != NULL)
818     rm->dpcb (rm->length,
819               rm->completed, eta, node->offset, data, size, rm->dpcbClosure);
820 }
821
822
823 /**
824  * DOWNLOAD children of this GNUNET_EC_IBlock.
825  *
826  * @param node the node for which the children should be downloaded
827  * @param data data for the node
828  * @param size size of data
829  */
830 static void iblock_download_children (const struct Node *node,
831                                       const char *data, unsigned int size);
832
833 /**
834  * Check if self block is already present on the drive.  If the block
835  * is a dblock and present, the ProgressModel is notified. If the
836  * block is present and it is an iblock, downloading the children is
837  * triggered.
838  *
839  * Also checks if the block is within the range of blocks
840  * that we are supposed to download.  If not, the method
841  * returns as if the block is present but does NOT signal
842  * progress.
843  *
844  * @param node that is checked for presence
845  * @return GNUNET_YES if present, GNUNET_NO if not.
846  */
847 static int
848 check_node_present (const struct Node *node)
849 {
850   int res;
851   int ret;
852   char *data;
853   unsigned int size;
854   GNUNET_HashCode hc;
855
856   size = get_node_size (node);
857   /* first check if node is within range.
858      For now, keeping it simple, we only do
859      this for level-0 nodes */
860   if ((node->level == 0) &&
861       ((node->offset + size < node->ctx->offset) ||
862        (node->offset >= node->ctx->offset + node->ctx->length)))
863     return GNUNET_YES;
864   data = GNUNET_malloc (size);
865   ret = GNUNET_NO;
866   res = read_from_files (node->ctx, node->level, node->offset, data, size);
867   if (res == size)
868     {
869       GNUNET_hash (data, size, &hc);
870       if (0 == memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
871         {
872           notify_client_about_progress (node, data, size);
873           if (node->level > 0)
874             iblock_download_children (node, data, size);
875           ret = GNUNET_YES;
876         }
877     }
878   GNUNET_free (data);
879   return ret;
880 }
881
882 /**
883  * DOWNLOAD children of this GNUNET_EC_IBlock.
884  *
885  * @param node the node that should be downloaded
886  */
887 static void
888 iblock_download_children (const struct Node *node,
889                           const char *data, unsigned int size)
890 {
891   struct GNUNET_GE_Context *ectx = node->ctx->ectx;
892   int i;
893   struct Node *child;
894   unsigned int childcount;
895   const GNUNET_EC_ContentHashKey *chks;
896   unsigned int levelSize;
897   unsigned long long baseOffset;
898
899   GNUNET_GE_ASSERT (ectx, node->level > 0);
900   childcount = size / sizeof (GNUNET_EC_ContentHashKey);
901   if (size != childcount * sizeof (GNUNET_EC_ContentHashKey))
902     {
903       GNUNET_GE_BREAK (ectx, 0);
904       return;
905     }
906   if (node->level == 1)
907     {
908       levelSize = GNUNET_ECRS_DBLOCK_SIZE;
909       baseOffset =
910         node->offset / sizeof (GNUNET_EC_ContentHashKey) *
911         GNUNET_ECRS_DBLOCK_SIZE;
912     }
913   else
914     {
915       levelSize =
916         sizeof (GNUNET_EC_ContentHashKey) * GNUNET_ECRS_CHK_PER_INODE;
917       baseOffset = node->offset * GNUNET_ECRS_CHK_PER_INODE;
918     }
919   chks = (const GNUNET_EC_ContentHashKey *) data;
920   for (i = 0; i < childcount; i++)
921     {
922       child = GNUNET_malloc (sizeof (struct Node));
923       child->ctx = node->ctx;
924       child->chk = chks[i];
925       child->offset = baseOffset + i * levelSize;
926       GNUNET_GE_ASSERT (ectx, child->offset < node->ctx->total);
927       child->level = node->level - 1;
928       GNUNET_GE_ASSERT (ectx, (child->level != 0) ||
929                         ((child->offset % GNUNET_ECRS_DBLOCK_SIZE) == 0));
930       if (GNUNET_NO == check_node_present (child))
931         add_request (child);
932       else
933         GNUNET_free (child);    /* done already! */
934     }
935 }
936
937
938 /**
939  * Decrypts a given data block
940  *
941  * @param data represents the data block
942  * @param hashcode represents the key concatenated with the initial
943  *        value used in the alg
944  * @param result where to store the result (encrypted block)
945  * @returns GNUNET_OK on success, GNUNET_SYSERR on error
946  */
947 static int
948 decrypt_content (const char *data,
949                  unsigned int size, const GNUNET_HashCode * hashcode,
950                  char *result)
951 {
952   GNUNET_AES_InitializationVector iv;
953   GNUNET_AES_SessionKey skey;
954
955   /* get key and init value from the GNUNET_HashCode */
956   GNUNET_hash_to_AES_key (hashcode, &skey, &iv);
957   return GNUNET_AES_decrypt (&skey, data, size, &iv, result);
958 }
959
960 /**
961  * We received a GNUNET_EC_ContentHashKey reply for a block. Decrypt.  Note
962  * that the caller (fslib) has already aquired the
963  * RM lock (we sometimes aquire it again in callees,
964  * mostly because our callees could be also be theoretically
965  * called from elsewhere).
966  *
967  * @param cls the node for which the reply is given, freed in
968  *        the function!
969  * @param query the query for which reply is the answer
970  * @param reply the reply
971  * @return GNUNET_OK if the reply was valid, GNUNET_SYSERR on error
972  */
973 static int
974 content_receive_callback (const GNUNET_HashCode * query,
975                           const GNUNET_DatastoreValue * reply, void *cls,
976                           unsigned long long uid)
977 {
978   struct Node *node = cls;
979   struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
980   struct GNUNET_GE_Context *ectx = rm->ectx;
981   GNUNET_HashCode hc;
982   unsigned int size;
983   char *data;
984
985   if (rm->abortFlag != GNUNET_NO)
986     return GNUNET_SYSERR;
987   GNUNET_GE_ASSERT (ectx,
988                     0 == memcmp (query, &node->chk.query,
989                                  sizeof (GNUNET_HashCode)));
990   size = ntohl (reply->size) - sizeof (GNUNET_DatastoreValue);
991   if ((size <= sizeof (GNUNET_EC_DBlock)) ||
992       (size - sizeof (GNUNET_EC_DBlock) != get_node_size (node)))
993     {
994       GNUNET_GE_BREAK (ectx, 0);
995       return GNUNET_SYSERR;     /* invalid size! */
996     }
997   size -= sizeof (GNUNET_EC_DBlock);
998   data = GNUNET_malloc (size);
999   if (GNUNET_SYSERR ==
1000       decrypt_content ((const char *)
1001                        &((const GNUNET_EC_DBlock *) &reply[1])[1], size,
1002                        &node->chk.key, data))
1003     GNUNET_GE_ASSERT (ectx, 0);
1004   GNUNET_hash (data, size, &hc);
1005   if (0 != memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
1006     {
1007       GNUNET_free (data);
1008       GNUNET_GE_BREAK (ectx, 0);
1009       signal_abort (rm,
1010                     _("Decrypted content does not match key. "
1011                       "This is either a bug or a maliciously inserted "
1012                       "file. Download aborted.\n"));
1013       return GNUNET_SYSERR;
1014     }
1015   if (size != write_to_files (rm, node->level, node->offset, data, size))
1016     {
1017       GNUNET_GE_LOG_STRERROR (ectx,
1018                               GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1019                               GNUNET_GE_USER | GNUNET_GE_BULK, "WRITE");
1020       signal_abort (rm, _("IO error."));
1021       return GNUNET_SYSERR;
1022     }
1023   notify_client_about_progress (node, data, size);
1024   if (node->level > 0)
1025     iblock_download_children (node, data, size);
1026   GNUNET_free (data);
1027   /* request satisfied, stop requesting! */
1028   delete_node (node);
1029   return GNUNET_OK;
1030 }
1031
1032
1033 /**
1034  * Helper function to sanitize filename
1035  * and create necessary directories.
1036  */
1037 static char *
1038 get_real_download_filename (struct GNUNET_GE_Context *ectx,
1039                             const char *filename)
1040 {
1041   struct stat buf;
1042   char *realFN;
1043   char *path;
1044   char *pos;
1045
1046   if ((filename[strlen (filename) - 1] == '/') ||
1047       (filename[strlen (filename) - 1] == '\\'))
1048     {
1049       realFN =
1050         GNUNET_malloc (strlen (filename) + strlen (GNUNET_DIRECTORY_EXT));
1051       strcpy (realFN, filename);
1052       realFN[strlen (filename) - 1] = '\0';
1053       strcat (realFN, GNUNET_DIRECTORY_EXT);
1054     }
1055   else
1056     {
1057       realFN = GNUNET_strdup (filename);
1058     }
1059   path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1);
1060   strcpy (path, realFN);
1061   pos = path;
1062   while (*pos != '\0')
1063     {
1064       if (*pos == DIR_SEPARATOR)
1065         {
1066           *pos = '\0';
1067           if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode)))
1068             {
1069               *pos = DIR_SEPARATOR;
1070               memmove (pos + strlen (GNUNET_DIRECTORY_EXT),
1071                        pos, strlen (pos));
1072               memcpy (pos,
1073                       GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT));
1074               pos += strlen (GNUNET_DIRECTORY_EXT);
1075             }
1076           else
1077             {
1078               *pos = DIR_SEPARATOR;
1079             }
1080         }
1081       pos++;
1082     }
1083   GNUNET_free (realFN);
1084   return path;
1085 }
1086
1087 /* ***************** main method **************** */
1088
1089
1090 /**
1091  * Download parts of a file.  Note that this will store
1092  * the blocks at the respective offset in the given file.
1093  * Also, the download is still using the blocking of the
1094  * underlying ECRS encoding.  As a result, the download
1095  * may *write* outside of the given boundaries (if offset
1096  * and length do not match the 32k ECRS block boundaries).
1097  * <p>
1098  *
1099  * This function should be used to focus a download towards a
1100  * particular portion of the file (optimization), not to strictly
1101  * limit the download to exactly those bytes.
1102  *
1103  * @param uri the URI of the file (determines what to download)
1104  * @param filename where to store the file
1105  * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
1106  * @param start starting offset
1107  * @param length length of the download (starting at offset)
1108  */
1109 struct GNUNET_ECRS_DownloadContext *
1110 GNUNET_ECRS_file_download_partial_start (struct GNUNET_GE_Context *ectx,
1111                                          struct GNUNET_GC_Configuration *cfg,
1112                                          struct GNUNET_FS_SearchContext *sc,
1113                                          const struct GNUNET_ECRS_URI *uri,
1114                                          const char *filename,
1115                                          unsigned long long offset,
1116                                          unsigned long long length,
1117                                          unsigned int anonymityLevel,
1118                                          int no_temporaries,
1119                                          GNUNET_ECRS_DownloadProgressCallback
1120                                          dpcb, void *dpcbClosure)
1121 {
1122   struct GNUNET_ECRS_DownloadContext *rm;
1123   struct stat buf;
1124   struct Node *top;
1125   int ret;
1126
1127   if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri)))
1128     {
1129       GNUNET_GE_BREAK (ectx, 0);
1130       return NULL;
1131     }
1132   rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext));
1133   memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext));
1134   if (sc == NULL)
1135     {
1136       rm->sctx = GNUNET_FS_create_search_context (ectx, cfg);
1137       if (rm->sctx == NULL)
1138         {
1139           GNUNET_free (rm);
1140           return NULL;
1141         }
1142       rm->my_sctx = GNUNET_YES;
1143     }
1144   else
1145     {
1146       rm->sctx = sc;
1147       rm->my_sctx = GNUNET_NO;
1148     }
1149   rm->ectx = ectx;
1150   rm->cfg = cfg;
1151   rm->startTime = GNUNET_get_time ();
1152   rm->anonymityLevel = anonymityLevel;
1153   rm->offset = offset;
1154   rm->length = length;
1155   rm->dpcb = dpcb;
1156   rm->dpcbClosure = dpcbClosure;
1157   rm->main = GNUNET_thread_get_self ();
1158   rm->total = GNUNET_ntohll (uri->data.fi.file_length);
1159   rm->filename =
1160     filename != NULL ? get_real_download_filename (ectx, filename) : NULL;
1161
1162   if ((rm->filename != NULL) &&
1163       (GNUNET_SYSERR ==
1164        GNUNET_disk_directory_create_for_file (ectx, rm->filename)))
1165     {
1166       free_request_manager (rm);
1167       return NULL;
1168     }
1169   if (0 == rm->total)
1170     {
1171       if (rm->filename != NULL)
1172         {
1173           ret = GNUNET_disk_file_open (ectx,
1174                                        rm->filename,
1175                                        O_CREAT | O_WRONLY | O_TRUNC,
1176                                        S_IRUSR | S_IWUSR);
1177           if (ret == -1)
1178             {
1179               free_request_manager (rm);
1180               return NULL;
1181             }
1182           CLOSE (ret);
1183         }
1184       dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure);
1185       free_request_manager (rm);
1186       return NULL;
1187     }
1188   rm->treedepth = GNUNET_ECRS_compute_depth (rm->total);
1189   if ((NULL != rm->filename) &&
1190       ((0 == STAT (rm->filename, &buf))
1191        && ((size_t) buf.st_size > rm->total)))
1192     {
1193       /* if exists and oversized, truncate */
1194       if (truncate (rm->filename, rm->total) != 0)
1195         {
1196           GNUNET_GE_LOG_STRERROR_FILE (ectx,
1197                                        GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1198                                        GNUNET_GE_BULK, "truncate",
1199                                        rm->filename);
1200           free_request_manager (rm);
1201           return NULL;
1202         }
1203     }
1204   if (rm->filename != NULL)
1205     {
1206       rm->handle = GNUNET_disk_file_open (ectx,
1207                                           rm->filename,
1208                                           O_CREAT | O_RDWR,
1209                                           S_IRUSR | S_IWUSR);
1210       if (rm->handle < 0)
1211         {
1212           free_request_manager (rm);
1213           return NULL;
1214         }
1215     }
1216   else
1217     rm->handle = -1;
1218   if (GNUNET_ECRS_uri_test_loc (uri))
1219     {
1220       GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey),
1221                    &rm->target.hashPubKey);
1222       rm->have_target = GNUNET_YES;
1223     }
1224   top = GNUNET_malloc (sizeof (struct Node));
1225   memset (top, 0, sizeof (struct Node));
1226   top->ctx = rm;
1227   top->chk = uri->data.fi.chk;
1228   top->offset = 0;
1229   top->level = rm->treedepth;
1230   if (GNUNET_NO == check_node_present (top))
1231     add_request (top);
1232   else
1233     GNUNET_free (top);
1234   return rm;
1235 }
1236
1237 int
1238 GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext
1239                                         *rm)
1240 {
1241   int ret;
1242
1243   ret = rm->abortFlag;
1244   free_request_manager (rm);
1245   if (ret == GNUNET_NO)
1246     ret = GNUNET_OK;            /* normal termination */
1247   return ret;
1248 }
1249
1250 /**
1251  * Download parts of a file.  Note that this will store
1252  * the blocks at the respective offset in the given file.
1253  * Also, the download is still using the blocking of the
1254  * underlying ECRS encoding.  As a result, the download
1255  * may *write* outside of the given boundaries (if offset
1256  * and length do not match the 32k ECRS block boundaries).
1257  * <p>
1258  *
1259  * This function should be used to focus a download towards a
1260  * particular portion of the file (optimization), not to strictly
1261  * limit the download to exactly those bytes.
1262  *
1263  * @param uri the URI of the file (determines what to download)
1264  * @param filename where to store the file
1265  * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
1266  * @param start starting offset
1267  * @param length length of the download (starting at offset)
1268  */
1269 int
1270 GNUNET_ECRS_file_download_partial (struct GNUNET_GE_Context *ectx,
1271                                    struct GNUNET_GC_Configuration *cfg,
1272                                    const struct GNUNET_ECRS_URI *uri,
1273                                    const char *filename,
1274                                    unsigned long long offset,
1275                                    unsigned long long length,
1276                                    unsigned int anonymityLevel,
1277                                    int no_temporaries,
1278                                    GNUNET_ECRS_DownloadProgressCallback dpcb,
1279                                    void *dpcbClosure,
1280                                    GNUNET_ECRS_TestTerminate tt,
1281                                    void *ttClosure)
1282 {
1283   struct GNUNET_ECRS_DownloadContext *rm;
1284   int ret;
1285
1286   if (length == 0)
1287     return GNUNET_OK;
1288   rm = GNUNET_ECRS_file_download_partial_start (ectx,
1289                                                 cfg,
1290                                                 NULL,
1291                                                 uri,
1292                                                 filename,
1293                                                 offset,
1294                                                 length,
1295                                                 anonymityLevel,
1296                                                 no_temporaries,
1297                                                 dpcb, dpcbClosure);
1298   if (rm == NULL)
1299     return GNUNET_SYSERR;
1300   while ((GNUNET_OK == tt (ttClosure)) &&
1301          (GNUNET_YES != GNUNET_shutdown_test ()) &&
1302          (rm->abortFlag == GNUNET_NO) && (rm->head != NULL))
1303     GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
1304   ret = GNUNET_ECRS_file_download_partial_stop (rm);
1305   return ret;
1306 }
1307
1308 /**
1309  * Download a file (simplified API).
1310  *
1311  * @param uri the URI of the file (determines what to download)
1312  * @param filename where to store the file
1313  */
1314 int
1315 GNUNET_ECRS_file_download (struct GNUNET_GE_Context *ectx,
1316                            struct GNUNET_GC_Configuration *cfg,
1317                            const struct GNUNET_ECRS_URI *uri,
1318                            const char *filename,
1319                            unsigned int anonymityLevel,
1320                            GNUNET_ECRS_DownloadProgressCallback dpcb,
1321                            void *dpcbClosure, GNUNET_ECRS_TestTerminate tt,
1322                            void *ttClosure)
1323 {
1324   return GNUNET_ECRS_file_download_partial (ectx,
1325                                             cfg,
1326                                             uri,
1327                                             filename,
1328                                             0,
1329                                             GNUNET_ECRS_uri_get_file_size
1330                                             (uri), anonymityLevel, GNUNET_NO,
1331                                             dpcb, dpcbClosure, tt, ttClosure);
1332 }
1333
1334 #endif
1335
1336 /* end of fs_download.c */