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