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