22fdd2141b5ca1837628fd0d6ff55386b9336a4d
[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 (sfn);
999       GNUNET_free (dn);
1000     }
1001   if ( (full_name != NULL) &&
1002        (GNUNET_OK !=
1003         GNUNET_DISK_directory_create_for_file (full_name)) )
1004     {
1005       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1006                   _("Failed to create directory for recursive download of `%s'\n"),
1007                   full_name);
1008       GNUNET_free (full_name);
1009       GNUNET_free_non_null (fn);
1010       return;
1011     }
1012
1013   temp_name = NULL;
1014 #if DEBUG_DOWNLOAD
1015   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1016               "Triggering recursive download of size %llu with %u bytes MD\n",
1017               (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
1018               (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (meta));
1019 #endif
1020   GNUNET_FS_download_start (dc->h,
1021                             uri,
1022                             meta,
1023                             full_name, temp_name,
1024                             0,
1025                             GNUNET_FS_uri_chk_get_file_size (uri),
1026                             dc->anonymity,
1027                             dc->options,
1028                             NULL,
1029                             dc);
1030   GNUNET_free_non_null (full_name);
1031   GNUNET_free_non_null (temp_name);
1032   GNUNET_free_non_null (fn);
1033 }
1034
1035
1036 /**
1037  * (recursively) free download request structure
1038  *
1039  * @param dr request to free
1040  */
1041 void
1042 GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
1043 {
1044   unsigned int i;
1045
1046   if (dr == NULL)
1047     return;
1048   for (i=0;i<dr->num_children;i++)
1049     GNUNET_FS_free_download_request_ (dr->children[i]);
1050   GNUNET_free_non_null (dr->children);
1051   GNUNET_free (dr);
1052 }
1053
1054
1055 /**
1056  * Iterator over entries in the pending requests in the 'active' map for the
1057  * reply that we just got.
1058  *
1059  * @param cls closure (our 'struct ProcessResultClosure')
1060  * @param key query for the given value / request
1061  * @param value value in the hash map (a 'struct DownloadRequest')
1062  * @return GNUNET_YES (we should continue to iterate); unless serious error
1063  */
1064 static int
1065 process_result_with_request (void *cls,
1066                              const GNUNET_HashCode *key,
1067                              void *value)
1068 {
1069   struct ProcessResultClosure *prc = cls;
1070   struct DownloadRequest *dr = value;
1071   struct GNUNET_FS_DownloadContext *dc = prc->dc;
1072   struct DownloadRequest *drc;
1073   struct GNUNET_DISK_FileHandle *fh = NULL;
1074   struct GNUNET_CRYPTO_AesSessionKey skey;
1075   struct GNUNET_CRYPTO_AesInitializationVector iv;
1076   char pt[prc->size];
1077   struct GNUNET_FS_ProgressInfo pi;
1078   uint64_t off;
1079   size_t bs;
1080   size_t app;
1081   int i;
1082   struct ContentHashKey *chk;
1083
1084 #if DEBUG_DOWNLOAD
1085   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1086               "Received block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1087               GNUNET_h2s (key),
1088               dr->depth,
1089               (unsigned long long) dr->offset,
1090               (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1091               
1092 #endif
1093   bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (dc->uri->data.chk.file_length),
1094                                             dr->offset,
1095                                             dr->depth);
1096   if (prc->size != bs)
1097     {
1098       GNUNET_asprintf (&dc->emsg,
1099                        _("Internal error or bogus download URI (expected %u bytes at depth %u and offset %llu/%llu, got %u bytes)\n"),
1100                        bs,
1101                        dr->depth,
1102                        (unsigned long long) dr->offset,
1103                        (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1104                        prc->size);
1105       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1106                   "%s", 
1107                   dc->emsg);
1108       while (dr->parent != NULL)
1109         {
1110           dr->state = BRS_ERROR;
1111           dr = dr->parent;
1112         }
1113       dr->state = BRS_ERROR;
1114       goto signal_error;
1115     }
1116
1117   (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1118                                                &prc->query,
1119                                                dr);
1120   if (GNUNET_YES == dr->is_pending)
1121     {
1122       GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1123                                    dc->pending_tail,
1124                                    dr);
1125       dr->is_pending = GNUNET_NO;
1126     }
1127
1128
1129   GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1130   if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
1131                                        prc->size,
1132                                        &skey,
1133                                        &iv,
1134                                        pt))
1135     {
1136       GNUNET_break (0);
1137       dc->emsg = GNUNET_strdup (_("internal error decrypting content"));
1138       goto signal_error;
1139     }
1140   off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1141                              dr->offset,
1142                              dr->depth);
1143   /* save to disk */
1144   if ( ( GNUNET_YES == prc->do_store) &&
1145        ( (dc->filename != NULL) ||
1146          (is_recursive_download (dc)) ) &&
1147        ( (dr->depth == dc->treedepth) ||
1148          (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1149     {
1150       fh = GNUNET_DISK_file_open (dc->filename != NULL 
1151                                   ? dc->filename 
1152                                   : dc->temp_filename, 
1153                                   GNUNET_DISK_OPEN_READWRITE | 
1154                                   GNUNET_DISK_OPEN_CREATE,
1155                                   GNUNET_DISK_PERM_USER_READ |
1156                                   GNUNET_DISK_PERM_USER_WRITE |
1157                                   GNUNET_DISK_PERM_GROUP_READ |
1158                                   GNUNET_DISK_PERM_OTHER_READ);    
1159       if (NULL == fh)
1160         {
1161           GNUNET_asprintf (&dc->emsg,
1162                            _("Download failed: could not open file `%s': %s\n"),
1163                            dc->filename,
1164                            STRERROR (errno));
1165           goto signal_error;
1166         }      
1167 #if DEBUG_DOWNLOAD
1168       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169                   "Saving decrypted block to disk at offset %llu\n",
1170                   (unsigned long long) off);
1171 #endif
1172       if ( (off  != 
1173             GNUNET_DISK_file_seek (fh,
1174                                    off,
1175                                    GNUNET_DISK_SEEK_SET) ) )
1176         {
1177           GNUNET_asprintf (&dc->emsg,
1178                            _("Failed to seek to offset %llu in file `%s': %s\n"),
1179                            (unsigned long long) off,
1180                            dc->filename,
1181                            STRERROR (errno));
1182           goto signal_error;
1183         }
1184       if (prc->size !=
1185           GNUNET_DISK_file_write (fh,
1186                                   pt,
1187                                   prc->size))
1188         {
1189           GNUNET_asprintf (&dc->emsg,
1190                            _("Failed to write block of %u bytes at offset %llu in file `%s': %s\n"),
1191                            (unsigned int) prc->size,
1192                            (unsigned long long) off,
1193                            dc->filename,
1194                            STRERROR (errno));
1195           goto signal_error;
1196         }
1197       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1198       fh = NULL;
1199     }
1200
1201   if (dr->depth == 0) 
1202     {
1203       /* DBLOCK, update progress and try recursion if applicable */
1204       app = prc->size;
1205       if (dr->offset < dc->offset)
1206         {
1207           /* starting offset begins in the middle of pt,
1208              do not count first bytes as progress */
1209           GNUNET_assert (app > (dc->offset - dr->offset));
1210           app -= (dc->offset - dr->offset);       
1211         }
1212       if (dr->offset + prc->size > dc->offset + dc->length)
1213         {
1214           /* end of block is after relevant range,
1215              do not count last bytes as progress */
1216           GNUNET_assert (app > (dr->offset + prc->size) - (dc->offset + dc->length));
1217           app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1218         }
1219       dc->completed += app;
1220
1221       /* do recursive download if option is set and either meta data
1222          says it is a directory or if no meta data is given AND filename 
1223          ends in '.gnd' (top-level case) */
1224       if (is_recursive_download (dc))
1225         GNUNET_FS_directory_list_contents (prc->size,
1226                                            pt,
1227                                            off,
1228                                            &trigger_recursive_download,
1229                                            dc);         
1230             
1231     }
1232   dr->state = BRS_DOWNLOAD_DOWN;
1233   pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1234   pi.value.download.specifics.progress.data = pt;
1235   pi.value.download.specifics.progress.offset = dr->offset;
1236   pi.value.download.specifics.progress.data_len = prc->size;
1237   pi.value.download.specifics.progress.depth = dr->depth;
1238   GNUNET_FS_download_make_status_ (&pi, dc);
1239   GNUNET_assert (dc->completed <= dc->length);
1240   if (dr->depth == 0) 
1241     propagate_up (dr);
1242
1243   if (dc->completed == dc->length)
1244     {
1245       /* download completed, signal */
1246 #if DEBUG_DOWNLOAD
1247       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1248                   "Download completed, truncating file to desired length %llu\n",
1249                   (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1250 #endif
1251       /* truncate file to size (since we store IBlocks at the end) */
1252       if (dc->filename != NULL)
1253         {
1254           if (0 != truncate (dc->filename,
1255                              GNUNET_ntohll (dc->uri->data.chk.file_length)))
1256             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1257                                       "truncate",
1258                                       dc->filename);
1259         }
1260       GNUNET_assert (dr->depth == 0);
1261       check_completed (dc);
1262     }
1263   if (dr->depth == 0) 
1264     {
1265       /* bottom of the tree, no child downloads possible, just sync */
1266       GNUNET_FS_download_sync_ (dc);
1267       return GNUNET_YES;
1268     }
1269
1270 #if DEBUG_DOWNLOAD
1271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1272               "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1273               dr->depth,
1274               (unsigned long long) dr->offset);
1275 #endif
1276   GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1277   chk = (struct ContentHashKey*) pt;
1278   for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
1279     {
1280       off = compute_dblock_offset (dr->offset,
1281                                    dr->depth,
1282                                    i);
1283       drc = dr->children[i];
1284       switch (drc->state)
1285         {
1286         case BRS_INIT:
1287           drc->chk = chk[i];
1288           drc->state = BRS_CHK_SET;
1289           schedule_block_download (dc, drc);
1290           break;
1291         case BRS_RECONSTRUCT_DOWN:
1292           GNUNET_assert (0);
1293           break;
1294         case BRS_RECONSTRUCT_META_UP:
1295           GNUNET_assert (0);
1296           break;
1297         case BRS_RECONSTRUCT_UP:
1298           GNUNET_assert (0);
1299           break;
1300         case BRS_CHK_SET:
1301           GNUNET_assert (0);
1302           break;
1303         case BRS_DOWNLOAD_DOWN:
1304           GNUNET_assert (0);
1305           break;
1306         case BRS_DOWNLOAD_UP:
1307           GNUNET_assert (0);
1308           break;
1309         case BRS_ERROR:
1310           GNUNET_assert (0);
1311           break;
1312         default:
1313           GNUNET_assert (0);
1314           break;
1315         }
1316     }
1317   GNUNET_FS_download_sync_ (dc);
1318   return GNUNET_YES;
1319
1320  signal_error:
1321   if (fh != NULL)
1322     GNUNET_DISK_file_close (fh);
1323   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1324   pi.value.download.specifics.error.message = dc->emsg;
1325   GNUNET_FS_download_make_status_ (&pi, dc);
1326   /* abort all pending requests */
1327   if (NULL != dc->th)
1328     {
1329       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1330       dc->th = NULL;
1331     }
1332   GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1333   dc->client = NULL;
1334   GNUNET_FS_free_download_request_ (dc->top_request);
1335   dc->top_request = NULL;
1336   GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1337   dc->active = NULL;
1338   dc->pending_head = NULL;
1339   dc->pending_tail = NULL;
1340   GNUNET_FS_download_sync_ (dc);
1341   return GNUNET_NO;
1342 }
1343
1344
1345 /**
1346  * Process a download result.
1347  *
1348  * @param dc our download context
1349  * @param type type of the result
1350  * @param data the (encrypted) response
1351  * @param size size of data
1352  */
1353 static void
1354 process_result (struct GNUNET_FS_DownloadContext *dc,
1355                 enum GNUNET_BLOCK_Type type,
1356                 const void *data,
1357                 size_t size)
1358 {
1359   struct ProcessResultClosure prc;
1360
1361   prc.dc = dc;
1362   prc.data = data;
1363   prc.size = size;
1364   prc.type = type;
1365   prc.do_store = GNUNET_YES;
1366   GNUNET_CRYPTO_hash (data, size, &prc.query);
1367 #if DEBUG_DOWNLOAD
1368   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1369               "Received result for query `%s' from `%s'-service\n",
1370               GNUNET_h2s (&prc.query),
1371               "FS");
1372 #endif
1373   GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1374                                               &prc.query,
1375                                               &process_result_with_request,
1376                                               &prc);
1377 }
1378
1379
1380 /**
1381  * Type of a function to call when we receive a message
1382  * from the service.
1383  *
1384  * @param cls closure
1385  * @param msg message received, NULL on timeout or fatal error
1386  */
1387 static void 
1388 receive_results (void *cls,
1389                  const struct GNUNET_MessageHeader * msg)
1390 {
1391   struct GNUNET_FS_DownloadContext *dc = cls;
1392   const struct PutMessage *cm;
1393   uint16_t msize;
1394
1395   if ( (NULL == msg) ||
1396        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
1397        (sizeof (struct PutMessage) > ntohs(msg->size)) )
1398     {
1399       GNUNET_break (msg == NULL);       
1400       try_reconnect (dc);
1401       return;
1402     }
1403   msize = ntohs(msg->size);
1404   cm = (const struct PutMessage*) msg;
1405   process_result (dc, 
1406                   ntohl (cm->type),
1407                   &cm[1],
1408                   msize - sizeof (struct PutMessage));
1409   if (dc->client == NULL)
1410     return; /* fatal error */
1411   /* continue receiving */
1412   GNUNET_CLIENT_receive (dc->client,
1413                          &receive_results,
1414                          dc,
1415                          GNUNET_TIME_UNIT_FOREVER_REL);
1416 }
1417
1418
1419
1420 /**
1421  * We're ready to transmit a search request to the
1422  * file-sharing service.  Do it.  If there is 
1423  * more than one request pending, try to send 
1424  * multiple or request another transmission.
1425  *
1426  * @param cls closure
1427  * @param size number of bytes available in buf
1428  * @param buf where the callee should write the message
1429  * @return number of bytes written to buf
1430  */
1431 static size_t
1432 transmit_download_request (void *cls,
1433                            size_t size, 
1434                            void *buf)
1435 {
1436   struct GNUNET_FS_DownloadContext *dc = cls;
1437   size_t msize;
1438   struct SearchMessage *sm;
1439   struct DownloadRequest *dr;
1440
1441   dc->th = NULL;
1442   if (NULL == buf)
1443     {
1444 #if DEBUG_DOWNLOAD
1445       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1446                   "Transmitting download request failed, trying to reconnect\n");
1447 #endif
1448       try_reconnect (dc);
1449       return 0;
1450     }
1451   GNUNET_assert (size >= sizeof (struct SearchMessage));
1452   msize = 0;
1453   sm = buf;
1454   while ( (NULL != (dr = dc->pending_head)) &&
1455           (size >= msize + sizeof (struct SearchMessage)) )
1456     {
1457 #if DEBUG_DOWNLOAD
1458       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459                   "Transmitting download request for `%s' to `%s'-service\n",
1460                   GNUNET_h2s (&dr->chk.query),
1461                   "FS");
1462 #endif
1463       memset (sm, 0, sizeof (struct SearchMessage));
1464       sm->header.size = htons (sizeof (struct SearchMessage));
1465       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1466       if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
1467         sm->options = htonl (1);
1468       else
1469         sm->options = htonl (0);      
1470       if (dr->depth == 0)
1471         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
1472       else
1473         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
1474       sm->anonymity_level = htonl (dc->anonymity);
1475       sm->target = dc->target.hashPubKey;
1476       sm->query = dr->chk.query;
1477       GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1478                                    dc->pending_tail,
1479                                    dr);
1480       dr->is_pending = GNUNET_NO;
1481       msize += sizeof (struct SearchMessage);
1482       sm++;
1483     }
1484   if (dc->pending_head != NULL)
1485     {
1486       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1487                                                     sizeof (struct SearchMessage),
1488                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1489                                                     GNUNET_NO,
1490                                                     &transmit_download_request,
1491                                                     dc); 
1492       GNUNET_assert (dc->th != NULL);
1493     }
1494   return msize;
1495 }
1496
1497
1498 /**
1499  * Reconnect to the FS service and transmit our queries NOW.
1500  *
1501  * @param cls our download context
1502  * @param tc unused
1503  */
1504 static void
1505 do_reconnect (void *cls,
1506               const struct GNUNET_SCHEDULER_TaskContext *tc)
1507 {
1508   struct GNUNET_FS_DownloadContext *dc = cls;
1509   struct GNUNET_CLIENT_Connection *client;
1510   
1511   dc->task = GNUNET_SCHEDULER_NO_TASK;
1512   client = GNUNET_CLIENT_connect ("fs",
1513                                   dc->h->cfg);
1514   if (NULL == client)
1515     {
1516       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1517                   "Connecting to `%s'-service failed, will try again.\n",
1518                   "FS");
1519       try_reconnect (dc);
1520       return;
1521     }
1522   dc->client = client;
1523   if (dc->pending_head != NULL)
1524     {
1525       dc->th = GNUNET_CLIENT_notify_transmit_ready (client,
1526                                                     sizeof (struct SearchMessage),
1527                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1528                                                     GNUNET_NO,
1529                                                     &transmit_download_request,
1530                                                     dc);
1531       GNUNET_assert (dc->th != NULL);
1532     }
1533   GNUNET_CLIENT_receive (client,
1534                          &receive_results,
1535                          dc,
1536                          GNUNET_TIME_UNIT_FOREVER_REL);
1537 }
1538
1539
1540 /**
1541  * Add entries to the pending list.
1542  *
1543  * @param cls our download context
1544  * @param key unused
1545  * @param entry entry of type "struct DownloadRequest"
1546  * @return GNUNET_OK
1547  */
1548 static int
1549 retry_entry (void *cls,
1550              const GNUNET_HashCode *key,
1551              void *entry)
1552 {
1553   struct GNUNET_FS_DownloadContext *dc = cls;
1554   struct DownloadRequest *dr = entry;
1555
1556   dr->next = NULL;
1557   dr->prev = NULL;
1558   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
1559                                dc->pending_tail,
1560                                dr);
1561   dr->is_pending = GNUNET_YES;
1562   return GNUNET_OK;
1563 }
1564
1565
1566 /**
1567  * We've lost our connection with the FS service.
1568  * Re-establish it and re-transmit all of our
1569  * pending requests.
1570  *
1571  * @param dc download context that is having trouble
1572  */
1573 static void
1574 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1575 {
1576   
1577   if (NULL != dc->client)
1578     {
1579 #if DEBUG_DOWNLOAD
1580       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1581                   "Moving all requests back to pending list\n");
1582 #endif
1583       if (NULL != dc->th)
1584         {
1585           GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1586           dc->th = NULL;
1587         }
1588       /* full reset of the pending list */
1589       dc->pending_head = NULL;
1590       dc->pending_tail = NULL;
1591       GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1592                                              &retry_entry,
1593                                              dc);
1594       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1595       dc->client = NULL;
1596     }
1597 #if DEBUG_DOWNLOAD
1598   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1599               "Will try to reconnect in 1s\n");
1600 #endif
1601   dc->task
1602     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1603                                     &do_reconnect,
1604                                     dc);
1605 }
1606
1607
1608 /**
1609  * We're allowed to ask the FS service for our blocks.  Start the download.
1610  *
1611  * @param cls the 'struct GNUNET_FS_DownloadContext'
1612  * @param client handle to use for communcation with FS (we must destroy it!)
1613  */
1614 static void
1615 activate_fs_download (void *cls,
1616                       struct GNUNET_CLIENT_Connection *client)
1617 {
1618   struct GNUNET_FS_DownloadContext *dc = cls;
1619   struct GNUNET_FS_ProgressInfo pi;
1620
1621 #if DEBUG_DOWNLOAD
1622   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1623               "Download activated\n");
1624 #endif
1625   GNUNET_assert (NULL != client);
1626   GNUNET_assert (dc->client == NULL);
1627   GNUNET_assert (dc->th == NULL);
1628   dc->client = client;
1629   GNUNET_CLIENT_receive (client,
1630                          &receive_results,
1631                          dc,
1632                          GNUNET_TIME_UNIT_FOREVER_REL);
1633   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1634   GNUNET_FS_download_make_status_ (&pi, dc);
1635   GNUNET_assert (dc->pending_head == NULL);
1636   GNUNET_assert (dc->pending_tail == NULL);
1637   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1638                                          &retry_entry,
1639                                          dc);
1640 #if DEBUG_DOWNLOAD
1641   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1642               "Asking for transmission to FS service\n");
1643 #endif
1644   if (dc->pending_head != NULL)
1645     {
1646       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1647                                                     sizeof (struct SearchMessage),
1648                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1649                                                     GNUNET_NO,
1650                                                     &transmit_download_request,
1651                                                     dc);    
1652       GNUNET_assert (dc->th != NULL);
1653     }
1654 }
1655
1656
1657 /**
1658  * We must stop to ask the FS service for our blocks.  Pause the download.
1659  *
1660  * @param cls the 'struct GNUNET_FS_DownloadContext'
1661  */
1662 static void
1663 deactivate_fs_download (void *cls)
1664 {
1665   struct GNUNET_FS_DownloadContext *dc = cls;
1666   struct GNUNET_FS_ProgressInfo pi;
1667
1668 #if DEBUG_DOWNLOAD
1669   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1670               "Download deactivated\n");
1671 #endif  
1672   if (NULL != dc->th)
1673     {
1674       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1675       dc->th = NULL;
1676     }
1677   if (NULL != dc->client)
1678     {
1679       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1680       dc->client = NULL;
1681     }
1682   dc->pending_head = NULL;
1683   dc->pending_tail = NULL;
1684   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1685   GNUNET_FS_download_make_status_ (&pi, dc);
1686 }
1687
1688
1689 /**
1690  * (recursively) Create a download request structure.
1691  *
1692  * @param parent parent of the current entry
1693  * @param depth depth of the current entry, 0 are the DBLOCKs,
1694  *              top level block is 'dc->treedepth - 1'
1695  * @param dr_offset offset in the original file this block maps to
1696  *              (as in, offset of the first byte of the first DBLOCK 
1697  *               in the subtree rooted in the returned download request tree)
1698  * @param file_start_offset desired starting offset for the download
1699  *             in the original file; requesting tree should not contain
1700  *             DBLOCKs prior to the file_start_offset
1701  * @param desired_length desired number of bytes the user wanted to access
1702  *        (from file_start_offset).  Resulting tree should not contain
1703  *        DBLOCKs after file_start_offset + file_length.
1704  * @return download request tree for the given range of DBLOCKs at
1705  *         the specified depth
1706  */
1707 static struct DownloadRequest *
1708 create_download_request (struct DownloadRequest *parent,
1709                          unsigned int depth,
1710                          uint64_t dr_offset,
1711                          uint64_t file_start_offset,
1712                          uint64_t desired_length)
1713 {
1714   struct DownloadRequest *dr;
1715   unsigned int i;
1716   unsigned int head_skip;
1717   uint64_t child_block_size;
1718   
1719   dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1720   dr->parent = parent;
1721   dr->depth = depth;
1722   dr->offset = dr_offset;
1723   if (depth > 0)
1724     {
1725       child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1726       
1727       /* calculate how many blocks at this level are not interesting
1728          from the start (rounded down), either because of the requested
1729          file offset or because this IBlock is further along */
1730       if (dr_offset < file_start_offset)
1731         head_skip = file_start_offset / child_block_size;       
1732       else
1733         head_skip = dr_offset / child_block_size;       
1734
1735       /* calculate index of last block at this level that is interesting (rounded up) */
1736       dr->num_children = file_start_offset + desired_length / child_block_size;
1737       if (dr->num_children * child_block_size < file_start_offset + desired_length)
1738         dr->num_children++; /* round up */
1739
1740       /* now we can get the total number of children for this block */
1741       dr->num_children -= head_skip;
1742       if (dr->num_children > CHK_PER_INODE)
1743         dr->num_children = CHK_PER_INODE; /* cap at max */
1744
1745       /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1746       GNUNET_assert (dr->num_children > 0); 
1747
1748       dr->children = GNUNET_malloc (dr->num_children * 
1749                                     sizeof (struct DownloadRequest *));
1750       for (i=0;i<dr->num_children;i++)
1751         dr->children[i] = create_download_request (dr,
1752                                                    depth - 1,
1753                                                    dr_offset + i * child_block_size,
1754                                                    file_start_offset,
1755                                                    desired_length);      
1756     }
1757   return dr;
1758 }
1759
1760
1761 /**
1762  * Continuation after a possible attempt to reconstruct
1763  * the current IBlock from the existing file.
1764  *
1765  * @param cls the 'struct ReconstructContext'
1766  * @param tc scheduler context
1767  */
1768 static void
1769 reconstruct_cont (void *cls,
1770                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1771 {
1772   struct GNUNET_FS_DownloadContext *dc = cls;
1773
1774   /* clean up state from tree encoder */
1775   if (dc->te != NULL)
1776     {
1777       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
1778       dc->te = NULL;
1779     }
1780   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
1781     {
1782       GNUNET_SCHEDULER_cancel (dc->task);
1783       dc->task = GNUNET_SCHEDULER_NO_TASK;
1784     }
1785   if (dc->rfh != NULL)
1786     {
1787       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1788       dc->rfh = NULL;
1789     }
1790   /* start "normal" download */
1791   schedule_block_download (dc, 
1792                            dc->top_request);
1793 }
1794
1795
1796 /**
1797  * Task requesting the next block from the tree encoder.
1798  *
1799  * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1800  * @param tc task context
1801  */
1802 static void
1803 get_next_block (void *cls,
1804                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1805 {
1806   struct GNUNET_FS_DownloadContext *dc = cls;
1807
1808   dc->task = GNUNET_SCHEDULER_NO_TASK;
1809   GNUNET_FS_tree_encoder_next (dc->te);
1810 }
1811
1812
1813
1814 /**
1815  * Function called asking for the current (encoded)
1816  * block to be processed.  After processing the
1817  * client should either call "GNUNET_FS_tree_encode_next"
1818  * or (on error) "GNUNET_FS_tree_encode_finish".
1819  *
1820  * This function checks if the content on disk matches
1821  * the expected content based on the URI.
1822  * 
1823  * @param cls closure
1824  * @param chk content hash key for the block
1825  * @param offset offset of the block
1826  * @param depth depth of the block, 0 for DBLOCK
1827  * @param type type of the block (IBLOCK or DBLOCK)
1828  * @param block the (encrypted) block
1829  * @param block_size size of block (in bytes)
1830  */
1831 static void 
1832 reconstruct_cb (void *cls,
1833                 const struct ContentHashKey *chk,
1834                 uint64_t offset,
1835                 unsigned int depth,
1836                 enum GNUNET_BLOCK_Type type,
1837                 const void *block,
1838                 uint16_t block_size)
1839 {
1840   struct GNUNET_FS_DownloadContext *dc = cls;
1841   struct GNUNET_FS_ProgressInfo pi;
1842   struct DownloadRequest *dr;
1843   uint64_t blen;
1844   unsigned int chld;
1845
1846   /* find corresponding request entry */
1847   dr = dc->top_request;
1848   while (dr->depth > depth)
1849     {
1850       blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1851       chld = (offset - dr->offset) / blen;
1852       GNUNET_assert (chld < dr->num_children);
1853       dr = dr->children[chld];
1854     }
1855   switch (dr->state)
1856     {
1857     case BRS_INIT:
1858       break;
1859     case BRS_RECONSTRUCT_DOWN:
1860       break;
1861     case BRS_RECONSTRUCT_META_UP:
1862       break;
1863     case BRS_RECONSTRUCT_UP:
1864       break;
1865     case BRS_CHK_SET:
1866       if (0 == memcmp (chk,
1867                        &dr->chk,
1868                        sizeof (struct ContentHashKey)))
1869         {
1870           /* block matches, hence tree below matches; 
1871              this request is done! */
1872           dr->state = BRS_DOWNLOAD_UP;
1873           /* calculate how many bytes of payload this block
1874              corresponds to */
1875           blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1876           /* how many of those bytes are in the requested range? */
1877           blen = GNUNET_MIN (blen,
1878                              dc->length + dc->offset - dr->offset);
1879           /* signal progress */
1880           dc->completed += blen;
1881           pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1882           pi.value.download.specifics.progress.data = NULL;
1883           pi.value.download.specifics.progress.offset = offset;
1884           pi.value.download.specifics.progress.data_len = 0;
1885           pi.value.download.specifics.progress.depth = 0;
1886           GNUNET_FS_download_make_status_ (&pi, dc);             
1887         }
1888       else
1889         {
1890         }
1891       break; 
1892     case BRS_DOWNLOAD_DOWN:
1893       break; 
1894     case BRS_DOWNLOAD_UP:
1895       break;
1896     case BRS_ERROR:
1897       break;
1898     default:
1899       GNUNET_assert (0);
1900       break;
1901     }
1902   if ( (dr == dc->top_request) &&
1903        (dr->state == BRS_DOWNLOAD_UP) )
1904     {
1905       check_completed (dc);
1906       return;
1907     }
1908   dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
1909                                        dc);
1910 }
1911
1912
1913 /**
1914  * Function called by the tree encoder to obtain a block of plaintext
1915  * data (for the lowest level of the tree).
1916  *
1917  * @param cls our 'struct ReconstructContext'
1918  * @param offset identifies which block to get
1919  * @param max (maximum) number of bytes to get; returning
1920  *        fewer will also cause errors
1921  * @param buf where to copy the plaintext buffer
1922  * @param emsg location to store an error message (on error)
1923  * @return number of bytes copied to buf, 0 on error
1924  */
1925 static size_t
1926 fh_reader (void *cls,
1927            uint64_t offset,
1928            size_t max, 
1929            void *buf,
1930            char **emsg)
1931 {
1932   struct GNUNET_FS_DownloadContext *dc = cls;
1933   struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1934   ssize_t ret;
1935
1936   *emsg = NULL;
1937   if (offset !=
1938       GNUNET_DISK_file_seek (fh,
1939                              offset,
1940                              GNUNET_DISK_SEEK_SET))
1941     {
1942       *emsg = GNUNET_strdup (strerror (errno));
1943       return 0;
1944     }
1945   ret = GNUNET_DISK_file_read (fh, buf, max);
1946   if (ret < 0)
1947     {
1948       *emsg = GNUNET_strdup (strerror (errno));
1949       return 0;
1950     }
1951   return ret;
1952 }
1953
1954
1955 /**
1956  * Task that creates the initial (top-level) download
1957  * request for the file.
1958  *
1959  * @param cls the 'struct GNUNET_FS_DownloadContext'
1960  * @param tc scheduler context
1961  */
1962 void
1963 GNUNET_FS_download_start_task_ (void *cls,
1964                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1965 {
1966   struct GNUNET_FS_DownloadContext *dc = cls;  
1967   struct GNUNET_FS_ProgressInfo pi;
1968   struct GNUNET_DISK_FileHandle *fh;
1969
1970 #if DEBUG_DOWNLOAD
1971   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1972               "Start task running...\n");
1973 #endif
1974   dc->task = GNUNET_SCHEDULER_NO_TASK;
1975   if (dc->length == 0)
1976     {
1977       /* no bytes required! */
1978       if (dc->filename != NULL) 
1979         {
1980           fh = GNUNET_DISK_file_open (dc->filename, 
1981                                       GNUNET_DISK_OPEN_READWRITE |
1982                                       GNUNET_DISK_OPEN_CREATE |
1983                                       ( (0 == GNUNET_FS_uri_chk_get_file_size (dc->uri)) 
1984                                         ? GNUNET_DISK_OPEN_TRUNCATE : 0),
1985                                       GNUNET_DISK_PERM_USER_READ |
1986                                       GNUNET_DISK_PERM_USER_WRITE |
1987                                       GNUNET_DISK_PERM_GROUP_READ |
1988                                       GNUNET_DISK_PERM_OTHER_READ);
1989           GNUNET_DISK_file_close (fh);
1990         }
1991       GNUNET_FS_download_sync_ (dc);
1992       check_completed (dc);
1993       return;
1994     }
1995   if (dc->emsg != NULL) 
1996     return;
1997   if (dc->top_request == NULL)
1998     {
1999       dc->top_request = create_download_request (NULL, dc->treedepth - 1, 0,
2000                                                  dc->offset, dc->length);
2001       dc->top_request->state = BRS_CHK_SET;
2002       dc->top_request->chk = (dc->uri->type == chk) 
2003         ? dc->uri->data.chk.chk
2004         : dc->uri->data.loc.fi.chk;
2005       /* signal start */
2006       GNUNET_FS_download_sync_ (dc);
2007       pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
2008       pi.value.download.specifics.start.meta = dc->meta;
2009       GNUNET_FS_download_make_status_ (&pi, dc);
2010     }
2011   GNUNET_FS_download_start_downloading_ (dc);
2012   /* attempt reconstruction from disk */
2013   if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))    
2014     dc->rfh = GNUNET_DISK_file_open (dc->filename,
2015                                      GNUNET_DISK_OPEN_READ,
2016                                      GNUNET_DISK_PERM_NONE);    
2017   if (dc->top_request->state == BRS_CHK_SET)
2018     {
2019       if (dc->rfh != NULL)
2020         {
2021           /* first, try top-down */
2022 #if DEBUG_DOWNLOAD
2023           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2024                       "Trying top-down reconstruction for `%s'\n",
2025                       dc->filename);
2026 #endif
2027           try_top_down_reconstruction (dc, dc->top_request);
2028           switch (dc->top_request->state)
2029             {
2030             case BRS_CHK_SET:
2031               break; /* normal */
2032             case BRS_DOWNLOAD_DOWN:
2033               break; /* normal, some blocks already down */
2034             case BRS_DOWNLOAD_UP:
2035               /* already done entirely, party! */
2036               if (dc->rfh != NULL)
2037                 {
2038                   /* avoid hanging on to file handle longer than 
2039                      necessary */
2040                   GNUNET_DISK_file_close (dc->rfh);
2041                   dc->rfh = NULL;
2042                 }
2043               return;      
2044             case BRS_ERROR:
2045               GNUNET_asprintf (&dc->emsg,
2046                                _("Invalid URI"));
2047               GNUNET_FS_download_sync_ (dc);
2048               pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
2049               pi.value.download.specifics.error.message = dc->emsg;
2050               GNUNET_FS_download_make_status_ (&pi, dc);
2051               return;
2052             default:
2053               GNUNET_assert (0);
2054               break;
2055             }
2056         }
2057     }
2058   /* attempt reconstruction from meta data */
2059   if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
2060        (NULL != dc->meta) )
2061     {
2062 #if DEBUG_DOWNLOAD
2063       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2064                   "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
2065                   (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
2066                   (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
2067 #endif
2068       GNUNET_CONTAINER_meta_data_iterate (dc->meta,
2069                                           &match_full_data,
2070                                           dc);
2071       if (dc->top_request->state == BRS_DOWNLOAD_UP)
2072         {
2073           if (dc->rfh != NULL)
2074             {
2075               /* avoid hanging on to file handle longer than 
2076                  necessary */
2077               GNUNET_DISK_file_close (dc->rfh);
2078               dc->rfh = NULL;
2079             }
2080           return; /* finished, status update was already done for us */
2081         }
2082     }
2083   if (dc->rfh != NULL)
2084     {
2085       /* finally, try bottom-up */
2086 #if DEBUG_DOWNLOAD
2087       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088                   "Trying bottom-up reconstruction of file `%s'\n",
2089                   dc->filename);
2090 #endif
2091       dc->te = GNUNET_FS_tree_encoder_create (dc->h,
2092                                               dc->old_file_size,
2093                                               dc,
2094                                               &fh_reader,
2095                                               &reconstruct_cb,
2096                                               NULL,
2097                                               &reconstruct_cont);     
2098       dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
2099                                            dc);
2100     }
2101   else
2102     {
2103       /* simple, top-level download */
2104         schedule_block_download (dc, 
2105                                  dc->top_request);
2106     }
2107   if (dc->top_request->state == BRS_DOWNLOAD_UP)
2108     check_completed (dc);
2109 }
2110
2111
2112 /**
2113  * Create SUSPEND event for the given download operation
2114  * and then clean up our state (without stop signal).
2115  *
2116  * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
2117  */
2118 void
2119 GNUNET_FS_download_signal_suspend_ (void *cls)
2120 {
2121   struct GNUNET_FS_DownloadContext *dc = cls;
2122   struct GNUNET_FS_ProgressInfo pi;
2123
2124   if (dc->top != NULL)
2125     GNUNET_FS_end_top (dc->h, dc->top);
2126   while (NULL != dc->child_head)
2127     GNUNET_FS_download_signal_suspend_ (dc->child_head);  
2128   if (dc->search != NULL)
2129     {
2130       dc->search->download = NULL;
2131       dc->search = NULL;
2132     }
2133   if (dc->job_queue != NULL)
2134     {
2135       GNUNET_FS_dequeue_ (dc->job_queue);
2136       dc->job_queue = NULL;
2137     }
2138   if (dc->parent != NULL)
2139     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2140                                  dc->parent->child_tail,
2141                                  dc);  
2142   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2143     {
2144       GNUNET_SCHEDULER_cancel (dc->task);
2145       dc->task = GNUNET_SCHEDULER_NO_TASK;
2146     }
2147   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
2148   GNUNET_FS_download_make_status_ (&pi, dc);
2149   if (dc->te != NULL)
2150     {
2151       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);       
2152       dc->te = NULL;
2153     }
2154   if (dc->rfh != NULL)
2155     {
2156       GNUNET_DISK_file_close (dc->rfh);
2157       dc->rfh = NULL;
2158     }
2159   GNUNET_FS_free_download_request_ (dc->top_request);
2160   if (dc->active != NULL)
2161     {
2162       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2163       dc->active = NULL;
2164     }
2165   GNUNET_free_non_null (dc->filename);
2166   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2167   GNUNET_FS_uri_destroy (dc->uri);
2168   GNUNET_free_non_null (dc->temp_filename);
2169   GNUNET_free_non_null (dc->serialization);
2170   GNUNET_free (dc);
2171 }
2172
2173
2174 /**
2175  * Download parts of a file.  Note that this will store
2176  * the blocks at the respective offset in the given file.  Also, the
2177  * download is still using the blocking of the underlying FS
2178  * encoding.  As a result, the download may *write* outside of the
2179  * given boundaries (if offset and length do not match the 32k FS
2180  * block boundaries). <p>
2181  *
2182  * This function should be used to focus a download towards a
2183  * particular portion of the file (optimization), not to strictly
2184  * limit the download to exactly those bytes.
2185  *
2186  * @param h handle to the file sharing subsystem
2187  * @param uri the URI of the file (determines what to download); CHK or LOC URI
2188  * @param meta known metadata for the file (can be NULL)
2189  * @param filename where to store the file, maybe NULL (then no file is
2190  *        created on disk and data must be grabbed from the callbacks)
2191  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2192  *        can be NULL (in which case we will pick a name if needed); the temporary file
2193  *        may already exist, in which case we will try to use the data that is there and
2194  *        if it is not what is desired, will overwrite it
2195  * @param offset at what offset should we start the download (typically 0)
2196  * @param length how many bytes should be downloaded starting at offset
2197  * @param anonymity anonymity level to use for the download
2198  * @param options various options
2199  * @param cctx initial value for the client context for this download
2200  * @param parent parent download to associate this download with (use NULL
2201  *        for top-level downloads; useful for manually-triggered recursive downloads)
2202  * @return context that can be used to control this download
2203  */
2204 struct GNUNET_FS_DownloadContext *
2205 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2206                           const struct GNUNET_FS_Uri *uri,
2207                           const struct GNUNET_CONTAINER_MetaData *meta,
2208                           const char *filename,
2209                           const char *tempname,
2210                           uint64_t offset,
2211                           uint64_t length,
2212                           uint32_t anonymity,
2213                           enum GNUNET_FS_DownloadOptions options,
2214                           void *cctx,
2215                           struct GNUNET_FS_DownloadContext *parent)
2216 {
2217   struct GNUNET_FS_DownloadContext *dc;
2218
2219   GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
2220                  GNUNET_FS_uri_test_loc (uri) );
2221                  
2222   if ( (offset + length < offset) ||
2223        (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
2224     {      
2225       GNUNET_break (0);
2226       return NULL;
2227     }
2228 #if DEBUG_DOWNLOAD
2229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2230               "Starting download `%s' of %llu bytes\n",
2231               filename,
2232               (unsigned long long) length);
2233 #endif
2234   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2235   dc->h = h;
2236   dc->parent = parent;
2237   if (parent != NULL)
2238     {
2239       GNUNET_CONTAINER_DLL_insert (parent->child_head,
2240                                    parent->child_tail,
2241                                    dc);
2242     }
2243   dc->uri = GNUNET_FS_uri_dup (uri);
2244   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2245   dc->client_info = cctx;
2246   dc->start_time = GNUNET_TIME_absolute_get ();
2247   if (NULL != filename)
2248     {
2249       dc->filename = GNUNET_strdup (filename);
2250       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2251         GNUNET_DISK_file_size (filename,
2252                                &dc->old_file_size,
2253                                GNUNET_YES);
2254     }
2255   if (GNUNET_FS_uri_test_loc (dc->uri))
2256     GNUNET_assert (GNUNET_OK ==
2257                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2258                                                         &dc->target));
2259   dc->offset = offset;
2260   dc->length = length;
2261   dc->anonymity = anonymity;
2262   dc->options = options;
2263   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2264   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
2265   if ( (filename == NULL) &&
2266        (is_recursive_download (dc) ) )
2267     {
2268       if (tempname != NULL)
2269         dc->temp_filename = GNUNET_strdup (tempname);
2270       else
2271         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2272     }
2273
2274 #if DEBUG_DOWNLOAD
2275   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2276               "Download tree has depth %u\n",
2277               dc->treedepth);
2278 #endif
2279   if (parent == NULL)
2280     {
2281       dc->top = GNUNET_FS_make_top (dc->h,
2282                                     &GNUNET_FS_download_signal_suspend_,
2283                                     dc);
2284     }
2285   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2286                                        dc);
2287   return dc;
2288 }
2289
2290
2291 /**
2292  * Download parts of a file based on a search result.  The download
2293  * will be associated with the search result (and the association
2294  * will be preserved when serializing/deserializing the state).
2295  * If the search is stopped, the download will not be aborted but
2296  * be 'promoted' to a stand-alone download.
2297  *
2298  * As with the other download function, this will store
2299  * the blocks at the respective offset in the given file.  Also, the
2300  * download is still using the blocking of the underlying FS
2301  * encoding.  As a result, the download may *write* outside of the
2302  * given boundaries (if offset and length do not match the 32k FS
2303  * block boundaries). <p>
2304  *
2305  * The given range can be used to focus a download towards a
2306  * particular portion of the file (optimization), not to strictly
2307  * limit the download to exactly those bytes.
2308  *
2309  * @param h handle to the file sharing subsystem
2310  * @param sr the search result to use for the download (determines uri and
2311  *        meta data and associations)
2312  * @param filename where to store the file, maybe NULL (then no file is
2313  *        created on disk and data must be grabbed from the callbacks)
2314  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2315  *        can be NULL (in which case we will pick a name if needed); the temporary file
2316  *        may already exist, in which case we will try to use the data that is there and
2317  *        if it is not what is desired, will overwrite it
2318  * @param offset at what offset should we start the download (typically 0)
2319  * @param length how many bytes should be downloaded starting at offset
2320  * @param anonymity anonymity level to use for the download
2321  * @param options various download options
2322  * @param cctx initial value for the client context for this download
2323  * @return context that can be used to control this download
2324  */
2325 struct GNUNET_FS_DownloadContext *
2326 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2327                                       struct GNUNET_FS_SearchResult *sr,
2328                                       const char *filename,
2329                                       const char *tempname,
2330                                       uint64_t offset,
2331                                       uint64_t length,
2332                                       uint32_t anonymity,
2333                                       enum GNUNET_FS_DownloadOptions options,
2334                                       void *cctx)
2335 {
2336   struct GNUNET_FS_DownloadContext *dc;
2337
2338   if ( (sr == NULL) ||
2339        (sr->download != NULL) )
2340     {
2341       GNUNET_break (0);
2342       return NULL;
2343     }
2344   GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
2345                  GNUNET_FS_uri_test_loc (sr->uri) );             
2346   if ( (offset + length < offset) ||
2347        (offset + length > sr->uri->data.chk.file_length) )
2348     {      
2349       GNUNET_break (0);
2350       return NULL;
2351     }
2352 #if DEBUG_DOWNLOAD
2353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2354               "Starting download `%s' of %llu bytes\n",
2355               filename,
2356               (unsigned long long) length);
2357 #endif
2358   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2359   dc->h = h;
2360   dc->search = sr;
2361   sr->download = dc;
2362   if (sr->probe_ctx != NULL)
2363     {
2364       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2365       sr->probe_ctx = NULL;      
2366     }
2367   dc->uri = GNUNET_FS_uri_dup (sr->uri);
2368   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
2369   dc->client_info = cctx;
2370   dc->start_time = GNUNET_TIME_absolute_get ();
2371   if (NULL != filename)
2372     {
2373       dc->filename = GNUNET_strdup (filename);
2374       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2375         GNUNET_DISK_file_size (filename,
2376                                &dc->old_file_size,
2377                                GNUNET_YES);
2378     }
2379   if (GNUNET_FS_uri_test_loc (dc->uri))
2380     GNUNET_assert (GNUNET_OK ==
2381                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2382                                                         &dc->target));
2383   dc->offset = offset;
2384   dc->length = length;
2385   dc->anonymity = anonymity;
2386   dc->options = options;
2387   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2388   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
2389   if ( (filename == NULL) &&
2390        (is_recursive_download (dc) ) )
2391     {
2392       if (tempname != NULL)
2393         dc->temp_filename = GNUNET_strdup (tempname);
2394       else
2395         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2396     }
2397
2398 #if DEBUG_DOWNLOAD
2399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2400               "Download tree has depth %u\n",
2401               dc->treedepth);
2402 #endif
2403   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2404                                        dc);
2405   return dc;  
2406 }
2407
2408
2409 /**
2410  * Start the downloading process (by entering the queue).
2411  *
2412  * @param dc our download context
2413  */
2414 void
2415 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2416 {
2417   if (dc->completed == dc->length)
2418     return;
2419   GNUNET_assert (dc->job_queue == NULL);
2420   dc->job_queue = GNUNET_FS_queue_ (dc->h, 
2421                                     &activate_fs_download,
2422                                     &deactivate_fs_download,
2423                                     dc,
2424                                     (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
2425 }
2426
2427
2428 /**
2429  * Stop a download (aborts if download is incomplete).
2430  *
2431  * @param dc handle for the download
2432  * @param do_delete delete files of incomplete downloads
2433  */
2434 void
2435 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
2436                          int do_delete)
2437 {
2438   struct GNUNET_FS_ProgressInfo pi;
2439   int have_children;
2440
2441   if (dc->top != NULL)
2442     GNUNET_FS_end_top (dc->h, dc->top);
2443
2444
2445   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2446     {
2447       GNUNET_SCHEDULER_cancel (dc->task);
2448       dc->task = GNUNET_SCHEDULER_NO_TASK;
2449     }
2450   if (dc->search != NULL)
2451     {
2452       dc->search->download = NULL;
2453       dc->search = NULL;
2454     }
2455   if (dc->job_queue != NULL)
2456     {
2457       GNUNET_FS_dequeue_ (dc->job_queue);
2458       dc->job_queue = NULL;
2459     }
2460   if (dc->te != NULL)
2461     {
2462       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2463       dc->te = NULL;
2464     }
2465   have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2466   while (NULL != dc->child_head)
2467     GNUNET_FS_download_stop (dc->child_head, 
2468                              do_delete);
2469   if (dc->parent != NULL)
2470     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2471                                  dc->parent->child_tail,
2472                                  dc);  
2473   if (dc->serialization != NULL)
2474     GNUNET_FS_remove_sync_file_ (dc->h,
2475                                  ( (dc->parent != NULL)  || (dc->search != NULL) )
2476                                  ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2477                                  : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD , 
2478                                  dc->serialization);
2479   if ( (GNUNET_YES == have_children) &&
2480        (dc->parent == NULL) )
2481     GNUNET_FS_remove_sync_dir_ (dc->h, 
2482                                 (dc->search != NULL) 
2483                                 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2484                                 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2485                                 dc->serialization);  
2486   pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2487   GNUNET_FS_download_make_status_ (&pi, dc);
2488   GNUNET_FS_free_download_request_ (dc->top_request);
2489   dc->top_request = NULL;
2490   if (dc->active != NULL)
2491     {
2492       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2493       dc->active = NULL;
2494     }
2495   if (dc->filename != NULL)
2496     {
2497       if ( (dc->completed != dc->length) &&
2498            (GNUNET_YES == do_delete) )
2499         {
2500           if (0 != UNLINK (dc->filename))
2501             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2502                                       "unlink",
2503                                       dc->filename);
2504         }
2505       GNUNET_free (dc->filename);
2506     }
2507   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2508   GNUNET_FS_uri_destroy (dc->uri);
2509   if (NULL != dc->temp_filename)
2510     {
2511       if (0 != UNLINK (dc->temp_filename))
2512         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2513                                   "unlink",
2514                                   dc->temp_filename);
2515       GNUNET_free (dc->temp_filename);
2516     }
2517   GNUNET_free_non_null (dc->serialization);
2518   GNUNET_free (dc);
2519 }
2520
2521 /* end of fs_download.c */