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