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