no includes on top level please
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file fs/fs_download.c
22  * @brief download methods
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - different priority for scheduling probe downloads?
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_fs_service.h"
31 #include "fs.h"
32 #include "fs_tree.h"
33
34 #define DEBUG_DOWNLOAD GNUNET_NO
35
36 /**
37  * Determine if the given download (options and meta data) should cause
38  * use to try to do a recursive download.
39  */
40 static int
41 is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
42 {
43   return  (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
44     ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
45       ( (dc->meta == NULL) &&
46         ( (NULL == dc->filename) ||            
47           ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
48             (NULL !=
49              strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
50                      GNUNET_FS_DIRECTORY_EXT)) ) ) ) );              
51 }
52
53
54 /**
55  * We're storing the IBLOCKS after the DBLOCKS on disk (so that we
56  * only have to truncate the file once we're done).
57  *
58  * Given the offset of a block (with respect to the DBLOCKS) and its
59  * depth, return the offset where we would store this block in the
60  * file.
61  * 
62  * @param fsize overall file size
63  * @param off offset of the block in the file
64  * @param depth depth of the block in the tree, 0 for DBLOCK
65  * @return off for DBLOCKS (depth == treedepth),
66  *         otherwise an offset past the end
67  *         of the file that does not overlap
68  *         with the range for any other block
69  */
70 static uint64_t
71 compute_disk_offset (uint64_t fsize,
72                      uint64_t off,
73                      unsigned int depth)
74 {
75   unsigned int i;
76   uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
77   uint64_t loff; /* where do IBlocks for depth "i" start? */
78   unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
79   
80   if (depth == 0)
81     return off;
82   /* first IBlocks start at the end of file, rounded up
83      to full DBLOCK_SIZE */
84   loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
85   lsize = ( (fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof (struct ContentHashKey);
86   GNUNET_assert (0 == (off % DBLOCK_SIZE));
87   ioff = (off / DBLOCK_SIZE);
88   for (i=1;i<depth;i++)
89     {
90       loff += lsize;
91       lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE;
92       GNUNET_assert (lsize > 0);
93       GNUNET_assert (0 == (ioff % CHK_PER_INODE));
94       ioff /= CHK_PER_INODE;
95     }
96   return loff + ioff * sizeof (struct ContentHashKey);
97 }
98
99
100 /**
101  * Given a block at the given offset and depth, calculate the offset
102  * for the CHK at the given index.
103  *
104  * @param offset the offset of the first
105  *        DBLOCK in the subtree of the 
106  *        identified IBLOCK
107  * @param depth the depth of the IBLOCK in the tree, 0 for DBLOCK
108  * @param k which CHK in the IBLOCK are we 
109  *        talking about
110  * @return offset if k=0, otherwise an appropriately
111  *         larger value (i.e., if depth = 1,
112  *         the returned value should be offset+k*DBLOCK_SIZE)
113  */
114 static uint64_t
115 compute_dblock_offset (uint64_t offset,
116                        unsigned int depth,
117                        unsigned int k)
118 {
119   unsigned int i;
120   uint64_t lsize; /* what is the size of the sum of all DBlocks 
121                      that a CHK at depth i corresponds to? */
122
123   if (depth == 0)
124     return offset;
125   lsize = DBLOCK_SIZE;
126   for (i=1;i<depth;i++)
127     lsize *= CHK_PER_INODE;
128   return offset + k * lsize;
129 }
130
131
132 /**
133  * Fill in all of the generic fields for a download event and call the
134  * callback.
135  *
136  * @param pi structure to fill in
137  * @param dc overall download context
138  */
139 void
140 GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
141                                  struct GNUNET_FS_DownloadContext *dc)
142 {
143   pi->value.download.dc = dc;
144   pi->value.download.cctx
145     = dc->client_info;
146   pi->value.download.pctx
147     = (dc->parent == NULL) ? NULL : dc->parent->client_info;
148   pi->value.download.sctx
149     = (dc->search == NULL) ? NULL : dc->search->client_info;
150   pi->value.download.uri 
151     = dc->uri;
152   pi->value.download.filename
153     = dc->filename;
154   pi->value.download.size
155     = dc->length;
156   pi->value.download.duration
157     = GNUNET_TIME_absolute_get_duration (dc->start_time);
158   pi->value.download.completed
159     = dc->completed;
160   pi->value.download.anonymity
161     = dc->anonymity;
162   pi->value.download.eta
163     = GNUNET_TIME_calculate_eta (dc->start_time,
164                                  dc->completed,
165                                  dc->length);
166   pi->value.download.is_active = (dc->client == NULL) ? GNUNET_NO : GNUNET_YES;
167   if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
168     dc->client_info = dc->h->upcb (dc->h->upcb_cls,
169                                    pi);
170   else
171     dc->client_info = GNUNET_FS_search_probe_progress_ (NULL,
172                                                         pi);
173 }
174
175
176 /**
177  * We're ready to transmit a search request to the
178  * file-sharing service.  Do it.  If there is 
179  * more than one request pending, try to send 
180  * multiple or request another transmission.
181  *
182  * @param cls closure
183  * @param size number of bytes available in buf
184  * @param buf where the callee should write the message
185  * @return number of bytes written to buf
186  */
187 static size_t
188 transmit_download_request (void *cls,
189                            size_t size, 
190                            void *buf);
191
192
193 /**
194  * Closure for iterator processing results.
195  */
196 struct ProcessResultClosure
197 {
198   
199   /**
200    * Hash of data.
201    */
202   GNUNET_HashCode query;
203
204   /**
205    * Data found in P2P network.
206    */ 
207   const void *data;
208
209   /**
210    * Our download context.
211    */
212   struct GNUNET_FS_DownloadContext *dc;
213                 
214   /**
215    * Number of bytes in data.
216    */
217   size_t size;
218
219   /**
220    * Type of data.
221    */
222   enum GNUNET_BLOCK_Type type;
223
224   /**
225    * Flag to indicate if this block should be stored on disk.
226    */
227   int do_store;
228   
229 };
230
231
232 /**
233  * Iterator over entries in the pending requests in the 'active' map for the
234  * reply that we just got.
235  *
236  * @param cls closure (our 'struct ProcessResultClosure')
237  * @param key query for the given value / request
238  * @param value value in the hash map (a 'struct DownloadRequest')
239  * @return GNUNET_YES (we should continue to iterate); unless serious error
240  */
241 static int
242 process_result_with_request (void *cls,
243                              const GNUNET_HashCode * key,
244                              void *value);
245
246
247 /**
248  * We've found a matching block without downloading it.
249  * Encrypt it and pass it to our "receive" function as
250  * if we had received it from the network.
251  * 
252  * @param dc download in question
253  * @param chk request this relates to
254  * @param dr request details
255  * @param block plaintext data matching request
256  * @param len number of bytes in block
257  * @param do_store should we still store the block on disk?
258  * @return GNUNET_OK on success
259  */
260 static int
261 encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
262                         const struct ContentHashKey *chk,
263                         struct DownloadRequest *dr,
264                         const char * block,                    
265                         size_t len,
266                         int do_store)
267 {
268   struct ProcessResultClosure prc;
269   char enc[len];
270   struct GNUNET_CRYPTO_AesSessionKey sk;
271   struct GNUNET_CRYPTO_AesInitializationVector iv;
272   GNUNET_HashCode query;
273   
274   GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
275   if (-1 == GNUNET_CRYPTO_aes_encrypt (block, len,
276                                        &sk,
277                                        &iv,
278                                        enc))
279     {
280       GNUNET_break (0);
281       return GNUNET_SYSERR;
282     }
283   GNUNET_CRYPTO_hash (enc, len, &query);
284   if (0 != memcmp (&query,
285                    &chk->query,
286                    sizeof (GNUNET_HashCode)))
287     {
288       GNUNET_break_op (0);
289       return GNUNET_SYSERR;
290     }
291 #if DEBUG_DOWNLOAD
292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293               "Matching block for `%s' at offset %llu already present, no need for download!\n",
294               dc->filename,
295               (unsigned long long) dr->offset);
296 #endif
297   /* already got it! */
298   prc.dc = dc;
299   prc.data = enc;
300   prc.size = len;
301   prc.type = (0 == dr->depth) 
302     ? GNUNET_BLOCK_TYPE_FS_DBLOCK 
303     : GNUNET_BLOCK_TYPE_FS_IBLOCK;
304   prc.query = chk->query;
305   prc.do_store = do_store;
306   process_result_with_request (&prc,
307                                &chk->key,
308                                dr);
309   return GNUNET_OK;
310 }
311
312
313 /**
314  * We've lost our connection with the FS service.
315  * Re-establish it and re-transmit all of our
316  * pending requests.
317  *
318  * @param dc download context that is having trouble
319  */
320 static void
321 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
322
323
324 /**
325  * We found an entry in a directory.  Check if the respective child
326  * already exists and if not create the respective child download.
327  *
328  * @param cls the parent download
329  * @param filename name of the file in the directory
330  * @param uri URI of the file (CHK or LOC)
331  * @param meta meta data of the file
332  * @param length number of bytes in data
333  * @param data contents of the file (or NULL if they were not inlined)
334  */
335 static void 
336 trigger_recursive_download (void *cls,
337                             const char *filename,
338                             const struct GNUNET_FS_Uri *uri,
339                             const struct GNUNET_CONTAINER_MetaData *meta,
340                             size_t length,
341                             const void *data);
342
343
344 /**
345  * We're done downloading a directory.  Open the file and
346  * trigger all of the (remaining) child downloads.
347  *
348  * @param dc context of download that just completed
349  */
350 static void
351 full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
352 {
353   size_t size;
354   uint64_t size64;
355   void *data;
356   struct GNUNET_DISK_FileHandle *h;
357   struct GNUNET_DISK_MapHandle *m;
358   
359   size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
360   size = (size_t) size64;
361   if (size64 != (uint64_t) size)
362     {
363       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364                   _("Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
365       return;
366     }
367   if (dc->filename != NULL)
368     {
369       h = GNUNET_DISK_file_open (dc->filename,
370                                  GNUNET_DISK_OPEN_READ,
371                                  GNUNET_DISK_PERM_NONE);
372     }
373   else
374     {
375       GNUNET_assert (dc->temp_filename != NULL);
376       h = GNUNET_DISK_file_open (dc->temp_filename,
377                                  GNUNET_DISK_OPEN_READ,
378                                  GNUNET_DISK_PERM_NONE);
379     }
380   if (h == NULL)
381     return; /* oops */
382   data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
383   if (data == NULL)
384     {
385       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386                   _("Directory too large for system address space\n"));
387     }
388   else
389     {
390       GNUNET_FS_directory_list_contents (size,
391                                          data,
392                                          0,
393                                          &trigger_recursive_download,
394                                          dc);         
395       GNUNET_DISK_file_unmap (m);
396     }
397   GNUNET_DISK_file_close (h);
398   if (dc->filename == NULL)
399     {
400       if (0 != UNLINK (dc->temp_filename))
401         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
402                                   "unlink",
403                                   dc->temp_filename);
404       GNUNET_free (dc->temp_filename);
405       dc->temp_filename = NULL;
406     }
407 }
408
409
410 /**
411  * Check if all child-downloads have completed (or trigger them if
412  * necessary) and once we're completely done, signal completion (and
413  * possibly recurse to parent).  This function MUST be called when the
414  * download of a file itself is done or when the download of a file is
415  * done and then later a direct child download has completed (and
416  * hence this download may complete itself).
417  *
418  * @param dc download to check for completion of children
419  */
420 static void
421 check_completed (struct GNUNET_FS_DownloadContext *dc)
422 {
423   struct GNUNET_FS_ProgressInfo pi;
424   struct GNUNET_FS_DownloadContext *pos;
425
426   /* first, check if we need to download children */
427   if ( (dc->child_head == NULL) &&
428        (is_recursive_download (dc)) )
429     full_recursive_download (dc);  
430   /* then, check if children are done already */
431   pos = dc->child_head;
432   while (pos != NULL)
433     {
434       if ( (pos->emsg == NULL) &&
435            (pos->completed < pos->length) )
436         return; /* not done yet */
437       if ( (pos->child_head != NULL) &&
438            (pos->has_finished != GNUNET_YES) )
439         return; /* not transitively done yet */
440       pos = pos->next;
441     }
442   /* All of our children are done, so mark this download done */
443   dc->has_finished = GNUNET_YES;
444   if (dc->job_queue != NULL)
445     {
446       GNUNET_FS_dequeue_ (dc->job_queue);
447       dc->job_queue = NULL;
448     }
449   GNUNET_FS_download_sync_ (dc);
450
451   /* signal completion */
452   pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
453   GNUNET_FS_download_make_status_ (&pi, dc);
454
455   /* let parent know */
456   if (dc->parent != NULL)
457     check_completed (dc->parent);  
458 }
459
460
461 /**
462  * We got a block of plaintext data (from the meta data).
463  * Try it for upward reconstruction of the data.  On success,
464  * the top-level block will move to state BRS_DOWNLOAD_UP.
465  *
466  * @param dc context for the download
467  * @param dr download request to match against
468  * @param data plaintext data, starting from the beginning of the file
469  * @param data_len number of bytes in data
470  */ 
471 static void
472 try_match_block (struct GNUNET_FS_DownloadContext *dc,
473                  struct DownloadRequest *dr,
474                  const char *data,
475                  size_t data_len)
476 {
477   struct GNUNET_FS_ProgressInfo pi;
478   unsigned int i;
479   char enc[DBLOCK_SIZE];
480   struct ContentHashKey chks[CHK_PER_INODE];
481   struct ContentHashKey in_chk;
482   struct GNUNET_CRYPTO_AesSessionKey sk;
483   struct GNUNET_CRYPTO_AesInitializationVector iv;
484   size_t dlen;
485   struct DownloadRequest *drc;
486   struct GNUNET_DISK_FileHandle *fh;
487   int complete;
488   const char *fn;
489   const char *odata;
490   size_t odata_len;
491   
492   odata = data;
493   odata_len = data_len;
494   if (BRS_DOWNLOAD_UP == dr->state)
495     return;
496   if (dr->depth > 0)
497     {
498       complete = GNUNET_YES;
499       for (i=0;i<dr->num_children;i++)
500         {
501           drc = dr->children[i];
502           try_match_block (dc,
503                            drc,
504                            data, data_len);
505           if (drc->state != BRS_RECONSTRUCT_META_UP)
506             complete = GNUNET_NO;
507           else
508             chks[i] = drc->chk;
509         }
510       if (GNUNET_YES != complete)
511         return;
512       data = (const char*) chks;
513       dlen = dr->num_children * sizeof (struct ContentHashKey);
514     }
515   else
516     {
517       if (dr->offset > data_len) 
518         return; /* oops */
519       dlen = GNUNET_MIN (data_len - dr->offset,
520                          DBLOCK_SIZE);
521     }
522   GNUNET_CRYPTO_hash (&data[dr->offset],
523                       dlen,
524                       &in_chk.key);
525   GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
526   if (-1 == GNUNET_CRYPTO_aes_encrypt (&data[dr->offset], dlen,
527                                        &sk,
528                                        &iv,
529                                        enc))
530     {
531       GNUNET_break (0);
532       return;
533     }
534   GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
535   switch (dr->state)
536     {
537     case BRS_INIT:
538       dr->chk = in_chk;
539       dr->state = BRS_RECONSTRUCT_META_UP;
540       break;
541     case BRS_CHK_SET:
542       if (0 != memcmp (&in_chk,
543                        &dr->chk,
544                        sizeof (struct ContentHashKey)))
545         {
546           /* other peer provided bogus meta data */
547           GNUNET_break_op (0);
548           break;
549         }
550       /* write block to disk */
551       fn = dc->filename != NULL 
552         ? dc->filename 
553         : dc->temp_filename;
554       fh = GNUNET_DISK_file_open (fn,
555                                   GNUNET_DISK_OPEN_READWRITE | 
556                                   GNUNET_DISK_OPEN_CREATE | 
557                                   GNUNET_DISK_OPEN_TRUNCATE,
558                                   GNUNET_DISK_PERM_USER_READ |
559                                   GNUNET_DISK_PERM_USER_WRITE |
560                                   GNUNET_DISK_PERM_GROUP_READ |
561                                   GNUNET_DISK_PERM_OTHER_READ);     
562       if (fh == NULL)
563         {
564           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
565                                     "open",
566                                     fn);
567           GNUNET_asprintf (&dc->emsg,
568                            _("Failed to open file `%s' for writing"),
569                            fn);
570           GNUNET_DISK_file_close (fh);
571           dr->state = BRS_ERROR;
572           pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
573           pi.value.download.specifics.error.message = dc->emsg;
574           GNUNET_FS_download_make_status_ (&pi, dc); 
575           return;
576         }
577       if (data_len != 
578           GNUNET_DISK_file_write (fh,
579                                   odata,
580                                   odata_len))
581         {
582           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
583                                     "write",
584                                     fn);              
585           GNUNET_asprintf (&dc->emsg,
586                            _("Failed to open file `%s' for writing"),
587                            fn);
588           GNUNET_DISK_file_close (fh);
589           dr->state = BRS_ERROR;
590           pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
591           pi.value.download.specifics.error.message = dc->emsg;
592           GNUNET_FS_download_make_status_ (&pi, dc);      
593           return;
594         }
595       GNUNET_DISK_file_close (fh);
596       /* signal success */      
597       dr->state = BRS_DOWNLOAD_UP;
598       dc->completed = dc->length;
599       GNUNET_FS_download_sync_ (dc);
600       pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
601       pi.value.download.specifics.progress.data = data;
602       pi.value.download.specifics.progress.offset = 0;
603       pi.value.download.specifics.progress.data_len = dlen;
604       pi.value.download.specifics.progress.depth = 0;
605       GNUNET_FS_download_make_status_ (&pi, dc);
606       if (0 != truncate (dc->filename,
607                          GNUNET_ntohll (dc->uri->data.chk.file_length)))
608         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
609                                   "truncate",
610                                   dc->filename);
611       check_completed (dc);      
612       break;
613     default:
614       /* how did we get here? */
615       GNUNET_break (0);
616       break;
617     }
618 }
619
620
621 /**
622  * Type of a function that libextractor calls for each
623  * meta data item found.  If we find full data meta data,
624  * call 'try_match_block' on it.
625  *
626  * @param cls our 'struct GNUNET_FS_DownloadContext*'
627  * @param plugin_name name of the plugin that produced this value;
628  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
629  *        used in the main libextractor library and yielding
630  *        meta data).
631  * @param type libextractor-type describing the meta data
632  * @param format basic format information about data 
633  * @param data_mime_type mime-type of data (not of the original file);
634  *        can be NULL (if mime-type is not known)
635  * @param data actual meta-data found
636  * @param data_len number of bytes in data
637  * @return 0 to continue extracting, 1 to abort
638  */ 
639 static int
640 match_full_data (void *cls,
641                  const char *plugin_name,
642                  enum EXTRACTOR_MetaType type,
643                  enum EXTRACTOR_MetaFormat format,
644                  const char *data_mime_type,
645                  const char *data,
646                  size_t data_len)
647 {
648   struct GNUNET_FS_DownloadContext *dc = cls;
649
650   if (type != EXTRACTOR_METATYPE_GNUNET_FULL_DATA) 
651     return 0;
652 #if DEBUG_DOWNLOAD
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654               "Found %u bytes of FD!\n",
655               (unsigned int) data_len);
656 #endif
657   if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
658     {
659       GNUNET_break_op (0);
660       return 1; /* bogus meta data */
661     }
662   try_match_block (dc,
663                    dc->top_request,
664                    data,
665                    data_len);
666   return 1;
667 }
668
669
670 /**
671  * Set the state of the given download request to
672  * BRS_DOWNLOAD_UP and propagate it up the tree.
673  *
674  * @param dr download request that is done
675  */
676 static void
677 propagate_up (struct DownloadRequest *dr)
678 {
679   unsigned int i;
680
681   do
682     {
683       dr->state = BRS_DOWNLOAD_UP;
684       dr = dr->parent;
685       if (dr == NULL)
686         break;
687       for (i=0;i<dr->num_children;i++)
688         if (dr->children[i]->state != BRS_DOWNLOAD_UP)
689           break;
690     }
691   while (i == dr->num_children);
692 }
693
694
695 /**
696  * Try top-down reconstruction.  Before, the given request node
697  * must have the state BRS_CHK_SET.  Afterwards, more nodes may
698  * have that state or advanced to BRS_DOWNLOAD_DOWN or even
699  * BRS_DOWNLOAD_UP.  It is also possible to get BRS_ERROR on the
700  * top level.
701  *
702  * @param dc overall download this block belongs to
703  * @param dr block to reconstruct
704  */
705 static void
706 try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
707                              struct DownloadRequest *dr)
708 {
709   uint64_t off;
710   char block[DBLOCK_SIZE];
711   GNUNET_HashCode key;
712   uint64_t total;
713   size_t len;
714   unsigned int i;
715   unsigned int chk_off;
716   struct DownloadRequest *drc;
717   uint64_t child_block_size;
718   const struct ContentHashKey *chks;
719   int up_done;
720   
721   GNUNET_assert (dc->rfh != NULL);
722   GNUNET_assert (dr->state == BRS_CHK_SET);
723   total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
724   GNUNET_assert (dr->depth < dc->treedepth);
725   len = GNUNET_FS_tree_calculate_block_size (total,
726                                              dr->offset,
727                                              dr->depth);
728   GNUNET_assert (len <= DBLOCK_SIZE);
729   off = compute_disk_offset (total,
730                              dr->offset,
731                              dr->depth);
732   if (dc->old_file_size < off + len) 
733     return; /* failure */
734   if (off  != 
735       GNUNET_DISK_file_seek (dc->rfh,
736                              off,
737                              GNUNET_DISK_SEEK_SET) ) 
738     {
739       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
740                                 "seek",
741                                 dc->filename);
742       return; /* failure */
743     }
744   if (len != 
745       GNUNET_DISK_file_read (dc->rfh,
746                              block,
747                              len)) 
748     {
749       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
750                                 "read",
751                                 dc->filename);
752       return; /* failure */
753     }
754   GNUNET_CRYPTO_hash (block, len, &key);
755   if (0 != memcmp (&key,
756                    &dr->chk.key,
757                    sizeof (GNUNET_HashCode)))
758     return; /* mismatch */
759   if (GNUNET_OK !=
760       encrypt_existing_match (dc,
761                               &dr->chk,
762                               dr,
763                               block,
764                               len,
765                               GNUNET_NO))
766     {
767       /* hash matches but encrypted block does not, really bad */
768       dr->state = BRS_ERROR;
769       /* propagate up */
770       while (dr->parent != NULL)
771         {
772           dr = dr->parent;
773           dr->state = BRS_ERROR;
774         }
775       return;
776     }
777   /* block matches */
778   dr->state = BRS_DOWNLOAD_DOWN;
779
780   /* set CHKs for children */
781   up_done = GNUNET_YES;
782   chks = (const struct ContentHashKey*) block;
783   for (i=0;i<dr->num_children;i++)
784     {
785       drc = dr->children[i];
786       GNUNET_assert (drc->offset >= dr->offset);
787       child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
788       GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
789       chk_off = (drc->offset - dr->offset) / child_block_size;
790       GNUNET_assert (drc->state == BRS_INIT);
791       drc->state = BRS_CHK_SET;
792       drc->chk = chks[chk_off];
793       try_top_down_reconstruction (dc, drc);
794       if (drc->state != BRS_DOWNLOAD_UP)
795         up_done = GNUNET_NO; /* children not all done */
796     } 
797   if (up_done == GNUNET_YES)
798     propagate_up (dr); /* children all done (or no children...) */
799 }
800
801
802 /**
803  * Schedule the download of the specified block in the tree.
804  *
805  * @param dc overall download this block belongs to
806  * @param dr request to schedule
807  */
808 static void
809 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
810                          struct DownloadRequest *dr)
811 {
812   unsigned int i;
813
814   switch (dr->state)
815     {
816     case BRS_INIT:
817       GNUNET_assert (0);
818       break;
819     case BRS_RECONSTRUCT_DOWN:
820       GNUNET_assert (0);
821       break;
822     case BRS_RECONSTRUCT_META_UP:
823       GNUNET_assert (0);
824       break;
825     case BRS_RECONSTRUCT_UP:
826       GNUNET_assert (0);
827       break;
828     case BRS_CHK_SET:
829       /* normal case, start download */
830       break;
831     case BRS_DOWNLOAD_DOWN:
832       for (i=0;i<dr->num_children;i++)
833         schedule_block_download (dc, dr->children[i]);
834       return;
835     case BRS_DOWNLOAD_UP:
836       /* We're done! */
837       return;
838     case BRS_ERROR:
839       GNUNET_break (0);
840       return;
841     }
842 #if DEBUG_DOWNLOAD
843   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844               "Scheduling download at offset %llu and depth %u for `%s'\n",
845               (unsigned long long) dr->offset,
846               dr->depth,
847               GNUNET_h2s (&dr->chk.query));
848 #endif
849   GNUNET_assert (GNUNET_NO ==
850                  GNUNET_CONTAINER_multihashmap_contains_value (dc->active,
851                                                                &dr->chk.query,
852                                                                dr));
853   GNUNET_CONTAINER_multihashmap_put (dc->active,
854                                      &dr->chk.query,
855                                      dr,
856                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
857   if (dc->client == NULL)
858     return; /* download not active */
859   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
860                                dc->pending_tail,
861                                dr);
862   dr->is_pending = GNUNET_YES;
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   dr->next = NULL;
1555   dr->prev = NULL;
1556   GNUNET_CONTAINER_DLL_insert (dc->pending_head,
1557                                dc->pending_tail,
1558                                dr);
1559   dr->is_pending = GNUNET_YES;
1560   return GNUNET_OK;
1561 }
1562
1563
1564 /**
1565  * We've lost our connection with the FS service.
1566  * Re-establish it and re-transmit all of our
1567  * pending requests.
1568  *
1569  * @param dc download context that is having trouble
1570  */
1571 static void
1572 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1573 {
1574   
1575   if (NULL != dc->client)
1576     {
1577 #if DEBUG_DOWNLOAD
1578       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1579                   "Moving all requests back to pending list\n");
1580 #endif
1581       if (NULL != dc->th)
1582         {
1583           GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1584           dc->th = NULL;
1585         }
1586       /* full reset of the pending list */
1587       dc->pending_head = NULL;
1588       dc->pending_tail = NULL;
1589       GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1590                                              &retry_entry,
1591                                              dc);
1592       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1593       dc->client = NULL;
1594     }
1595 #if DEBUG_DOWNLOAD
1596   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1597               "Will try to reconnect in 1s\n");
1598 #endif
1599   dc->task
1600     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1601                                     &do_reconnect,
1602                                     dc);
1603 }
1604
1605
1606 /**
1607  * We're allowed to ask the FS service for our blocks.  Start the download.
1608  *
1609  * @param cls the 'struct GNUNET_FS_DownloadContext'
1610  * @param client handle to use for communcation with FS (we must destroy it!)
1611  */
1612 static void
1613 activate_fs_download (void *cls,
1614                       struct GNUNET_CLIENT_Connection *client)
1615 {
1616   struct GNUNET_FS_DownloadContext *dc = cls;
1617   struct GNUNET_FS_ProgressInfo pi;
1618
1619 #if DEBUG_DOWNLOAD
1620   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1621               "Download activated\n");
1622 #endif
1623   GNUNET_assert (NULL != client);
1624   GNUNET_assert (dc->client == NULL);
1625   GNUNET_assert (dc->th == NULL);
1626   dc->client = client;
1627   GNUNET_CLIENT_receive (client,
1628                          &receive_results,
1629                          dc,
1630                          GNUNET_TIME_UNIT_FOREVER_REL);
1631   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1632   GNUNET_FS_download_make_status_ (&pi, dc);
1633   GNUNET_assert (dc->pending_head == NULL);
1634   GNUNET_assert (dc->pending_tail == NULL);
1635   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1636                                          &retry_entry,
1637                                          dc);
1638 #if DEBUG_DOWNLOAD
1639   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1640               "Asking for transmission to FS service\n");
1641 #endif
1642   if (dc->pending_head != NULL)
1643     {
1644       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1645                                                     sizeof (struct SearchMessage),
1646                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1647                                                     GNUNET_NO,
1648                                                     &transmit_download_request,
1649                                                     dc);    
1650       GNUNET_assert (dc->th != NULL);
1651     }
1652 }
1653
1654
1655 /**
1656  * We must stop to ask the FS service for our blocks.  Pause the download.
1657  *
1658  * @param cls the 'struct GNUNET_FS_DownloadContext'
1659  */
1660 static void
1661 deactivate_fs_download (void *cls)
1662 {
1663   struct GNUNET_FS_DownloadContext *dc = cls;
1664   struct GNUNET_FS_ProgressInfo pi;
1665
1666 #if DEBUG_DOWNLOAD
1667   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1668               "Download deactivated\n");
1669 #endif  
1670   if (NULL != dc->th)
1671     {
1672       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1673       dc->th = NULL;
1674     }
1675   if (NULL != dc->client)
1676     {
1677       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1678       dc->client = NULL;
1679     }
1680   dc->pending_head = NULL;
1681   dc->pending_tail = NULL;
1682   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1683   GNUNET_FS_download_make_status_ (&pi, dc);
1684 }
1685
1686
1687 /**
1688  * (recursively) Create a download request structure.
1689  *
1690  * @param parent parent of the current entry
1691  * @param depth depth of the current entry, 0 are the DBLOCKs,
1692  *              top level block is 'dc->treedepth - 1'
1693  * @param dr_offset offset in the original file this block maps to
1694  *              (as in, offset of the first byte of the first DBLOCK 
1695  *               in the subtree rooted in the returned download request tree)
1696  * @param file_start_offset desired starting offset for the download
1697  *             in the original file; requesting tree should not contain
1698  *             DBLOCKs prior to the file_start_offset
1699  * @param desired_length desired number of bytes the user wanted to access
1700  *        (from file_start_offset).  Resulting tree should not contain
1701  *        DBLOCKs after file_start_offset + file_length.
1702  * @return download request tree for the given range of DBLOCKs at
1703  *         the specified depth
1704  */
1705 static struct DownloadRequest *
1706 create_download_request (struct DownloadRequest *parent,
1707                          unsigned int depth,
1708                          uint64_t dr_offset,
1709                          uint64_t file_start_offset,
1710                          uint64_t desired_length)
1711 {
1712   struct DownloadRequest *dr;
1713   unsigned int i;
1714   unsigned int head_skip;
1715   uint64_t child_block_size;
1716   
1717   dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1718   dr->parent = parent;
1719   dr->depth = depth;
1720   dr->offset = dr_offset;
1721   if (depth > 0)
1722     {
1723       child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1724       
1725       /* calculate how many blocks at this level are not interesting
1726          from the start (rounded down), either because of the requested
1727          file offset or because this IBlock is further along */
1728       if (dr_offset < file_start_offset)
1729         head_skip = file_start_offset / child_block_size;       
1730       else
1731         head_skip = dr_offset / child_block_size;       
1732
1733       /* calculate index of last block at this level that is interesting (rounded up) */
1734       dr->num_children = file_start_offset + desired_length / child_block_size;
1735       if (dr->num_children * child_block_size < file_start_offset + desired_length)
1736         dr->num_children++; /* round up */
1737
1738       /* now we can get the total number of children for this block */
1739       dr->num_children -= head_skip;
1740       if (dr->num_children > CHK_PER_INODE)
1741         dr->num_children = CHK_PER_INODE; /* cap at max */
1742
1743       /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1744       GNUNET_assert (dr->num_children > 0); 
1745
1746       dr->children = GNUNET_malloc (dr->num_children * 
1747                                     sizeof (struct DownloadRequest *));
1748       for (i=0;i<dr->num_children;i++)
1749         dr->children[i] = create_download_request (dr,
1750                                                    depth - 1,
1751                                                    dr_offset + i * child_block_size,
1752                                                    file_start_offset,
1753                                                    desired_length);      
1754     }
1755   return dr;
1756 }
1757
1758
1759 /**
1760  * Continuation after a possible attempt to reconstruct
1761  * the current IBlock from the existing file.
1762  *
1763  * @param cls the 'struct ReconstructContext'
1764  * @param tc scheduler context
1765  */
1766 static void
1767 reconstruct_cont (void *cls,
1768                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1769 {
1770   struct GNUNET_FS_DownloadContext *dc = cls;
1771
1772   /* clean up state from tree encoder */
1773   if (dc->te != NULL)
1774     {
1775       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
1776       dc->te = NULL;
1777     }
1778   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
1779     {
1780       GNUNET_SCHEDULER_cancel (dc->task);
1781       dc->task = GNUNET_SCHEDULER_NO_TASK;
1782     }
1783   if (dc->rfh != NULL)
1784     {
1785       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1786       dc->rfh = NULL;
1787     }
1788   /* start "normal" download */
1789   schedule_block_download (dc, 
1790                            dc->top_request);
1791 }
1792
1793
1794 /**
1795  * Task requesting the next block from the tree encoder.
1796  *
1797  * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1798  * @param tc task context
1799  */
1800 static void
1801 get_next_block (void *cls,
1802                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1803 {
1804   struct GNUNET_FS_DownloadContext *dc = cls;
1805
1806   dc->task = GNUNET_SCHEDULER_NO_TASK;
1807   GNUNET_FS_tree_encoder_next (dc->te);
1808 }
1809
1810
1811
1812 /**
1813  * Function called asking for the current (encoded)
1814  * block to be processed.  After processing the
1815  * client should either call "GNUNET_FS_tree_encode_next"
1816  * or (on error) "GNUNET_FS_tree_encode_finish".
1817  *
1818  * This function checks if the content on disk matches
1819  * the expected content based on the URI.
1820  * 
1821  * @param cls closure
1822  * @param chk content hash key for the block
1823  * @param offset offset of the block
1824  * @param depth depth of the block, 0 for DBLOCK
1825  * @param type type of the block (IBLOCK or DBLOCK)
1826  * @param block the (encrypted) block
1827  * @param block_size size of block (in bytes)
1828  */
1829 static void 
1830 reconstruct_cb (void *cls,
1831                 const struct ContentHashKey *chk,
1832                 uint64_t offset,
1833                 unsigned int depth,
1834                 enum GNUNET_BLOCK_Type type,
1835                 const void *block,
1836                 uint16_t block_size)
1837 {
1838   struct GNUNET_FS_DownloadContext *dc = cls;
1839   struct GNUNET_FS_ProgressInfo pi;
1840   struct DownloadRequest *dr;
1841   uint64_t blen;
1842   unsigned int chld;
1843
1844   /* find corresponding request entry */
1845   dr = dc->top_request;
1846   while (dr->depth > depth)
1847     {
1848       blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1849       chld = (offset - dr->offset) / blen;
1850       GNUNET_assert (chld < dr->num_children);
1851       dr = dr->children[chld];
1852     }
1853   switch (dr->state)
1854     {
1855     case BRS_INIT:
1856       break;
1857     case BRS_RECONSTRUCT_DOWN:
1858       break;
1859     case BRS_RECONSTRUCT_META_UP:
1860       break;
1861     case BRS_RECONSTRUCT_UP:
1862       break;
1863     case BRS_CHK_SET:
1864       if (0 == memcmp (chk,
1865                        &dr->chk,
1866                        sizeof (struct ContentHashKey)))
1867         {
1868           /* block matches, hence tree below matches; 
1869              this request is done! */
1870           dr->state = BRS_DOWNLOAD_UP;
1871           /* calculate how many bytes of payload this block
1872              corresponds to */
1873           blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1874           /* how many of those bytes are in the requested range? */
1875           blen = GNUNET_MIN (blen,
1876                              dc->length + dc->offset - dr->offset);
1877           /* signal progress */
1878           dc->completed += blen;
1879           pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1880           pi.value.download.specifics.progress.data = NULL;
1881           pi.value.download.specifics.progress.offset = offset;
1882           pi.value.download.specifics.progress.data_len = 0;
1883           pi.value.download.specifics.progress.depth = 0;
1884           GNUNET_FS_download_make_status_ (&pi, dc);             
1885         }
1886       else
1887         {
1888         }
1889       break; 
1890     case BRS_DOWNLOAD_DOWN:
1891       break; 
1892     case BRS_DOWNLOAD_UP:
1893       break;
1894     case BRS_ERROR:
1895       break;
1896     default:
1897       GNUNET_assert (0);
1898       break;
1899     }
1900   if ( (dr == dc->top_request) &&
1901        (dr->state == BRS_DOWNLOAD_UP) )
1902     {
1903       check_completed (dc);
1904       return;
1905     }
1906   dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
1907                                        dc);
1908 }
1909
1910
1911 /**
1912  * Function called by the tree encoder to obtain a block of plaintext
1913  * data (for the lowest level of the tree).
1914  *
1915  * @param cls our 'struct ReconstructContext'
1916  * @param offset identifies which block to get
1917  * @param max (maximum) number of bytes to get; returning
1918  *        fewer will also cause errors
1919  * @param buf where to copy the plaintext buffer
1920  * @param emsg location to store an error message (on error)
1921  * @return number of bytes copied to buf, 0 on error
1922  */
1923 static size_t
1924 fh_reader (void *cls,
1925            uint64_t offset,
1926            size_t max, 
1927            void *buf,
1928            char **emsg)
1929 {
1930   struct GNUNET_FS_DownloadContext *dc = cls;
1931   struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1932   ssize_t ret;
1933
1934   *emsg = NULL;
1935   if (offset !=
1936       GNUNET_DISK_file_seek (fh,
1937                              offset,
1938                              GNUNET_DISK_SEEK_SET))
1939     {
1940       *emsg = GNUNET_strdup (strerror (errno));
1941       return 0;
1942     }
1943   ret = GNUNET_DISK_file_read (fh, buf, max);
1944   if (ret < 0)
1945     {
1946       *emsg = GNUNET_strdup (strerror (errno));
1947       return 0;
1948     }
1949   return ret;
1950 }
1951
1952
1953 /**
1954  * Task that creates the initial (top-level) download
1955  * request for the file.
1956  *
1957  * @param cls the 'struct GNUNET_FS_DownloadContext'
1958  * @param tc scheduler context
1959  */
1960 void
1961 GNUNET_FS_download_start_task_ (void *cls,
1962                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1963 {
1964   struct GNUNET_FS_DownloadContext *dc = cls;  
1965   struct GNUNET_FS_ProgressInfo pi;
1966   struct GNUNET_DISK_FileHandle *fh;
1967
1968 #if DEBUG_DOWNLOAD
1969   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1970               "Start task running...\n");
1971 #endif
1972   dc->task = GNUNET_SCHEDULER_NO_TASK;
1973   if (dc->length == 0)
1974     {
1975       /* no bytes required! */
1976       if (dc->filename != NULL) 
1977         {
1978           fh = GNUNET_DISK_file_open (dc->filename != NULL 
1979                                       ? dc->filename 
1980                                       : dc->temp_filename, 
1981                                       GNUNET_DISK_OPEN_READWRITE |
1982                                       GNUNET_DISK_OPEN_CREATE |
1983                                       ( (0 == GNUNET_FS_uri_chk_get_file_size (dc->uri)) 
1984                                         ? GNUNET_DISK_OPEN_TRUNCATE : 0),
1985                                       GNUNET_DISK_PERM_USER_READ |
1986                                       GNUNET_DISK_PERM_USER_WRITE |
1987                                       GNUNET_DISK_PERM_GROUP_READ |
1988                                       GNUNET_DISK_PERM_OTHER_READ);
1989           GNUNET_DISK_file_close (fh);
1990         }
1991       GNUNET_FS_download_sync_ (dc);
1992       check_completed (dc);
1993       return;
1994     }
1995   if (dc->emsg != NULL) 
1996     return;
1997   if (dc->top_request == NULL)
1998     {
1999       dc->top_request = create_download_request (NULL, dc->treedepth - 1, 0,
2000                                                  dc->offset, dc->length);
2001       dc->top_request->state = BRS_CHK_SET;
2002       dc->top_request->chk = (dc->uri->type == chk) 
2003         ? dc->uri->data.chk.chk
2004         : dc->uri->data.loc.fi.chk;
2005       /* signal start */
2006       GNUNET_FS_download_sync_ (dc);
2007       pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
2008       pi.value.download.specifics.start.meta = dc->meta;
2009       GNUNET_FS_download_make_status_ (&pi, dc);
2010     }
2011   GNUNET_FS_download_start_downloading_ (dc);
2012   /* attempt reconstruction from disk */
2013   if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))    
2014     dc->rfh = GNUNET_DISK_file_open (dc->filename,
2015                                      GNUNET_DISK_OPEN_READ,
2016                                      GNUNET_DISK_PERM_NONE);    
2017   if (dc->top_request->state == BRS_CHK_SET)
2018     {
2019       if (dc->rfh != NULL)
2020         {
2021           /* first, try top-down */
2022 #if DEBUG_DOWNLOAD
2023           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2024                       "Trying top-down reconstruction for `%s'\n",
2025                       dc->filename);
2026 #endif
2027           try_top_down_reconstruction (dc, dc->top_request);
2028           switch (dc->top_request->state)
2029             {
2030             case BRS_CHK_SET:
2031               break; /* normal */
2032             case BRS_DOWNLOAD_DOWN:
2033               break; /* normal, some blocks already down */
2034             case BRS_DOWNLOAD_UP:
2035               /* already done entirely, party! */
2036               if (dc->rfh != NULL)
2037                 {
2038                   /* avoid hanging on to file handle longer than 
2039                      necessary */
2040                   GNUNET_DISK_file_close (dc->rfh);
2041                   dc->rfh = NULL;
2042                 }
2043               return;      
2044             case BRS_ERROR:
2045               GNUNET_asprintf (&dc->emsg,
2046                                _("Invalid URI"));
2047               GNUNET_FS_download_sync_ (dc);
2048               pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
2049               pi.value.download.specifics.error.message = dc->emsg;
2050               GNUNET_FS_download_make_status_ (&pi, dc);
2051               return;
2052             default:
2053               GNUNET_assert (0);
2054               break;
2055             }
2056         }
2057     }
2058   /* attempt reconstruction from meta data */
2059   if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
2060        (NULL != dc->meta) )
2061     {
2062 #if DEBUG_DOWNLOAD
2063       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2064                   "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
2065                   (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
2066                   (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
2067 #endif
2068       GNUNET_CONTAINER_meta_data_iterate (dc->meta,
2069                                           &match_full_data,
2070                                           dc);
2071       if (dc->top_request->state == BRS_DOWNLOAD_UP)
2072         {
2073           if (dc->rfh != NULL)
2074             {
2075               /* avoid hanging on to file handle longer than 
2076                  necessary */
2077               GNUNET_DISK_file_close (dc->rfh);
2078               dc->rfh = NULL;
2079             }
2080           return; /* finished, status update was already done for us */
2081         }
2082     }
2083   if (dc->rfh != NULL)
2084     {
2085       /* finally, try bottom-up */
2086 #if DEBUG_DOWNLOAD
2087       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088                   "Trying bottom-up reconstruction of file `%s'\n",
2089                   dc->filename);
2090 #endif
2091       dc->te = GNUNET_FS_tree_encoder_create (dc->h,
2092                                               dc->old_file_size,
2093                                               dc,
2094                                               &fh_reader,
2095                                               &reconstruct_cb,
2096                                               NULL,
2097                                               &reconstruct_cont);     
2098       dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
2099                                            dc);
2100     }
2101   else
2102     {
2103       /* simple, top-level download */
2104         schedule_block_download (dc, 
2105                                  dc->top_request);
2106     }
2107   if (dc->top_request->state == BRS_DOWNLOAD_UP)
2108     check_completed (dc);
2109 }
2110
2111
2112 /**
2113  * Create SUSPEND event for the given download operation
2114  * and then clean up our state (without stop signal).
2115  *
2116  * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
2117  */
2118 void
2119 GNUNET_FS_download_signal_suspend_ (void *cls)
2120 {
2121   struct GNUNET_FS_DownloadContext *dc = cls;
2122   struct GNUNET_FS_ProgressInfo pi;
2123
2124   if (dc->top != NULL)
2125     GNUNET_FS_end_top (dc->h, dc->top);
2126   while (NULL != dc->child_head)
2127     GNUNET_FS_download_signal_suspend_ (dc->child_head);  
2128   if (dc->search != NULL)
2129     {
2130       dc->search->download = NULL;
2131       dc->search = NULL;
2132     }
2133   if (dc->job_queue != NULL)
2134     {
2135       GNUNET_FS_dequeue_ (dc->job_queue);
2136       dc->job_queue = NULL;
2137     }
2138   if (dc->parent != NULL)
2139     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2140                                  dc->parent->child_tail,
2141                                  dc);  
2142   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2143     {
2144       GNUNET_SCHEDULER_cancel (dc->task);
2145       dc->task = GNUNET_SCHEDULER_NO_TASK;
2146     }
2147   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
2148   GNUNET_FS_download_make_status_ (&pi, dc);
2149   if (dc->te != NULL)
2150     {
2151       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);       
2152       dc->te = NULL;
2153     }
2154   if (dc->rfh != NULL)
2155     {
2156       GNUNET_DISK_file_close (dc->rfh);
2157       dc->rfh = NULL;
2158     }
2159   GNUNET_FS_free_download_request_ (dc->top_request);
2160   if (dc->active != NULL)
2161     {
2162       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2163       dc->active = NULL;
2164     }
2165   GNUNET_free_non_null (dc->filename);
2166   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2167   GNUNET_FS_uri_destroy (dc->uri);
2168   GNUNET_free_non_null (dc->temp_filename);
2169   GNUNET_free_non_null (dc->serialization);
2170   GNUNET_free (dc);
2171 }
2172
2173
2174 /**
2175  * Download parts of a file.  Note that this will store
2176  * the blocks at the respective offset in the given file.  Also, the
2177  * download is still using the blocking of the underlying FS
2178  * encoding.  As a result, the download may *write* outside of the
2179  * given boundaries (if offset and length do not match the 32k FS
2180  * block boundaries). <p>
2181  *
2182  * This function should be used to focus a download towards a
2183  * particular portion of the file (optimization), not to strictly
2184  * limit the download to exactly those bytes.
2185  *
2186  * @param h handle to the file sharing subsystem
2187  * @param uri the URI of the file (determines what to download); CHK or LOC URI
2188  * @param meta known metadata for the file (can be NULL)
2189  * @param filename where to store the file, maybe NULL (then no file is
2190  *        created on disk and data must be grabbed from the callbacks)
2191  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2192  *        can be NULL (in which case we will pick a name if needed); the temporary file
2193  *        may already exist, in which case we will try to use the data that is there and
2194  *        if it is not what is desired, will overwrite it
2195  * @param offset at what offset should we start the download (typically 0)
2196  * @param length how many bytes should be downloaded starting at offset
2197  * @param anonymity anonymity level to use for the download
2198  * @param options various options
2199  * @param cctx initial value for the client context for this download
2200  * @param parent parent download to associate this download with (use NULL
2201  *        for top-level downloads; useful for manually-triggered recursive downloads)
2202  * @return context that can be used to control this download
2203  */
2204 struct GNUNET_FS_DownloadContext *
2205 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2206                           const struct GNUNET_FS_Uri *uri,
2207                           const struct GNUNET_CONTAINER_MetaData *meta,
2208                           const char *filename,
2209                           const char *tempname,
2210                           uint64_t offset,
2211                           uint64_t length,
2212                           uint32_t anonymity,
2213                           enum GNUNET_FS_DownloadOptions options,
2214                           void *cctx,
2215                           struct GNUNET_FS_DownloadContext *parent)
2216 {
2217   struct GNUNET_FS_DownloadContext *dc;
2218
2219   GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
2220                  GNUNET_FS_uri_test_loc (uri) );
2221                  
2222   if ( (offset + length < offset) ||
2223        (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
2224     {      
2225       GNUNET_break (0);
2226       return NULL;
2227     }
2228 #if DEBUG_DOWNLOAD
2229   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2230               "Starting download `%s' of %llu bytes\n",
2231               filename,
2232               (unsigned long long) length);
2233 #endif
2234   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2235   dc->h = h;
2236   dc->parent = parent;
2237   if (parent != NULL)
2238     {
2239       GNUNET_CONTAINER_DLL_insert (parent->child_head,
2240                                    parent->child_tail,
2241                                    dc);
2242     }
2243   dc->uri = GNUNET_FS_uri_dup (uri);
2244   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2245   dc->client_info = cctx;
2246   dc->start_time = GNUNET_TIME_absolute_get ();
2247   if (NULL != filename)
2248     {
2249       dc->filename = GNUNET_strdup (filename);
2250       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2251         GNUNET_DISK_file_size (filename,
2252                                &dc->old_file_size,
2253                                GNUNET_YES);
2254     }
2255   if (GNUNET_FS_uri_test_loc (dc->uri))
2256     GNUNET_assert (GNUNET_OK ==
2257                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2258                                                         &dc->target));
2259   dc->offset = offset;
2260   dc->length = length;
2261   dc->anonymity = anonymity;
2262   dc->options = options;
2263   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2264   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
2265   if ( (filename == NULL) &&
2266        (is_recursive_download (dc) ) )
2267     {
2268       if (tempname != NULL)
2269         dc->temp_filename = GNUNET_strdup (tempname);
2270       else
2271         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2272     }
2273
2274 #if DEBUG_DOWNLOAD
2275   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2276               "Download tree has depth %u\n",
2277               dc->treedepth);
2278 #endif
2279   if (parent == NULL)
2280     {
2281       dc->top = GNUNET_FS_make_top (dc->h,
2282                                     &GNUNET_FS_download_signal_suspend_,
2283                                     dc);
2284     }
2285   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2286                                        dc);
2287   return dc;
2288 }
2289
2290
2291 /**
2292  * Download parts of a file based on a search result.  The download
2293  * will be associated with the search result (and the association
2294  * will be preserved when serializing/deserializing the state).
2295  * If the search is stopped, the download will not be aborted but
2296  * be 'promoted' to a stand-alone download.
2297  *
2298  * As with the other download function, this will store
2299  * the blocks at the respective offset in the given file.  Also, the
2300  * download is still using the blocking of the underlying FS
2301  * encoding.  As a result, the download may *write* outside of the
2302  * given boundaries (if offset and length do not match the 32k FS
2303  * block boundaries). <p>
2304  *
2305  * The given range can be used to focus a download towards a
2306  * particular portion of the file (optimization), not to strictly
2307  * limit the download to exactly those bytes.
2308  *
2309  * @param h handle to the file sharing subsystem
2310  * @param sr the search result to use for the download (determines uri and
2311  *        meta data and associations)
2312  * @param filename where to store the file, maybe NULL (then no file is
2313  *        created on disk and data must be grabbed from the callbacks)
2314  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2315  *        can be NULL (in which case we will pick a name if needed); the temporary file
2316  *        may already exist, in which case we will try to use the data that is there and
2317  *        if it is not what is desired, will overwrite it
2318  * @param offset at what offset should we start the download (typically 0)
2319  * @param length how many bytes should be downloaded starting at offset
2320  * @param anonymity anonymity level to use for the download
2321  * @param options various download options
2322  * @param cctx initial value for the client context for this download
2323  * @return context that can be used to control this download
2324  */
2325 struct GNUNET_FS_DownloadContext *
2326 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2327                                       struct GNUNET_FS_SearchResult *sr,
2328                                       const char *filename,
2329                                       const char *tempname,
2330                                       uint64_t offset,
2331                                       uint64_t length,
2332                                       uint32_t anonymity,
2333                                       enum GNUNET_FS_DownloadOptions options,
2334                                       void *cctx)
2335 {
2336   struct GNUNET_FS_DownloadContext *dc;
2337
2338   if ( (sr == NULL) ||
2339        (sr->download != NULL) )
2340     {
2341       GNUNET_break (0);
2342       return NULL;
2343     }
2344   GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
2345                  GNUNET_FS_uri_test_loc (sr->uri) );             
2346   if ( (offset + length < offset) ||
2347        (offset + length > sr->uri->data.chk.file_length) )
2348     {      
2349       GNUNET_break (0);
2350       return NULL;
2351     }
2352 #if DEBUG_DOWNLOAD
2353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2354               "Starting download `%s' of %llu bytes\n",
2355               filename,
2356               (unsigned long long) length);
2357 #endif
2358   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2359   dc->h = h;
2360   dc->search = sr;
2361   sr->download = dc;
2362   if (sr->probe_ctx != NULL)
2363     {
2364       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2365       sr->probe_ctx = NULL;      
2366     }
2367   dc->uri = GNUNET_FS_uri_dup (sr->uri);
2368   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
2369   dc->client_info = cctx;
2370   dc->start_time = GNUNET_TIME_absolute_get ();
2371   if (NULL != filename)
2372     {
2373       dc->filename = GNUNET_strdup (filename);
2374       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2375         GNUNET_DISK_file_size (filename,
2376                                &dc->old_file_size,
2377                                GNUNET_YES);
2378     }
2379   if (GNUNET_FS_uri_test_loc (dc->uri))
2380     GNUNET_assert (GNUNET_OK ==
2381                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2382                                                         &dc->target));
2383   dc->offset = offset;
2384   dc->length = length;
2385   dc->anonymity = anonymity;
2386   dc->options = options;
2387   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2388   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
2389   if ( (filename == NULL) &&
2390        (is_recursive_download (dc) ) )
2391     {
2392       if (tempname != NULL)
2393         dc->temp_filename = GNUNET_strdup (tempname);
2394       else
2395         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
2396     }
2397
2398 #if DEBUG_DOWNLOAD
2399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2400               "Download tree has depth %u\n",
2401               dc->treedepth);
2402 #endif
2403   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, 
2404                                        dc);
2405   return dc;  
2406 }
2407
2408
2409 /**
2410  * Start the downloading process (by entering the queue).
2411  *
2412  * @param dc our download context
2413  */
2414 void
2415 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2416 {
2417   if (dc->completed == dc->length)
2418     return;
2419   GNUNET_assert (dc->job_queue == NULL);
2420   dc->job_queue = GNUNET_FS_queue_ (dc->h, 
2421                                     &activate_fs_download,
2422                                     &deactivate_fs_download,
2423                                     dc,
2424                                     (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
2425 }
2426
2427
2428 /**
2429  * Stop a download (aborts if download is incomplete).
2430  *
2431  * @param dc handle for the download
2432  * @param do_delete delete files of incomplete downloads
2433  */
2434 void
2435 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
2436                          int do_delete)
2437 {
2438   struct GNUNET_FS_ProgressInfo pi;
2439   int have_children;
2440
2441   if (dc->top != NULL)
2442     GNUNET_FS_end_top (dc->h, dc->top);
2443
2444
2445   if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2446     {
2447       GNUNET_SCHEDULER_cancel (dc->task);
2448       dc->task = GNUNET_SCHEDULER_NO_TASK;
2449     }
2450   if (dc->search != NULL)
2451     {
2452       dc->search->download = NULL;
2453       dc->search = NULL;
2454     }
2455   if (dc->job_queue != NULL)
2456     {
2457       GNUNET_FS_dequeue_ (dc->job_queue);
2458       dc->job_queue = NULL;
2459     }
2460   if (dc->te != NULL)
2461     {
2462       GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2463       dc->te = NULL;
2464     }
2465   have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2466   while (NULL != dc->child_head)
2467     GNUNET_FS_download_stop (dc->child_head, 
2468                              do_delete);
2469   if (dc->parent != NULL)
2470     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2471                                  dc->parent->child_tail,
2472                                  dc);  
2473   if (dc->serialization != NULL)
2474     GNUNET_FS_remove_sync_file_ (dc->h,
2475                                  ( (dc->parent != NULL)  || (dc->search != NULL) )
2476                                  ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2477                                  : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD , 
2478                                  dc->serialization);
2479   if ( (GNUNET_YES == have_children) &&
2480        (dc->parent == NULL) )
2481     GNUNET_FS_remove_sync_dir_ (dc->h, 
2482                                 (dc->search != NULL) 
2483                                 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
2484                                 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2485                                 dc->serialization);  
2486   pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2487   GNUNET_FS_download_make_status_ (&pi, dc);
2488   GNUNET_FS_free_download_request_ (dc->top_request);
2489   dc->top_request = NULL;
2490   if (dc->active != NULL)
2491     {
2492       GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2493       dc->active = NULL;
2494     }
2495   if (dc->filename != NULL)
2496     {
2497       if ( (dc->completed != dc->length) &&
2498            (GNUNET_YES == do_delete) )
2499         {
2500           if (0 != UNLINK (dc->filename))
2501             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2502                                       "unlink",
2503                                       dc->filename);
2504         }
2505       GNUNET_free (dc->filename);
2506     }
2507   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2508   GNUNET_FS_uri_destroy (dc->uri);
2509   if (NULL != dc->temp_filename)
2510     {
2511       if (0 != UNLINK (dc->temp_filename))
2512         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2513                                   "unlink",
2514                                   dc->temp_filename);
2515       GNUNET_free (dc->temp_filename);
2516     }
2517   GNUNET_free_non_null (dc->serialization);
2518   GNUNET_free (dc);
2519 }
2520
2521 /* end of fs_download.c */