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