cf5a7b65a9b8ba37a75e678b07238666acbaf430
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2011 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 3, 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  * - different priority for scheduling probe downloads?
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_fs_service.h"
31 #include "fs.h"
32 #include "fs_tree.h"
33
34 #define DEBUG_DOWNLOAD GNUNET_NO
35
36 /**
37  * Determine if the given download (options and meta data) should cause
38  * use to try to do a recursive download.
39  */
40 static int
41 is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
42 {
43   return  (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
44     ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
45       ( (dc->meta == NULL) &&
46         ( (NULL == dc->filename) ||            
47           ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
48             (NULL !=
49              strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
50                      GNUNET_FS_DIRECTORY_EXT)) ) ) ) );              
51 }
52
53
54 /**
55  * We're storing the IBLOCKS after the DBLOCKS on disk (so that we
56  * only have to truncate the file once we're done).
57  *
58  * Given the offset of a block (with respect to the DBLOCKS) and its
59  * depth, return the offset where we would store this block in the
60  * file.
61  * 
62  * @param fsize overall file size
63  * @param off offset of the block in the file
64  * @param depth depth of the block in the tree, 0 for DBLOCK
65  * @return off for DBLOCKS (depth == treedepth),
66  *         otherwise an offset past the end
67  *         of the file that does not overlap
68  *         with the range for any other block
69  */
70 static uint64_t
71 compute_disk_offset (uint64_t fsize,
72                      uint64_t off,
73                      unsigned int depth)
74 {
75   unsigned int i;
76   uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
77   uint64_t loff; /* where do IBlocks for depth "i" start? */
78   unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
79   
80   if (depth == 0)
81     return off;
82   /* first IBlocks start at the end of file, rounded up
83      to full DBLOCK_SIZE */
84   loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
85   lsize = ( (fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof (struct ContentHashKey);
86   GNUNET_assert (0 == (off % DBLOCK_SIZE));
87   ioff = (off / DBLOCK_SIZE);
88   for (i=1;i<depth;i++)
89     {
90       loff += lsize;
91       lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE;
92       GNUNET_assert (lsize > 0);
93       GNUNET_assert (0 == (ioff % CHK_PER_INODE));
94       ioff /= CHK_PER_INODE;
95     }
96   return loff + ioff * sizeof (struct ContentHashKey);
97 }
98
99
100 /**
101  * Given a block at the given offset and depth, calculate the offset
102  * for the CHK at the given index.
103  *
104  * @param offset the offset of the first
105  *        DBLOCK in the subtree of the 
106  *        identified IBLOCK
107  * @param depth the depth of the IBLOCK in the tree, 0 for DBLOCK
108  * @param k which CHK in the IBLOCK are we 
109  *        talking about
110  * @return offset if k=0, otherwise an appropriately
111  *         larger value (i.e., if depth = 1,
112  *         the returned value should be offset+k*DBLOCK_SIZE)
113  */
114 static uint64_t
115 compute_dblock_offset (uint64_t offset,
116                        unsigned int depth,
117                        unsigned int k)
118 {
119   unsigned int i;
120   uint64_t lsize; /* what is the size of the sum of all DBlocks 
121                      that a CHK at depth i corresponds to? */
122
123   if (depth == 0)
124     return offset;
125   lsize = DBLOCK_SIZE;
126   for (i=1;i<depth;i++)
127     lsize *= CHK_PER_INODE;
128   return offset + k * lsize;
129 }
130
131
132 /**
133  * Fill in all of the generic fields for a download event and call the
134  * callback.
135  *
136  * @param pi structure to fill in
137  * @param dc overall download context
138  */
139 void
140 GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
141                                  struct GNUNET_FS_DownloadContext *dc)
142 {
143   pi->value.download.dc = dc;
144   pi->value.download.cctx
145     = dc->client_info;
146   pi->value.download.pctx
147     = (dc->parent == NULL) ? NULL : dc->parent->client_info;
148   pi->value.download.sctx
149     = (dc->search == NULL) ? NULL : dc->search->client_info;
150   pi->value.download.uri 
151     = dc->uri;
152   pi->value.download.filename
153     = dc->filename;
154   pi->value.download.size
155     = dc->length;
156   pi->value.download.duration
157     = GNUNET_TIME_absolute_get_duration (dc->start_time);
158   pi->value.download.completed
159     = dc->completed;
160   pi->value.download.anonymity
161     = dc->anonymity;
162   pi->value.download.eta
163     = GNUNET_TIME_calculate_eta (dc->start_time,
164                                  dc->completed,
165                                  dc->length);
166   pi->value.download.is_active = (dc->client == NULL) ? GNUNET_NO : GNUNET_YES;
167   if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
168     dc->client_info = dc->h->upcb (dc->h->upcb_cls,
169                                    pi);
170   else
171     dc->client_info = GNUNET_FS_search_probe_progress_ (NULL,
172                                                         pi);
173 }
174
175
176 /**
177  * We're ready to transmit a search request to the
178  * file-sharing service.  Do it.  If there is 
179  * more than one request pending, try to send 
180  * multiple or request another transmission.
181  *
182  * @param cls closure
183  * @param size number of bytes available in buf
184  * @param buf where the callee should write the message
185  * @return number of bytes written to buf
186  */
187 static size_t
188 transmit_download_request (void *cls,
189                            size_t size, 
190                            void *buf);
191
192
193 /**
194  * Closure for iterator processing results.
195  */
196 struct ProcessResultClosure
197 {
198   
199   /**
200    * Hash of data.
201    */
202   GNUNET_HashCode query;
203
204   /**
205    * Data found in P2P network.
206    */ 
207   const void *data;
208
209   /**
210    * Our download context.
211    */
212   struct GNUNET_FS_DownloadContext *dc;
213                 
214   /**
215    * Number of bytes in data.
216    */
217   size_t size;
218
219   /**
220    * Type of data.
221    */
222   enum GNUNET_BLOCK_Type type;
223
224   /**
225    * Flag to indicate if this block should be stored on disk.
226    */
227   int do_store;
228   
229 };
230
231
232 /**
233  * Iterator over entries in the pending requests in the 'active' map for the
234  * reply that we just got.
235  *
236  * @param cls closure (our 'struct ProcessResultClosure')
237  * @param key query for the given value / request
238  * @param value value in the hash map (a 'struct DownloadRequest')
239  * @return GNUNET_YES (we should continue to iterate); unless serious error
240  */
241 static int
242 process_result_with_request (void *cls,
243                              const GNUNET_HashCode * key,
244                              void *value);
245
246
247 /**
248  * We've found a matching block without downloading it.
249  * Encrypt it and pass it to our "receive" function as
250  * if we had received it from the network.
251  * 
252  * @param dc download in question
253  * @param chk request this relates to
254  * @param dr request details
255  * @param block plaintext data matching request
256  * @param len number of bytes in block
257  * @param do_store should we still store the block on disk?
258  * @return GNUNET_OK on success
259  */
260 static int
261 encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
262                         const struct ContentHashKey *chk,
263                         struct DownloadRequest *dr,
264                         const char * block,                    
265                         size_t len,
266                         int do_store)
267 {
268   struct ProcessResultClosure prc;
269   char enc[len];
270   struct GNUNET_CRYPTO_AesSessionKey sk;
271   struct GNUNET_CRYPTO_AesInitializationVector iv;
272   GNUNET_HashCode query;
273   
274   GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
275   if (-1 == GNUNET_CRYPTO_aes_encrypt (block, len,
276                                        &sk,
277                                        &iv,
278                                        enc))
279     {
280       GNUNET_break (0);
281       return GNUNET_SYSERR;
282     }
283   GNUNET_CRYPTO_hash (enc, len, &query);
284   if (0 != memcmp (&query,
285                    &chk->query,
286                    sizeof (GNUNET_HashCode)))
287     {
288       GNUNET_break_op (0);
289       return GNUNET_SYSERR;
290     }
291 #if DEBUG_DOWNLOAD
292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293               "Matching block for `%s' at offset %llu already present, no need for download!\n",
294               dc->filename,
295               (unsigned long long) dr->offset);
296 #endif
297   /* already got it! */
298   prc.dc = dc;
299   prc.data = enc;
300   prc.size = len;
301   prc.type = (0 == dr->depth) 
302     ? GNUNET_BLOCK_TYPE_FS_DBLOCK 
303     : GNUNET_BLOCK_TYPE_FS_IBLOCK;
304   prc.query = chk->query;
305   prc.do_store = do_store;
306   process_result_with_request (&prc,
307                                &chk->key,
308                                dr);
309   return GNUNET_OK;
310 }
311
312
313 /**
314  * We've lost our connection with the FS service.
315  * Re-establish it and re-transmit all of our
316  * pending requests.
317  *
318  * @param dc download context that is having trouble
319  */
320 static void
321 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
322
323
324 /**
325  * We found an entry in a directory.  Check if the respective child
326  * already exists and if not create the respective child download.
327  *
328  * @param cls the parent download
329  * @param filename name of the file in the directory
330  * @param uri URI of the file (CHK or LOC)
331  * @param meta meta data of the file
332  * @param length number of bytes in data
333  * @param data contents of the file (or NULL if they were not inlined)
334  */
335 static void 
336 trigger_recursive_download (void *cls,
337                             const char *filename,
338                             const struct GNUNET_FS_Uri *uri,
339                             const struct GNUNET_CONTAINER_MetaData *meta,
340                             size_t length,
341                             const void *data);
342
343
344 /**
345  * We're done downloading a directory.  Open the file and
346  * trigger all of the (remaining) child downloads.
347  *
348  * @param dc context of download that just completed
349  */
350 static void
351 full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
352 {
353   size_t size;
354   uint64_t size64;
355   void *data;
356   struct GNUNET_DISK_FileHandle *h;
357   struct GNUNET_DISK_MapHandle *m;
358   
359   size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
360   size = (size_t) size64;
361   if (size64 != (uint64_t) size)
362     {
363       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364                   _("Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
365       return;
366     }
367   if (dc->filename != NULL)
368     {
369       h = GNUNET_DISK_file_open (dc->filename,
370                                  GNUNET_DISK_OPEN_READ,
371                                  GNUNET_DISK_PERM_NONE);
372     }
373   else
374     {
375       GNUNET_assert (dc->temp_filename != NULL);
376       h = GNUNET_DISK_file_open (dc->temp_filename,
377                                  GNUNET_DISK_OPEN_READ,
378                                  GNUNET_DISK_PERM_NONE);
379     }
380   if (h == NULL)
381     return; /* oops */
382   data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
383   if (data == NULL)
384     {
385       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386                   _("Directory too large for system address space\n"));
387     }
388   else
389     {
390       GNUNET_FS_directory_list_contents (size,
391                                          data,
392                                          0,
393                                          &trigger_recursive_download,
394                                          dc);         
395       GNUNET_DISK_file_unmap (m);
396     }
397   GNUNET_DISK_file_close (h);
398   if (dc->filename == NULL)
399     {
400       if (0 != UNLINK (dc->temp_filename))
401         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
402                                   "unlink",
403                                   dc->temp_filename);
404       GNUNET_free (dc->temp_filename);
405       dc->temp_filename = NULL;
406     }
407 }
408
409
410 /**
411  * Check if all child-downloads have completed (or trigger them if
412  * necessary) and once we're completely done, signal completion (and
413  * possibly recurse to parent).  This function MUST be called when the
414  * download of a file itself is done or when the download of a file is
415  * done and then later a direct child download has completed (and
416  * hence this download may complete itself).
417  *
418  * @param dc download to check for completion of children
419  */
420 static void
421 check_completed (struct GNUNET_FS_DownloadContext *dc)
422 {
423   struct GNUNET_FS_ProgressInfo pi;
424   struct GNUNET_FS_DownloadContext *pos;
425
426   /* first, check if we need to download children */
427   if ( (dc->child_head == NULL) &&
428        (is_recursive_download (dc)) )
429     full_recursive_download (dc);  
430   /* then, check if children are done already */
431   pos = dc->child_head;
432   while (pos != NULL)
433     {
434       if ( (pos->emsg == NULL) &&
435            (pos->completed < pos->length) )
436         return; /* not done yet */
437       if ( (pos->child_head != NULL) &&
438            (pos->has_finished != GNUNET_YES) )
439         return; /* not transitively done yet */
440       pos = pos->next;
441     }
442   /* All of our children are done, so mark this download done */
443   dc->has_finished = GNUNET_YES;
444   if (dc->job_queue != NULL)
445     {
446       GNUNET_FS_dequeue_ (dc->job_queue);
447       dc->job_queue = NULL;
448     }
449   GNUNET_FS_download_sync_ (dc);
450
451   /* signal completion */
452   pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
453   GNUNET_FS_download_make_status_ (&pi, dc);
454
455   /* let parent know */
456   if (dc->parent != NULL)
457     check_completed (dc->parent);  
458 }
459
460
461 /**
462  * We got a block of plaintext data (from the meta data).
463  * Try it for upward reconstruction of the data.  On success,
464  * the top-level block will move to state BRS_DOWNLOAD_UP.
465  *
466  * @param dc context for the download
467  * @param dr download request to match against
468  * @param data plaintext data, starting from the beginning of the file
469  * @param data_len number of bytes in data
470  */ 
471 static void
472 try_match_block (struct GNUNET_FS_DownloadContext *dc,
473                  struct DownloadRequest *dr,
474                  const char *data,
475                  size_t data_len)
476 {
477   struct GNUNET_FS_ProgressInfo pi;
478   unsigned int i;
479   char enc[DBLOCK_SIZE];
480   struct ContentHashKey chks[CHK_PER_INODE];
481   struct ContentHashKey in_chk;
482   struct GNUNET_CRYPTO_AesSessionKey sk;
483   struct GNUNET_CRYPTO_AesInitializationVector iv;
484   size_t dlen;
485   struct DownloadRequest *drc;
486   struct GNUNET_DISK_FileHandle *fh;
487   int complete;
488   const char *fn;
489   const char *odata;
490   size_t odata_len;
491   
492   odata = data;
493   odata_len = data_len;
494   if (BRS_DOWNLOAD_UP == dr->state)
495     return;
496   if (dr->depth > 0)
497     {
498       complete = GNUNET_YES;
499       for (i=0;i<dr->num_children;i++)
500         {
501           drc = dr->children[i];
502           try_match_block (dc,
503                            drc,
504                            data, data_len);
505           if (drc->state != BRS_RECONSTRUCT_META_UP)
506             complete = GNUNET_NO;
507           else
508             chks[i] = drc->chk;
509         }
510       if (GNUNET_YES != complete)
511         return;
512       data = (const char*) chks;
513       dlen = dr->num_children * sizeof (struct ContentHashKey);
514     }
515   else
516     {
517       if (dr->offset > data_len) 
518         return; /* oops */
519       dlen = GNUNET_MIN (data_len - dr->offset,
520                          DBLOCK_SIZE);
521     }
522   GNUNET_CRYPTO_hash (&data[dr->offset],
523                       dlen,
524                       &in_chk.key);
525   GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
526   if (-1 == GNUNET_CRYPTO_aes_encrypt (&data[dr->offset], dlen,
527                                        &sk,
528                                        &iv,
529                                        enc))
530     {
531       GNUNET_break (0);
532       return;
533     }
534   GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
535   switch (dr->state)
536     {
537     case BRS_INIT:
538       dr->chk = in_chk;
539       dr->state = BRS_RECONSTRUCT_META_UP;
540       break;
541     case BRS_CHK_SET:
542       if (0 != memcmp (&in_chk,
543                        &dr->chk,
544                        sizeof (struct ContentHashKey)))
545         {
546           /* other peer provided bogus meta data */
547           GNUNET_break_op (0);
548           break;
549         }
550       /* write block to disk */
551       fn = dc->filename != NULL 
552         ? dc->filename 
553         : dc->temp_filename;
554       fh = GNUNET_DISK_file_open (fn,
555                                   GNUNET_DISK_OPEN_READWRITE | 
556                                   GNUNET_DISK_OPEN_CREATE | 
557                                   GNUNET_DISK_OPEN_TRUNCATE,
558                                   GNUNET_DISK_PERM_USER_READ |
559                                   GNUNET_DISK_PERM_USER_WRITE |
560                                   GNUNET_DISK_PERM_GROUP_READ |
561                                   GNUNET_DISK_PERM_OTHER_READ);     
562       if (fh == NULL)
563         {
564           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
565                                     "open",
566                                     fn);
567           GNUNET_asprintf (&dc->emsg,
568                            _("Failed to open file `%s' for writing"),
569                            fn);
570           GNUNET_DISK_file_close (fh);
571           dr->state = BRS_ERROR;
572           pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
573           pi.value.download.specifics.error.message = dc->emsg;
574           GNUNET_FS_download_make_status_ (&pi, dc); 
575           return;
576         }
577       if (data_len != 
578           GNUNET_DISK_file_write (fh,
579                                   odata,
580                                   odata_len))
581         {
582           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
583                                     "write",
584                                     fn);              
585           GNUNET_asprintf (&dc->emsg,
586                            _("Failed to open file `%s' for writing"),
587                            fn);
588           GNUNET_DISK_file_close (fh);
589           dr->state = BRS_ERROR;
590           pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
591           pi.value.download.specifics.error.message = dc->emsg;
592           GNUNET_FS_download_make_status_ (&pi, dc);      
593           return;
594         }
595       GNUNET_DISK_file_close (fh);
596       /* signal success */      
597       dr->state = BRS_DOWNLOAD_UP;
598       dc->completed = dc->length;
599       GNUNET_FS_download_sync_ (dc);
600       pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
601       pi.value.download.specifics.progress.data = data;
602       pi.value.download.specifics.progress.offset = 0;
603       pi.value.download.specifics.progress.data_len = dlen;
604       pi.value.download.specifics.progress.depth = 0;
605       GNUNET_FS_download_make_status_ (&pi, dc);
606       if ( (NULL != dc->filename) &&
607            (0 != truncate (dc->filename,
608                            GNUNET_ntohll (dc->uri->data.chk.file_length))) )
609         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
610                                   "truncate",
611                                   dc->filename);
612       check_completed (dc);      
613       break;
614     default:
615       /* how did we get here? */
616       GNUNET_break (0);
617       break;
618     }
619 }
620
621
622 /**
623  * Type of a function that libextractor calls for each
624  * meta data item found.  If we find full data meta data,
625  * call 'try_match_block' on it.
626  *
627  * @param cls our 'struct GNUNET_FS_DownloadContext*'
628  * @param plugin_name name of the plugin that produced this value;
629  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
630  *        used in the main libextractor library and yielding
631  *        meta data).
632  * @param type libextractor-type describing the meta data
633  * @param format basic format information about data 
634  * @param data_mime_type mime-type of data (not of the original file);
635  *        can be NULL (if mime-type is not known)
636  * @param data actual meta-data found
637  * @param data_len number of bytes in data
638  * @return 0 to continue extracting, 1 to abort
639  */ 
640 static int
641 match_full_data (void *cls,
642                  const char *plugin_name,
643                  enum EXTRACTOR_MetaType type,
644                  enum EXTRACTOR_MetaFormat format,
645                  const char *data_mime_type,
646                  const char *data,
647                  size_t data_len)
648 {
649   struct GNUNET_FS_DownloadContext *dc = cls;
650
651   if (type != EXTRACTOR_METATYPE_GNUNET_FULL_DATA) 
652     return 0;
653 #if DEBUG_DOWNLOAD
654   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
655               "Found %u bytes of FD!\n",
656               (unsigned int) data_len);
657 #endif
658   if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
659     {
660       GNUNET_break_op (0);
661       return 1; /* bogus meta data */
662     }
663   try_match_block (dc,
664                    dc->top_request,
665                    data,
666                    data_len);
667   return 1;
668 }
669
670
671 /**
672  * Set the state of the given download request to
673  * BRS_DOWNLOAD_UP and propagate it up the tree.
674  *
675  * @param dr download request that is done
676  */
677 static void
678 propagate_up (struct DownloadRequest *dr)
679 {
680   unsigned int i;
681
682   do
683     {
684       dr->state = BRS_DOWNLOAD_UP;
685       dr = dr->parent;
686       if (dr == NULL)
687         break;
688       for (i=0;i<dr->num_children;i++)
689         if (dr->children[i]->state != BRS_DOWNLOAD_UP)
690           break;
691     }
692   while (i == dr->num_children);
693 }
694
695
696 /**
697  * Try top-down reconstruction.  Before, the given request node
698  * must have the state BRS_CHK_SET.  Afterwards, more nodes may
699  * have that state or advanced to BRS_DOWNLOAD_DOWN or even
700  * BRS_DOWNLOAD_UP.  It is also possible to get BRS_ERROR on the
701  * top level.
702  *
703  * @param dc overall download this block belongs to
704  * @param dr block to reconstruct
705  */
706 static void
707 try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
708                              struct DownloadRequest *dr)
709 {
710   uint64_t off;
711   char block[DBLOCK_SIZE];
712   GNUNET_HashCode key;
713   uint64_t total;
714   size_t len;
715   unsigned int i;
716   unsigned int chk_off;
717   struct DownloadRequest *drc;
718   uint64_t child_block_size;
719   const struct ContentHashKey *chks;
720   int up_done;
721   
722   GNUNET_assert (dc->rfh != NULL);
723   GNUNET_assert (dr->state == BRS_CHK_SET);
724   total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
725   GNUNET_assert (dr->depth < dc->treedepth);
726   len = GNUNET_FS_tree_calculate_block_size (total,
727                                              dr->offset,
728                                              dr->depth);
729   GNUNET_assert (len <= DBLOCK_SIZE);
730   off = compute_disk_offset (total,
731                              dr->offset,
732                              dr->depth);
733   if (dc->old_file_size < off + len) 
734     return; /* failure */
735   if (off  != 
736       GNUNET_DISK_file_seek (dc->rfh,
737                              off,
738                              GNUNET_DISK_SEEK_SET) ) 
739     {
740       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
741                                 "seek",
742                                 dc->filename);
743       return; /* failure */
744     }
745   if (len != 
746       GNUNET_DISK_file_read (dc->rfh,
747                              block,
748                              len)) 
749     {
750       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
751                                 "read",
752                                 dc->filename);
753       return; /* failure */
754     }
755   GNUNET_CRYPTO_hash (block, len, &key);
756   if (0 != memcmp (&key,
757                    &dr->chk.key,
758                    sizeof (GNUNET_HashCode)))
759     return; /* mismatch */
760   if (GNUNET_OK !=
761       encrypt_existing_match (dc,
762                               &dr->chk,
763                               dr,
764                               block,
765                               len,
766                               GNUNET_NO))
767     {
768       /* hash matches but encrypted block does not, really bad */
769       dr->state = BRS_ERROR;
770       /* propagate up */
771       while (dr->parent != NULL)
772         {
773           dr = dr->parent;
774           dr->state = BRS_ERROR;
775         }
776       return;
777     }
778   /* block matches */
779   dr->state = BRS_DOWNLOAD_DOWN;
780
781   /* set CHKs for children */
782   up_done = GNUNET_YES;
783   chks = (const struct ContentHashKey*) block;
784   for (i=0;i<dr->num_children;i++)
785     {
786       drc = dr->children[i];
787       GNUNET_assert (drc->offset >= dr->offset);
788       child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
789       GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
790       chk_off = (drc->offset - dr->offset) / child_block_size;
791       GNUNET_assert (drc->state == BRS_INIT);
792       drc->state = BRS_CHK_SET;
793       drc->chk = chks[chk_off];
794       try_top_down_reconstruction (dc, drc);
795       if (drc->state != BRS_DOWNLOAD_UP)
796         up_done = GNUNET_NO; /* children not all done */
797     } 
798   if (up_done == GNUNET_YES)
799     propagate_up (dr); /* children all done (or no children...) */
800 }
801
802
803 /**
804  * Schedule the download of the specified block in the tree.
805  *
806  * @param dc overall download this block belongs to
807  * @param dr request to schedule
808  */
809 static void
810 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
811                          struct DownloadRequest *dr)
812 {
813   unsigned int i;
814
815   switch (dr->state)
816     {
817     case BRS_INIT:
818       GNUNET_assert (0);
819       break;
820     case BRS_RECONSTRUCT_DOWN:
821       GNUNET_assert (0);
822       break;
823     case BRS_RECONSTRUCT_META_UP:
824       GNUNET_assert (0);
825       break;
826     case BRS_RECONSTRUCT_UP:
827       GNUNET_assert (0);
828       break;
829     case BRS_CHK_SET:
830       /* normal case, start download */
831       break;
832     case BRS_DOWNLOAD_DOWN:
833       for (i=0;i<dr->num_children;i++)
834         schedule_block_download (dc, dr->children[i]);
835       return;
836     case BRS_DOWNLOAD_UP:
837       /* We're done! */
838       return;
839     case BRS_ERROR:
840       GNUNET_break (0);
841       return;
842     }
843 #if DEBUG_DOWNLOAD
844   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
845               "Scheduling download at offset %llu and depth %u for `%s'\n",
846               (unsigned long long) dr->offset,
847               dr->depth,
848               GNUNET_h2s (&dr->chk.query));
849 #endif
850   GNUNET_assert (GNUNET_NO ==
851                  GNUNET_CONTAINER_multihashmap_contains_value (dc->active,
852                                                                &dr->chk.query,
853                                                                dr));
854   GNUNET_CONTAINER_multihashmap_put (dc->active,
855                                      &dr->chk.query,
856                                      dr,
857                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
858   if (dc->client == NULL)
859     return; /* download not active */
860   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
861                                dc->pending_tail,
862                                dr);
863   dr->is_pending = GNUNET_YES;
864   if (NULL == dc->th)
865     dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
866                                                   sizeof (struct SearchMessage),
867                                                   GNUNET_CONSTANTS_SERVICE_TIMEOUT,
868                                                   GNUNET_NO,
869                                                   &transmit_download_request,
870                                                   dc);
871 }
872
873
874 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
875
876 /**
877  * We found an entry in a directory.  Check if the respective child
878  * already exists and if not create the respective child download.
879  *
880  * @param cls the parent download
881  * @param filename name of the file in the directory
882  * @param uri URI of the file (CHK or LOC)
883  * @param meta meta data of the file
884  * @param length number of bytes in data
885  * @param data contents of the file (or NULL if they were not inlined)
886  */
887 static void 
888 trigger_recursive_download (void *cls,
889                             const char *filename,
890                             const struct GNUNET_FS_Uri *uri,
891                             const struct GNUNET_CONTAINER_MetaData *meta,
892                             size_t length,
893                             const void *data)
894 {
895   struct GNUNET_FS_DownloadContext *dc = cls;  
896   struct GNUNET_FS_DownloadContext *cpos;
897   char *temp_name;
898   char *fn;
899   char *us;
900   char *ext;
901   char *dn;
902   char *pos;
903   char *full_name;
904   char *sfn;
905
906   if (NULL == uri)
907     return; /* entry for the directory itself */
908   cpos = dc->child_head;
909   while (cpos != NULL)
910     {
911       if ( (GNUNET_FS_uri_test_equal (uri,
912                                       cpos->uri)) ||
913            ( (filename != NULL) &&
914              (0 == strcmp (cpos->filename,
915                            filename)) ) )
916         break;  
917       cpos = cpos->next;
918     }
919   if (cpos != NULL)
920     return; /* already exists */
921   fn = NULL;
922   if (NULL == filename)
923     {
924       fn = GNUNET_FS_meta_data_suggest_filename (meta);
925       if (fn == NULL)
926         {
927           us = GNUNET_FS_uri_to_string (uri);
928           fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_CHK_PREFIX)]);
929           GNUNET_free (us);
930         }
931       else if (fn[0] == '.')
932         {
933           ext = fn;
934           us = GNUNET_FS_uri_to_string (uri);
935           GNUNET_asprintf (&fn,
936                            "%s%s",
937                            &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], ext);
938           GNUNET_free (ext);
939           GNUNET_free (us);
940         }
941       /* change '\' to '/' (this should have happened
942        during insertion, but malicious peers may
943        not have done this) */
944       while (NULL != (pos = strstr (fn, "\\")))
945         *pos = '/';
946       /* remove '../' everywhere (again, well-behaved
947          peers don't do this, but don't trust that
948          we did not get something nasty) */
949       while (NULL != (pos = strstr (fn, "../")))
950         {
951           pos[0] = '_';
952           pos[1] = '_';
953           pos[2] = '_';
954         }
955       filename = fn;
956     }
957   if (dc->filename == NULL)
958     {
959       full_name = NULL;
960     }
961   else
962     {
963       dn = GNUNET_strdup (dc->filename);
964       GNUNET_break ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
965                      (NULL !=
966                       strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
967                               GNUNET_FS_DIRECTORY_EXT)) );
968       sfn = GNUNET_strdup (filename);
969       while ( (strlen (sfn) > 0) &&
970               (filename[strlen(sfn)-1] == '/') )
971         sfn[strlen(sfn)-1] = '\0';
972       if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
973            (NULL !=
974             strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
975                     GNUNET_FS_DIRECTORY_EXT)) )      
976         dn[strlen(dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';      
977       if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
978            ( (strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
979              (NULL ==
980               strstr (filename + strlen(filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
981                       GNUNET_FS_DIRECTORY_EXT)) ) )
982         {
983           GNUNET_asprintf (&full_name,
984                            "%s%s%s%s",
985                            dn,
986                            DIR_SEPARATOR_STR,
987                            sfn,
988                            GNUNET_FS_DIRECTORY_EXT);
989         }
990       else
991         {
992           GNUNET_asprintf (&full_name,
993                            "%s%s%s",
994                            dn,
995                            DIR_SEPARATOR_STR,
996                            sfn);
997         }
998       GNUNET_free (dn);
999     }
1000   if ( (full_name != NULL) &&
1001        (GNUNET_OK !=
1002         GNUNET_DISK_directory_create_for_file (full_name)) )
1003     {
1004       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1005                   _("Failed to create directory for recursive download of `%s'\n"),
1006                   full_name);
1007       GNUNET_free (full_name);
1008       GNUNET_free_non_null (fn);
1009       return;
1010     }
1011
1012   temp_name = NULL;
1013 #if DEBUG_DOWNLOAD
1014   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1015               "Triggering recursive download of size %llu with %u bytes MD\n",
1016               (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
1017               (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (meta));
1018 #endif
1019   GNUNET_FS_download_start (dc->h,
1020                             uri,
1021                             meta,
1022                             full_name, temp_name,
1023                             0,
1024                             GNUNET_FS_uri_chk_get_file_size (uri),
1025                             dc->anonymity,
1026                             dc->options,
1027                             NULL,
1028                             dc);
1029   GNUNET_free_non_null (full_name);
1030   GNUNET_free_non_null (temp_name);
1031   GNUNET_free_non_null (fn);
1032 }
1033
1034
1035 /**
1036  * (recursively) free download request structure
1037  *
1038  * @param dr request to free
1039  */
1040 void
1041 GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
1042 {
1043   unsigned int i;
1044
1045   if (dr == NULL)
1046     return;
1047   for (i=0;i<dr->num_children;i++)
1048     GNUNET_FS_free_download_request_ (dr->children[i]);
1049   GNUNET_free_non_null (dr->children);
1050   GNUNET_free (dr);
1051 }
1052
1053
1054 /**
1055  * Iterator over entries in the pending requests in the 'active' map for the
1056  * reply that we just got.
1057  *
1058  * @param cls closure (our 'struct ProcessResultClosure')
1059  * @param key query for the given value / request
1060  * @param value value in the hash map (a 'struct DownloadRequest')
1061  * @return GNUNET_YES (we should continue to iterate); unless serious error
1062  */
1063 static int
1064 process_result_with_request (void *cls,
1065                              const GNUNET_HashCode *key,
1066                              void *value)
1067 {
1068   struct ProcessResultClosure *prc = cls;
1069   struct DownloadRequest *dr = value;
1070   struct GNUNET_FS_DownloadContext *dc = prc->dc;
1071   struct DownloadRequest *drc;
1072   struct GNUNET_DISK_FileHandle *fh = NULL;
1073   struct GNUNET_CRYPTO_AesSessionKey skey;
1074   struct GNUNET_CRYPTO_AesInitializationVector iv;
1075   char pt[prc->size];
1076   struct GNUNET_FS_ProgressInfo pi;
1077   uint64_t off;
1078   size_t bs;
1079   size_t app;
1080   int i;
1081   struct ContentHashKey *chk;
1082
1083 #if DEBUG_DOWNLOAD
1084   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1085               "Received block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1086               GNUNET_h2s (key),
1087               dr->depth,
1088               (unsigned long long) dr->offset,
1089               (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1090               
1091 #endif
1092   bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (dc->uri->data.chk.file_length),
1093                                             dr->offset,
1094                                             dr->depth);
1095   if (prc->size != bs)
1096     {
1097       GNUNET_asprintf (&dc->emsg,
1098                        _("Internal error or bogus download URI (expected %u bytes at depth %u and offset %llu/%llu, got %u bytes)\n"),
1099                        bs,
1100                        dr->depth,
1101                        (unsigned long long) dr->offset,
1102                        (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1103                        prc->size);
1104       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1105                   "%s", 
1106                   dc->emsg);
1107       while (dr->parent != NULL)
1108         {
1109           dr->state = BRS_ERROR;
1110           dr = dr->parent;
1111         }
1112       dr->state = BRS_ERROR;
1113       goto signal_error;
1114     }
1115
1116   (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1117                                                &prc->query,
1118                                                dr);
1119   if (GNUNET_YES == dr->is_pending)
1120     {
1121       GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1122                                    dc->pending_tail,
1123                                    dr);
1124       dr->is_pending = GNUNET_NO;
1125     }
1126
1127
1128   GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1129   if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
1130                                        prc->size,
1131                                        &skey,
1132                                        &iv,
1133                                        pt))
1134     {
1135       GNUNET_break (0);
1136       dc->emsg = GNUNET_strdup (_("internal error decrypting content"));
1137       goto signal_error;
1138     }
1139   off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1140                              dr->offset,
1141                              dr->depth);
1142   /* save to disk */
1143   if ( ( GNUNET_YES == prc->do_store) &&
1144        ( (dc->filename != NULL) ||
1145          (is_recursive_download (dc)) ) &&
1146        ( (dr->depth == dc->treedepth) ||
1147          (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1148     {
1149       fh = GNUNET_DISK_file_open (dc->filename != NULL 
1150                                   ? dc->filename 
1151                                   : dc->temp_filename, 
1152                                   GNUNET_DISK_OPEN_READWRITE | 
1153                                   GNUNET_DISK_OPEN_CREATE,
1154                                   GNUNET_DISK_PERM_USER_READ |
1155                                   GNUNET_DISK_PERM_USER_WRITE |
1156                                   GNUNET_DISK_PERM_GROUP_READ |
1157                                   GNUNET_DISK_PERM_OTHER_READ);    
1158       if (NULL == fh)
1159         {
1160           GNUNET_asprintf (&dc->emsg,
1161                            _("Download failed: could not open file `%s': %s\n"),
1162                            dc->filename,
1163                            STRERROR (errno));
1164           goto signal_error;
1165         }      
1166 #if DEBUG_DOWNLOAD
1167       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1168                   "Saving decrypted block to disk at offset %llu\n",
1169                   (unsigned long long) off);
1170 #endif
1171       if ( (off  != 
1172             GNUNET_DISK_file_seek (fh,
1173                                    off,
1174                                    GNUNET_DISK_SEEK_SET) ) )
1175         {
1176           GNUNET_asprintf (&dc->emsg,
1177                            _("Failed to seek to offset %llu in file `%s': %s\n"),
1178                            (unsigned long long) off,
1179                            dc->filename,
1180                            STRERROR (errno));
1181           goto signal_error;
1182         }
1183       if (prc->size !=
1184           GNUNET_DISK_file_write (fh,
1185                                   pt,
1186                                   prc->size))
1187         {
1188           GNUNET_asprintf (&dc->emsg,
1189                            _("Failed to write block of %u bytes at offset %llu in file `%s': %s\n"),
1190                            (unsigned int) prc->size,
1191                            (unsigned long long) off,
1192                            dc->filename,
1193                            STRERROR (errno));
1194           goto signal_error;
1195         }
1196       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1197       fh = NULL;
1198     }
1199
1200   if (dr->depth == 0) 
1201     {
1202       /* DBLOCK, update progress and try recursion if applicable */
1203       app = prc->size;
1204       if (dr->offset < dc->offset)
1205         {
1206           /* starting offset begins in the middle of pt,
1207              do not count first bytes as progress */
1208           GNUNET_assert (app > (dc->offset - dr->offset));
1209           app -= (dc->offset - dr->offset);       
1210         }
1211       if (dr->offset + prc->size > dc->offset + dc->length)
1212         {
1213           /* end of block is after relevant range,
1214              do not count last bytes as progress */
1215           GNUNET_assert (app > (dr->offset + prc->size) - (dc->offset + dc->length));
1216           app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1217         }
1218       dc->completed += app;
1219
1220       /* do recursive download if option is set and either meta data
1221          says it is a directory or if no meta data is given AND filename 
1222          ends in '.gnd' (top-level case) */
1223       if (is_recursive_download (dc))
1224         GNUNET_FS_directory_list_contents (prc->size,
1225                                            pt,
1226                                            off,
1227                                            &trigger_recursive_download,
1228                                            dc);         
1229             
1230     }
1231   dr->state = BRS_DOWNLOAD_DOWN;
1232   pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1233   pi.value.download.specifics.progress.data = pt;
1234   pi.value.download.specifics.progress.offset = dr->offset;
1235   pi.value.download.specifics.progress.data_len = prc->size;
1236   pi.value.download.specifics.progress.depth = dr->depth;
1237   GNUNET_FS_download_make_status_ (&pi, dc);
1238   GNUNET_assert (dc->completed <= dc->length);
1239   if (dr->depth == 0) 
1240     propagate_up (dr);
1241
1242   if (dc->completed == dc->length)
1243     {
1244       /* download completed, signal */
1245 #if DEBUG_DOWNLOAD
1246       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247                   "Download completed, truncating file to desired length %llu\n",
1248                   (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1249 #endif
1250       /* truncate file to size (since we store IBlocks at the end) */
1251       if (dc->filename != NULL)
1252         {
1253           if (0 != truncate (dc->filename,
1254                              GNUNET_ntohll (dc->uri->data.chk.file_length)))
1255             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1256                                       "truncate",
1257                                       dc->filename);
1258         }
1259       GNUNET_assert (dr->depth == 0);
1260       check_completed (dc);
1261     }
1262   if (dr->depth == 0) 
1263     {
1264       /* bottom of the tree, no child downloads possible, just sync */
1265       GNUNET_FS_download_sync_ (dc);
1266       return GNUNET_YES;
1267     }
1268
1269 #if DEBUG_DOWNLOAD
1270   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1271               "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1272               dr->depth,
1273               (unsigned long long) dr->offset);
1274 #endif
1275   GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1276   chk = (struct ContentHashKey*) pt;
1277   for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
1278     {
1279       off = compute_dblock_offset (dr->offset,
1280                                    dr->depth,
1281                                    i);
1282       drc = dr->children[i];
1283       switch (drc->state)
1284         {
1285         case BRS_INIT:
1286           drc->chk = chk[i];
1287           drc->state = BRS_CHK_SET;
1288           schedule_block_download (dc, drc);
1289           break;
1290         case BRS_RECONSTRUCT_DOWN:
1291           GNUNET_assert (0);
1292           break;
1293         case BRS_RECONSTRUCT_META_UP:
1294           GNUNET_assert (0);
1295           break;
1296         case BRS_RECONSTRUCT_UP:
1297           GNUNET_assert (0);
1298           break;
1299         case BRS_CHK_SET:
1300           GNUNET_assert (0);
1301           break;
1302         case BRS_DOWNLOAD_DOWN:
1303           GNUNET_assert (0);
1304           break;
1305         case BRS_DOWNLOAD_UP:
1306           GNUNET_assert (0);
1307           break;
1308         case BRS_ERROR:
1309           GNUNET_assert (0);
1310           break;
1311         default:
1312           GNUNET_assert (0);
1313           break;
1314         }
1315     }
1316   GNUNET_FS_download_sync_ (dc);
1317   return GNUNET_YES;
1318
1319  signal_error:
1320   if (fh != NULL)
1321     GNUNET_DISK_file_close (fh);
1322   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1323   pi.value.download.specifics.error.message = dc->emsg;
1324   GNUNET_FS_download_make_status_ (&pi, dc);
1325   /* abort all pending requests */
1326   if (NULL != dc->th)
1327     {
1328       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1329       dc->th = NULL;
1330     }
1331   GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1332   dc->client = NULL;
1333   GNUNET_FS_free_download_request_ (dc->top_request);
1334   dc->top_request = NULL;
1335   GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1336   dc->active = NULL;
1337   dc->pending_head = NULL;
1338   dc->pending_tail = NULL;
1339   GNUNET_FS_download_sync_ (dc);
1340   return GNUNET_NO;
1341 }
1342
1343
1344 /**
1345  * Process a download result.
1346  *
1347  * @param dc our download context
1348  * @param type type of the result
1349  * @param data the (encrypted) response
1350  * @param size size of data
1351  */
1352 static void
1353 process_result (struct GNUNET_FS_DownloadContext *dc,
1354                 enum GNUNET_BLOCK_Type type,
1355                 const void *data,
1356                 size_t size)
1357 {
1358   struct ProcessResultClosure prc;
1359
1360   prc.dc = dc;
1361   prc.data = data;
1362   prc.size = size;
1363   prc.type = type;
1364   prc.do_store = GNUNET_YES;
1365   GNUNET_CRYPTO_hash (data, size, &prc.query);
1366 #if DEBUG_DOWNLOAD
1367   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1368               "Received result for query `%s' from `%s'-service\n",
1369               GNUNET_h2s (&prc.query),
1370               "FS");
1371 #endif
1372   GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1373                                               &prc.query,
1374                                               &process_result_with_request,
1375                                               &prc);
1376 }
1377
1378
1379 /**
1380  * Type of a function to call when we receive a message
1381  * from the service.
1382  *
1383  * @param cls closure
1384  * @param msg message received, NULL on timeout or fatal error
1385  */
1386 static void 
1387 receive_results (void *cls,
1388                  const struct GNUNET_MessageHeader * msg)
1389 {
1390   struct GNUNET_FS_DownloadContext *dc = cls;
1391   const struct PutMessage *cm;
1392   uint16_t msize;
1393
1394   if ( (NULL == msg) ||
1395        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
1396        (sizeof (struct PutMessage) > ntohs(msg->size)) )
1397     {
1398       GNUNET_break (msg == NULL);       
1399       try_reconnect (dc);
1400       return;
1401     }
1402   msize = ntohs(msg->size);
1403   cm = (const struct PutMessage*) msg;
1404   process_result (dc, 
1405                   ntohl (cm->type),
1406                   &cm[1],
1407                   msize - sizeof (struct PutMessage));
1408   if (dc->client == NULL)
1409     return; /* fatal error */
1410   /* continue receiving */
1411   GNUNET_CLIENT_receive (dc->client,
1412                          &receive_results,
1413                          dc,
1414                          GNUNET_TIME_UNIT_FOREVER_REL);
1415 }
1416
1417
1418
1419 /**
1420  * We're ready to transmit a search request to the
1421  * file-sharing service.  Do it.  If there is 
1422  * more than one request pending, try to send 
1423  * multiple or request another transmission.
1424  *
1425  * @param cls closure
1426  * @param size number of bytes available in buf
1427  * @param buf where the callee should write the message
1428  * @return number of bytes written to buf
1429  */
1430 static size_t
1431 transmit_download_request (void *cls,
1432                            size_t size, 
1433                            void *buf)
1434 {
1435   struct GNUNET_FS_DownloadContext *dc = cls;
1436   size_t msize;
1437   struct SearchMessage *sm;
1438   struct DownloadRequest *dr;
1439
1440   dc->th = NULL;
1441   if (NULL == buf)
1442     {
1443 #if DEBUG_DOWNLOAD
1444       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1445                   "Transmitting download request failed, trying to reconnect\n");
1446 #endif
1447       try_reconnect (dc);
1448       return 0;
1449     }
1450   GNUNET_assert (size >= sizeof (struct SearchMessage));
1451   msize = 0;
1452   sm = buf;
1453   while ( (NULL != (dr = dc->pending_head)) &&
1454           (size >= msize + sizeof (struct SearchMessage)) )
1455     {
1456 #if DEBUG_DOWNLOAD
1457       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1458                   "Transmitting download request for `%s' to `%s'-service\n",
1459                   GNUNET_h2s (&dr->chk.query),
1460                   "FS");
1461 #endif
1462       memset (sm, 0, sizeof (struct SearchMessage));
1463       sm->header.size = htons (sizeof (struct SearchMessage));
1464       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1465       if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
1466         sm->options = htonl (1);
1467       else
1468         sm->options = htonl (0);      
1469       if (dr->depth == 0)
1470         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
1471       else
1472         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
1473       sm->anonymity_level = htonl (dc->anonymity);
1474       sm->target = dc->target.hashPubKey;
1475       sm->query = dr->chk.query;
1476       GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1477                                    dc->pending_tail,
1478                                    dr);
1479       dr->is_pending = GNUNET_NO;
1480       msize += sizeof (struct SearchMessage);
1481       sm++;
1482     }
1483   if (dc->pending_head != NULL)
1484     {
1485       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1486                                                     sizeof (struct SearchMessage),
1487                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1488                                                     GNUNET_NO,
1489                                                     &transmit_download_request,
1490                                                     dc); 
1491       GNUNET_assert (dc->th != NULL);
1492     }
1493   return msize;
1494 }
1495
1496
1497 /**
1498  * Reconnect to the FS service and transmit our queries NOW.
1499  *
1500  * @param cls our download context
1501  * @param tc unused
1502  */
1503 static void
1504 do_reconnect (void *cls,
1505               const struct GNUNET_SCHEDULER_TaskContext *tc)
1506 {
1507   struct GNUNET_FS_DownloadContext *dc = cls;
1508   struct GNUNET_CLIENT_Connection *client;
1509   
1510   dc->task = GNUNET_SCHEDULER_NO_TASK;
1511   client = GNUNET_CLIENT_connect ("fs",
1512                                   dc->h->cfg);
1513   if (NULL == client)
1514     {
1515       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1516                   "Connecting to `%s'-service failed, will try again.\n",
1517                   "FS");
1518       try_reconnect (dc);
1519       return;
1520     }
1521   dc->client = client;
1522   if (dc->pending_head != NULL)
1523     {
1524       dc->th = GNUNET_CLIENT_notify_transmit_ready (client,
1525                                                     sizeof (struct SearchMessage),
1526                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1527                                                     GNUNET_NO,
1528                                                     &transmit_download_request,
1529                                                     dc);
1530       GNUNET_assert (dc->th != NULL);
1531     }
1532   GNUNET_CLIENT_receive (client,
1533                          &receive_results,
1534                          dc,
1535                          GNUNET_TIME_UNIT_FOREVER_REL);
1536 }
1537
1538
1539 /**
1540  * Add entries to the pending list.
1541  *
1542  * @param cls our download context
1543  * @param key unused
1544  * @param entry entry of type "struct DownloadRequest"
1545  * @return GNUNET_OK
1546  */
1547 static int
1548 retry_entry (void *cls,
1549              const GNUNET_HashCode *key,
1550              void *entry)
1551 {
1552   struct GNUNET_FS_DownloadContext *dc = cls;
1553   struct DownloadRequest *dr = entry;
1554
1555   dr->next = NULL;
1556   dr->prev = NULL;
1557   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
1558                                dc->pending_tail,
1559                                dr);
1560   dr->is_pending = GNUNET_YES;
1561   return GNUNET_OK;
1562 }
1563
1564
1565 /**
1566  * We've lost our connection with the FS service.
1567  * Re-establish it and re-transmit all of our
1568  * pending requests.
1569  *
1570  * @param dc download context that is having trouble
1571  */
1572 static void
1573 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1574 {
1575   
1576   if (NULL != dc->client)
1577     {
1578 #if DEBUG_DOWNLOAD
1579       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1580                   "Moving all requests back to pending list\n");
1581 #endif
1582       if (NULL != dc->th)
1583         {
1584           GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1585           dc->th = NULL;
1586         }
1587       /* full reset of the pending list */
1588       dc->pending_head = NULL;
1589       dc->pending_tail = NULL;
1590       GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1591                                              &retry_entry,
1592                                              dc);
1593       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1594       dc->client = NULL;
1595     }
1596 #if DEBUG_DOWNLOAD
1597   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1598               "Will try to reconnect in 1s\n");
1599 #endif
1600   dc->task
1601     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1602                                     &do_reconnect,
1603                                     dc);
1604 }
1605
1606
1607 /**
1608  * We're allowed to ask the FS service for our blocks.  Start the download.
1609  *
1610  * @param cls the 'struct GNUNET_FS_DownloadContext'
1611  * @param client handle to use for communcation with FS (we must destroy it!)
1612  */
1613 static void
1614 activate_fs_download (void *cls,
1615                       struct GNUNET_CLIENT_Connection *client)
1616 {
1617   struct GNUNET_FS_DownloadContext *dc = cls;
1618   struct GNUNET_FS_ProgressInfo pi;
1619
1620 #if DEBUG_DOWNLOAD
1621   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1622               "Download activated\n");
1623 #endif
1624   GNUNET_assert (NULL != client);
1625   GNUNET_assert (dc->client == NULL);
1626   GNUNET_assert (dc->th == NULL);
1627   dc->client = client;
1628   GNUNET_CLIENT_receive (client,
1629                          &receive_results,
1630                          dc,
1631                          GNUNET_TIME_UNIT_FOREVER_REL);
1632   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1633   GNUNET_FS_download_make_status_ (&pi, dc);
1634   GNUNET_assert (dc->pending_head == NULL);
1635   GNUNET_assert (dc->pending_tail == NULL);
1636   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1637                                          &retry_entry,
1638                                          dc);
1639 #if DEBUG_DOWNLOAD
1640   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1641               "Asking for transmission to FS service\n");
1642 #endif
1643   if (dc->pending_head != NULL)
1644     {
1645       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1646                                                     sizeof (struct SearchMessage),
1647                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1648                                                     GNUNET_NO,
1649                                                     &transmit_download_request,
1650                                                     dc);    
1651       GNUNET_assert (dc->th != NULL);
1652     }
1653 }
1654
1655
1656 /**
1657  * We must stop to ask the FS service for our blocks.  Pause the download.
1658  *
1659  * @param cls the 'struct GNUNET_FS_DownloadContext'
1660  */
1661 static void
1662 deactivate_fs_download (void *cls)
1663 {
1664   struct GNUNET_FS_DownloadContext *dc = cls;
1665   struct GNUNET_FS_ProgressInfo pi;
1666
1667 #if DEBUG_DOWNLOAD
1668   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1669               "Download deactivated\n");
1670 #endif  
1671   if (NULL != dc->th)
1672     {
1673       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1674       dc->th = NULL;
1675     }
1676   if (NULL != dc->client)
1677     {
1678       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1679       dc->client = NULL;
1680     }
1681   dc->pending_head = NULL;
1682   dc->pending_tail = NULL;
1683   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1684   GNUNET_FS_download_make_status_ (&pi, dc);
1685 }
1686
1687
1688 /**
1689  * (recursively) Create a download request structure.
1690  *
1691  * @param parent parent of the current entry
1692  * @param depth depth of the current entry, 0 are the DBLOCKs,
1693  *              top level block is 'dc->treedepth - 1'
1694  * @param dr_offset offset in the original file this block maps to
1695  *              (as in, offset of the first byte of the first DBLOCK 
1696  *               in the subtree rooted in the returned download request tree)
1697  * @param file_start_offset desired starting offset for the download
1698  *             in the original file; requesting tree should not contain
1699  *             DBLOCKs prior to the file_start_offset
1700  * @param desired_length desired number of bytes the user wanted to access
1701  *        (from file_start_offset).  Resulting tree should not contain
1702  *        DBLOCKs after file_start_offset + file_length.
1703  * @return download request tree for the given range of DBLOCKs at
1704  *         the specified depth
1705  */
1706 static struct DownloadRequest *
1707 create_download_request (struct DownloadRequest *parent,
1708                          unsigned int depth,
1709                          uint64_t dr_offset,
1710                          uint64_t file_start_offset,
1711                          uint64_t desired_length)
1712 {
1713   struct DownloadRequest *dr;
1714   unsigned int i;
1715   unsigned int head_skip;
1716   uint64_t child_block_size;
1717   
1718   dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1719   dr->parent = parent;
1720   dr->depth = depth;
1721   dr->offset = dr_offset;
1722   if (depth > 0)
1723     {
1724       child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1725       
1726       /* calculate how many blocks at this level are not interesting
1727          from the start (rounded down), either because of the requested
1728          file offset or because this IBlock is further along */
1729       if (dr_offset < file_start_offset)
1730         head_skip = file_start_offset / child_block_size;       
1731       else
1732         head_skip = dr_offset / child_block_size;       
1733
1734       /* calculate index of last block at this level that is interesting (rounded up) */
1735       dr->num_children = file_start_offset + desired_length / child_block_size;
1736       if (dr->num_children * child_block_size < file_start_offset + desired_length)
1737         dr->num_children++; /* round up */
1738
1739       /* now we can get the total number of children for this block */
1740       dr->num_children -= head_skip;
1741       if (dr->num_children > CHK_PER_INODE)
1742         dr->num_children = CHK_PER_INODE; /* cap at max */
1743
1744       /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1745       GNUNET_assert (dr->num_children > 0); 
1746
1747       dr->children = GNUNET_malloc (dr->num_children * 
1748                                     sizeof (struct DownloadRequest *));
1749       for (i=0;i<dr->num_children;i++)
1750         dr->children[i] = create_download_request (dr,
1751                                                    depth - 1,
1752                                                    dr_offset + i * child_block_size,
1753                                                    file_start_offset,
1754                                                    desired_length);      
1755     }
1756   return dr;
1757 }
1758
1759
1760 /**
1761  * Continuation after a possible attempt to reconstruct
1762  * the current IBlock from the existing file.
1763  *
1764  * @param cls the 'struct ReconstructContext'
1765  * @param tc scheduler context
1766  */
1767 static void
1768 reconstruct_cont (void *cls,
1769                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1770 {
1771   struct GNUNET_FS_DownloadContext *dc = cls;
1772
1773   /* clean up state from tree encoder */
1774   if (dc->te != NULL)
1775     {
1776       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
1777       dc->te = NULL;
1778     }
1779   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
1780     {
1781       GNUNET_SCHEDULER_cancel (dc->task);
1782       dc->task = GNUNET_SCHEDULER_NO_TASK;
1783     }
1784   if (dc->rfh != NULL)
1785     {
1786       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1787       dc->rfh = NULL;
1788     }
1789   /* start "normal" download */
1790   schedule_block_download (dc, 
1791                            dc->top_request);
1792 }
1793
1794
1795 /**
1796  * Task requesting the next block from the tree encoder.
1797  *
1798  * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1799  * @param tc task context
1800  */
1801 static void
1802 get_next_block (void *cls,
1803                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1804 {
1805   struct GNUNET_FS_DownloadContext *dc = cls;
1806
1807   dc->task = GNUNET_SCHEDULER_NO_TASK;
1808   GNUNET_FS_tree_encoder_next (dc->te);
1809 }
1810
1811
1812
1813 /**
1814  * Function called asking for the current (encoded)
1815  * block to be processed.  After processing the
1816  * client should either call "GNUNET_FS_tree_encode_next"
1817  * or (on error) "GNUNET_FS_tree_encode_finish".
1818  *
1819  * This function checks if the content on disk matches
1820  * the expected content based on the URI.
1821  * 
1822  * @param cls closure
1823  * @param chk content hash key for the block
1824  * @param offset offset of the block
1825  * @param depth depth of the block, 0 for DBLOCK
1826  * @param type type of the block (IBLOCK or DBLOCK)
1827  * @param block the (encrypted) block
1828  * @param block_size size of block (in bytes)
1829  */
1830 static void 
1831 reconstruct_cb (void *cls,
1832                 const struct ContentHashKey *chk,
1833                 uint64_t offset,
1834                 unsigned int depth,
1835                 enum GNUNET_BLOCK_Type type,
1836                 const void *block,
1837                 uint16_t block_size)
1838 {
1839   struct GNUNET_FS_DownloadContext *dc = cls;
1840   struct GNUNET_FS_ProgressInfo pi;
1841   struct DownloadRequest *dr;
1842   uint64_t blen;
1843   unsigned int chld;
1844
1845   /* find corresponding request entry */
1846   dr = dc->top_request;
1847   while (dr->depth > depth)
1848     {
1849       blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1850       chld = (offset - dr->offset) / blen;
1851       GNUNET_assert (chld < dr->num_children);
1852       dr = dr->children[chld];
1853     }
1854   switch (dr->state)
1855     {
1856     case BRS_INIT:
1857       break;
1858     case BRS_RECONSTRUCT_DOWN:
1859       break;
1860     case BRS_RECONSTRUCT_META_UP:
1861       break;
1862     case BRS_RECONSTRUCT_UP:
1863       break;
1864     case BRS_CHK_SET:
1865       if (0 == memcmp (chk,
1866                        &dr->chk,
1867                        sizeof (struct ContentHashKey)))
1868         {
1869           /* block matches, hence tree below matches; 
1870              this request is done! */
1871           dr->state = BRS_DOWNLOAD_UP;
1872           /* calculate how many bytes of payload this block
1873              corresponds to */
1874           blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1875           /* how many of those bytes are in the requested range? */
1876           blen = GNUNET_MIN (blen,
1877                              dc->length + dc->offset - dr->offset);
1878           /* signal progress */
1879           dc->completed += blen;
1880           pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1881           pi.value.download.specifics.progress.data = NULL;
1882           pi.value.download.specifics.progress.offset = offset;
1883           pi.value.download.specifics.progress.data_len = 0;
1884           pi.value.download.specifics.progress.depth = 0;
1885           GNUNET_FS_download_make_status_ (&pi, dc);             
1886         }
1887       else
1888         {
1889         }
1890       break; 
1891     case BRS_DOWNLOAD_DOWN:
1892       break; 
1893     case BRS_DOWNLOAD_UP:
1894       break;
1895     case BRS_ERROR:
1896       break;
1897     default:
1898       GNUNET_assert (0);
1899       break;
1900     }
1901   if ( (dr == dc->top_request) &&
1902        (dr->state == BRS_DOWNLOAD_UP) )
1903     {
1904       check_completed (dc);
1905       return;
1906     }
1907   dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
1908                                        dc);
1909 }
1910
1911
1912 /**
1913  * Function called by the tree encoder to obtain a block of plaintext
1914  * data (for the lowest level of the tree).
1915  *
1916  * @param cls our 'struct ReconstructContext'
1917  * @param offset identifies which block to get
1918  * @param max (maximum) number of bytes to get; returning
1919  *        fewer will also cause errors
1920  * @param buf where to copy the plaintext buffer
1921  * @param emsg location to store an error message (on error)
1922  * @return number of bytes copied to buf, 0 on error
1923  */
1924 static size_t
1925 fh_reader (void *cls,
1926            uint64_t offset,
1927            size_t max, 
1928            void *buf,
1929            char **emsg)
1930 {
1931   struct GNUNET_FS_DownloadContext *dc = cls;
1932   struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1933   ssize_t ret;
1934
1935   *emsg = NULL;
1936   if (offset !=
1937       GNUNET_DISK_file_seek (fh,
1938                              offset,
1939                              GNUNET_DISK_SEEK_SET))
1940     {
1941       *emsg = GNUNET_strdup (strerror (errno));
1942       return 0;
1943     }
1944   ret = GNUNET_DISK_file_read (fh, buf, max);
1945   if (ret < 0)
1946     {
1947       *emsg = GNUNET_strdup (strerror (errno));
1948       return 0;
1949     }
1950   return ret;
1951 }
1952
1953
1954 /**
1955  * Task that creates the initial (top-level) download
1956  * request for the file.
1957  *
1958  * @param cls the 'struct GNUNET_FS_DownloadContext'
1959  * @param tc scheduler context
1960  */
1961 void
1962 GNUNET_FS_download_start_task_ (void *cls,
1963                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1964 {
1965   struct GNUNET_FS_DownloadContext *dc = cls;  
1966   struct GNUNET_FS_ProgressInfo pi;
1967   struct GNUNET_DISK_FileHandle *fh;
1968
1969 #if DEBUG_DOWNLOAD
1970   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1971               "Start task running...\n");
1972 #endif
1973   dc->task = GNUNET_SCHEDULER_NO_TASK;
1974   if (dc->length == 0)
1975     {
1976       /* no bytes required! */
1977       if (dc->filename != NULL) 
1978         {
1979           fh = GNUNET_DISK_file_open (dc->filename, 
1980                                       GNUNET_DISK_OPEN_READWRITE |
1981                                       GNUNET_DISK_OPEN_CREATE |
1982                                       ( (0 == GNUNET_FS_uri_chk_get_file_size (dc->uri)) 
1983                                         ? GNUNET_DISK_OPEN_TRUNCATE : 0),
1984                                       GNUNET_DISK_PERM_USER_READ |
1985                                       GNUNET_DISK_PERM_USER_WRITE |
1986                                       GNUNET_DISK_PERM_GROUP_READ |
1987                                       GNUNET_DISK_PERM_OTHER_READ);
1988           GNUNET_DISK_file_close (fh);
1989         }
1990       GNUNET_FS_download_sync_ (dc);
1991       check_completed (dc);
1992       return;
1993     }
1994   if (dc->emsg != NULL) 
1995     return;
1996   if (dc->top_request == NULL)
1997     {
1998       dc->top_request = create_download_request (NULL, dc->treedepth - 1, 0,
1999                                                  dc->offset, dc->length);
2000       dc->top_request->state = BRS_CHK_SET;
2001       dc->top_request->chk = (dc->uri->type == chk) 
2002         ? dc->uri->data.chk.chk
2003         : dc->uri->data.loc.fi.chk;
2004       /* signal start */
2005       GNUNET_FS_download_sync_ (dc);
2006       pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
2007       pi.value.download.specifics.start.meta = dc->meta;
2008       GNUNET_FS_download_make_status_ (&pi, dc);
2009     }
2010   GNUNET_FS_download_start_downloading_ (dc);
2011   /* attempt reconstruction from disk */
2012   if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))    
2013     dc->rfh = GNUNET_DISK_file_open (dc->filename,
2014                                      GNUNET_DISK_OPEN_READ,
2015                                      GNUNET_DISK_PERM_NONE);    
2016   if (dc->top_request->state == BRS_CHK_SET)
2017     {
2018       if (dc->rfh != NULL)
2019         {
2020           /* first, try top-down */
2021 #if DEBUG_DOWNLOAD
2022           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2023                       "Trying top-down reconstruction for `%s'\n",
2024                       dc->filename);
2025 #endif
2026           try_top_down_reconstruction (dc, dc->top_request);
2027           switch (dc->top_request->state)
2028             {
2029             case BRS_CHK_SET:
2030               break; /* normal */
2031             case BRS_DOWNLOAD_DOWN:
2032               break; /* normal, some blocks already down */
2033             case BRS_DOWNLOAD_UP:
2034               /* already done entirely, party! */
2035               if (dc->rfh != NULL)
2036                 {
2037                   /* avoid hanging on to file handle longer than 
2038                      necessary */
2039                   GNUNET_DISK_file_close (dc->rfh);
2040                   dc->rfh = NULL;
2041                 }
2042               return;      
2043             case BRS_ERROR:
2044               GNUNET_asprintf (&dc->emsg,
2045                                _("Invalid URI"));
2046               GNUNET_FS_download_sync_ (dc);
2047               pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
2048               pi.value.download.specifics.error.message = dc->emsg;
2049               GNUNET_FS_download_make_status_ (&pi, dc);
2050               return;
2051             default:
2052               GNUNET_assert (0);
2053               break;
2054             }
2055         }
2056     }
2057   /* attempt reconstruction from meta data */
2058   if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
2059        (NULL != dc->meta) )
2060     {
2061 #if DEBUG_DOWNLOAD
2062       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2063                   "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
2064                   (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
2065                   (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
2066 #endif
2067       GNUNET_CONTAINER_meta_data_iterate (dc->meta,
2068                                           &match_full_data,
2069                                           dc);
2070       if (dc->top_request->state == BRS_DOWNLOAD_UP)
2071         {
2072           if (dc->rfh != NULL)
2073             {
2074               /* avoid hanging on to file handle longer than 
2075                  necessary */
2076               GNUNET_DISK_file_close (dc->rfh);
2077               dc->rfh = NULL;
2078             }
2079           return; /* finished, status update was already done for us */
2080         }
2081     }
2082   if (dc->rfh != NULL)
2083     {
2084       /* finally, try bottom-up */
2085 #if DEBUG_DOWNLOAD
2086       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2087                   "Trying bottom-up reconstruction of file `%s'\n",
2088                   dc->filename);
2089 #endif
2090       dc->te = GNUNET_FS_tree_encoder_create (dc->h,
2091                                               dc->old_file_size,
2092                                               dc,
2093                                               &fh_reader,
2094                                               &reconstruct_cb,
2095                                               NULL,
2096                                               &reconstruct_cont);     
2097       dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
2098                                            dc);
2099     }
2100   else
2101     {
2102       /* simple, top-level download */
2103         schedule_block_download (dc, 
2104                                  dc->top_request);
2105     }
2106   if (dc->top_request->state == BRS_DOWNLOAD_UP)
2107     check_completed (dc);
2108 }
2109
2110
2111 /**
2112  * Create SUSPEND event for the given download operation
2113  * and then clean up our state (without stop signal).
2114  *
2115  * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
2116  */
2117 void
2118 GNUNET_FS_download_signal_suspend_ (void *cls)
2119 {
2120   struct GNUNET_FS_DownloadContext *dc = cls;
2121   struct GNUNET_FS_ProgressInfo pi;
2122
2123   if (dc->top != NULL)
2124     GNUNET_FS_end_top (dc->h, dc->top);
2125   while (NULL != dc->child_head)
2126     GNUNET_FS_download_signal_suspend_ (dc->child_head);  
2127   if (dc->search != NULL)
2128     {
2129       dc->search->download = NULL;
2130       dc->search = NULL;
2131     }
2132   if (dc->job_queue != NULL)
2133     {
2134       GNUNET_FS_dequeue_ (dc->job_queue);
2135       dc->job_queue = NULL;
2136     }
2137   if (dc->parent != NULL)
2138     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2139                                  dc->parent->child_tail,
2140                                  dc);  
2141   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2142     {
2143       GNUNET_SCHEDULER_cancel (dc->task);
2144       dc->task = GNUNET_SCHEDULER_NO_TASK;
2145     }
2146   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
2147   GNUNET_FS_download_make_status_ (&pi, dc);
2148   if (dc->te != NULL)
2149     {
2150       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);       
2151       dc->te = NULL;
2152     }
2153   if (dc->rfh != NULL)
2154     {
2155       GNUNET_DISK_file_close (dc->rfh);
2156       dc->rfh = NULL;
2157     }
2158   GNUNET_FS_free_download_request_ (dc->top_request);
2159   if (dc->active != NULL)
2160     {
2161       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2162       dc->active = NULL;
2163     }
2164   GNUNET_free_non_null (dc->filename);
2165   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2166   GNUNET_FS_uri_destroy (dc->uri);
2167   GNUNET_free_non_null (dc->temp_filename);
2168   GNUNET_free_non_null (dc->serialization);
2169   GNUNET_free (dc);
2170 }
2171
2172
2173 /**
2174  * Download parts of a file.  Note that this will store
2175  * the blocks at the respective offset in the given file.  Also, the
2176  * download is still using the blocking of the underlying FS
2177  * encoding.  As a result, the download may *write* outside of the
2178  * given boundaries (if offset and length do not match the 32k FS
2179  * block boundaries). <p>
2180  *
2181  * This function should be used to focus a download towards a
2182  * particular portion of the file (optimization), not to strictly
2183  * limit the download to exactly those bytes.
2184  *
2185  * @param h handle to the file sharing subsystem
2186  * @param uri the URI of the file (determines what to download); CHK or LOC URI
2187  * @param meta known metadata for the file (can be NULL)
2188  * @param filename where to store the file, maybe NULL (then no file is
2189  *        created on disk and data must be grabbed from the callbacks)
2190  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2191  *        can be NULL (in which case we will pick a name if needed); the temporary file
2192  *        may already exist, in which case we will try to use the data that is there and
2193  *        if it is not what is desired, will overwrite it
2194  * @param offset at what offset should we start the download (typically 0)
2195  * @param length how many bytes should be downloaded starting at offset
2196  * @param anonymity anonymity level to use for the download
2197  * @param options various options
2198  * @param cctx initial value for the client context for this download
2199  * @param parent parent download to associate this download with (use NULL
2200  *        for top-level downloads; useful for manually-triggered recursive downloads)
2201  * @return context that can be used to control this download
2202  */
2203 struct GNUNET_FS_DownloadContext *
2204 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2205                           const struct GNUNET_FS_Uri *uri,
2206                           const struct GNUNET_CONTAINER_MetaData *meta,
2207                           const char *filename,
2208                           const char *tempname,
2209                           uint64_t offset,
2210                           uint64_t length,
2211                           uint32_t anonymity,
2212                           enum GNUNET_FS_DownloadOptions options,
2213                           void *cctx,
2214                           struct GNUNET_FS_DownloadContext *parent)
2215 {
2216   struct GNUNET_FS_DownloadContext *dc;
2217
2218   GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
2219                  GNUNET_FS_uri_test_loc (uri) );
2220                  
2221   if ( (offset + length < offset) ||
2222        (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
2223     {      
2224       GNUNET_break (0);
2225       return NULL;
2226     }
2227 #if DEBUG_DOWNLOAD
2228   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2229               "Starting download `%s' of %llu bytes\n",
2230               filename,
2231               (unsigned long long) length);
2232 #endif
2233   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2234   dc->h = h;
2235   dc->parent = parent;
2236   if (parent != NULL)
2237     {
2238       GNUNET_CONTAINER_DLL_insert (parent->child_head,
2239                                    parent->child_tail,
2240                                    dc);
2241     }
2242   dc->uri = GNUNET_FS_uri_dup (uri);
2243   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2244   dc->client_info = cctx;
2245   dc->start_time = GNUNET_TIME_absolute_get ();
2246   if (NULL != filename)
2247     {
2248       dc->filename = GNUNET_strdup (filename);
2249       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2250         GNUNET_DISK_file_size (filename,
2251                                &dc->old_file_size,
2252                                GNUNET_YES);
2253     }
2254   if (GNUNET_FS_uri_test_loc (dc->uri))
2255     GNUNET_assert (GNUNET_OK ==
2256                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2257                                                         &dc->target));
2258   dc->offset = offset;
2259   dc->length = length;
2260   dc->anonymity = anonymity;
2261   dc->options = options;
2262   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2263   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
2264   if ( (filename == NULL) &&
2265        (is_recursive_download (dc) ) )
2266     {
2267       if (tempname != NULL)
2268         dc->temp_filename = GNUNET_strdup (tempname);
2269       else
2270         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2271     }
2272
2273 #if DEBUG_DOWNLOAD
2274   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2275               "Download tree has depth %u\n",
2276               dc->treedepth);
2277 #endif
2278   if (parent == NULL)
2279     {
2280       dc->top = GNUNET_FS_make_top (dc->h,
2281                                     &GNUNET_FS_download_signal_suspend_,
2282                                     dc);
2283     }
2284   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2285                                        dc);
2286   return dc;
2287 }
2288
2289
2290 /**
2291  * Download parts of a file based on a search result.  The download
2292  * will be associated with the search result (and the association
2293  * will be preserved when serializing/deserializing the state).
2294  * If the search is stopped, the download will not be aborted but
2295  * be 'promoted' to a stand-alone download.
2296  *
2297  * As with the other download function, this will store
2298  * the blocks at the respective offset in the given file.  Also, the
2299  * download is still using the blocking of the underlying FS
2300  * encoding.  As a result, the download may *write* outside of the
2301  * given boundaries (if offset and length do not match the 32k FS
2302  * block boundaries). <p>
2303  *
2304  * The given range can be used to focus a download towards a
2305  * particular portion of the file (optimization), not to strictly
2306  * limit the download to exactly those bytes.
2307  *
2308  * @param h handle to the file sharing subsystem
2309  * @param sr the search result to use for the download (determines uri and
2310  *        meta data and associations)
2311  * @param filename where to store the file, maybe NULL (then no file is
2312  *        created on disk and data must be grabbed from the callbacks)
2313  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2314  *        can be NULL (in which case we will pick a name if needed); the temporary file
2315  *        may already exist, in which case we will try to use the data that is there and
2316  *        if it is not what is desired, will overwrite it
2317  * @param offset at what offset should we start the download (typically 0)
2318  * @param length how many bytes should be downloaded starting at offset
2319  * @param anonymity anonymity level to use for the download
2320  * @param options various download options
2321  * @param cctx initial value for the client context for this download
2322  * @return context that can be used to control this download
2323  */
2324 struct GNUNET_FS_DownloadContext *
2325 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2326                                       struct GNUNET_FS_SearchResult *sr,
2327                                       const char *filename,
2328                                       const char *tempname,
2329                                       uint64_t offset,
2330                                       uint64_t length,
2331                                       uint32_t anonymity,
2332                                       enum GNUNET_FS_DownloadOptions options,
2333                                       void *cctx)
2334 {
2335   struct GNUNET_FS_DownloadContext *dc;
2336
2337   if ( (sr == NULL) ||
2338        (sr->download != NULL) )
2339     {
2340       GNUNET_break (0);
2341       return NULL;
2342     }
2343   GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
2344                  GNUNET_FS_uri_test_loc (sr->uri) );             
2345   if ( (offset + length < offset) ||
2346        (offset + length > sr->uri->data.chk.file_length) )
2347     {      
2348       GNUNET_break (0);
2349       return NULL;
2350     }
2351 #if DEBUG_DOWNLOAD
2352   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2353               "Starting download `%s' of %llu bytes\n",
2354               filename,
2355               (unsigned long long) length);
2356 #endif
2357   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2358   dc->h = h;
2359   dc->search = sr;
2360   sr->download = dc;
2361   if (sr->probe_ctx != NULL)
2362     {
2363       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2364       sr->probe_ctx = NULL;      
2365     }
2366   dc->uri = GNUNET_FS_uri_dup (sr->uri);
2367   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
2368   dc->client_info = cctx;
2369   dc->start_time = GNUNET_TIME_absolute_get ();
2370   if (NULL != filename)
2371     {
2372       dc->filename = GNUNET_strdup (filename);
2373       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2374         GNUNET_DISK_file_size (filename,
2375                                &dc->old_file_size,
2376                                GNUNET_YES);
2377     }
2378   if (GNUNET_FS_uri_test_loc (dc->uri))
2379     GNUNET_assert (GNUNET_OK ==
2380                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2381                                                         &dc->target));
2382   dc->offset = offset;
2383   dc->length = length;
2384   dc->anonymity = anonymity;
2385   dc->options = options;
2386   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2387   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
2388   if ( (filename == NULL) &&
2389        (is_recursive_download (dc) ) )
2390     {
2391       if (tempname != NULL)
2392         dc->temp_filename = GNUNET_strdup (tempname);
2393       else
2394         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2395     }
2396
2397 #if DEBUG_DOWNLOAD
2398   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2399               "Download tree has depth %u\n",
2400               dc->treedepth);
2401 #endif
2402   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2403                                        dc);
2404   return dc;  
2405 }
2406
2407
2408 /**
2409  * Start the downloading process (by entering the queue).
2410  *
2411  * @param dc our download context
2412  */
2413 void
2414 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2415 {
2416   if (dc->completed == dc->length)
2417     return;
2418   GNUNET_assert (dc->job_queue == NULL);
2419   dc->job_queue = GNUNET_FS_queue_ (dc->h, 
2420                                     &activate_fs_download,
2421                                     &deactivate_fs_download,
2422                                     dc,
2423                                     (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
2424 }
2425
2426
2427 /**
2428  * Stop a download (aborts if download is incomplete).
2429  *
2430  * @param dc handle for the download
2431  * @param do_delete delete files of incomplete downloads
2432  */
2433 void
2434 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
2435                          int do_delete)
2436 {
2437   struct GNUNET_FS_ProgressInfo pi;
2438   int have_children;
2439
2440   if (dc->top != NULL)
2441     GNUNET_FS_end_top (dc->h, dc->top);
2442
2443
2444   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2445     {
2446       GNUNET_SCHEDULER_cancel (dc->task);
2447       dc->task = GNUNET_SCHEDULER_NO_TASK;
2448     }
2449   if (dc->search != NULL)
2450     {
2451       dc->search->download = NULL;
2452       dc->search = NULL;
2453     }
2454   if (dc->job_queue != NULL)
2455     {
2456       GNUNET_FS_dequeue_ (dc->job_queue);
2457       dc->job_queue = NULL;
2458     }
2459   if (dc->te != NULL)
2460     {
2461       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2462       dc->te = NULL;
2463     }
2464   have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2465   while (NULL != dc->child_head)
2466     GNUNET_FS_download_stop (dc->child_head, 
2467                              do_delete);
2468   if (dc->parent != NULL)
2469     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2470                                  dc->parent->child_tail,
2471                                  dc);  
2472   if (dc->serialization != NULL)
2473     GNUNET_FS_remove_sync_file_ (dc->h,
2474                                  ( (dc->parent != NULL)  || (dc->search != NULL) )
2475                                  ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2476                                  : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD , 
2477                                  dc->serialization);
2478   if ( (GNUNET_YES == have_children) &&
2479        (dc->parent == NULL) )
2480     GNUNET_FS_remove_sync_dir_ (dc->h, 
2481                                 (dc->search != NULL) 
2482                                 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2483                                 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2484                                 dc->serialization);  
2485   pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2486   GNUNET_FS_download_make_status_ (&pi, dc);
2487   GNUNET_FS_free_download_request_ (dc->top_request);
2488   dc->top_request = NULL;
2489   if (dc->active != NULL)
2490     {
2491       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2492       dc->active = NULL;
2493     }
2494   if (dc->filename != NULL)
2495     {
2496       if ( (dc->completed != dc->length) &&
2497            (GNUNET_YES == do_delete) )
2498         {
2499           if (0 != UNLINK (dc->filename))
2500             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2501                                       "unlink",
2502                                       dc->filename);
2503         }
2504       GNUNET_free (dc->filename);
2505     }
2506   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2507   GNUNET_FS_uri_destroy (dc->uri);
2508   if (NULL != dc->temp_filename)
2509     {
2510       if (0 != UNLINK (dc->temp_filename))
2511         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2512                                   "unlink",
2513                                   dc->temp_filename);
2514       GNUNET_free (dc->temp_filename);
2515     }
2516   GNUNET_free_non_null (dc->serialization);
2517   GNUNET_free (dc);
2518 }
2519
2520 /* end of fs_download.c */