fix
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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  * - check if iblocks can be computed from existing blocks (can wait, hard)
28  */
29 #include "platform.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_fs_service.h"
32 #include "fs.h"
33 #include "fs_tree.h"
34
35 #define DEBUG_DOWNLOAD GNUNET_NO
36
37 /**
38  * Determine if the given download (options and meta data) should cause
39  * use to try to do a recursive download.
40  */
41 static int
42 is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
43 {
44   return  (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
45     ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
46       ( (dc->meta == NULL) &&
47         ( (NULL == dc->filename) ||            
48           ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
49             (NULL !=
50              strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
51                      GNUNET_FS_DIRECTORY_EXT)) ) ) ) );              
52 }
53
54
55 /**
56  * We're storing the IBLOCKS after the DBLOCKS on disk (so that we
57  * only have to truncate the file once we're done).
58  *
59  * Given the offset of a block (with respect to the DBLOCKS) and its
60  * depth, return the offset where we would store this block in the
61  * file.
62  * 
63  * @param fsize overall file size
64  * @param off offset of the block in the file
65  * @param depth depth of the block in the tree
66  * @param treedepth maximum depth of the tree
67  * @return off for DBLOCKS (depth == treedepth),
68  *         otherwise an offset past the end
69  *         of the file that does not overlap
70  *         with the range for any other block
71  */
72 static uint64_t
73 compute_disk_offset (uint64_t fsize,
74                      uint64_t off,
75                      unsigned int depth,
76                      unsigned int treedepth)
77 {
78   unsigned int i;
79   uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
80   uint64_t loff; /* where do IBlocks for depth "i" start? */
81   unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
82   
83   if (depth == treedepth)
84     return off;
85   /* first IBlocks start at the end of file, rounded up
86      to full DBLOCK_SIZE */
87   loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
88   lsize = ( (fsize + DBLOCK_SIZE-1) / DBLOCK_SIZE) * sizeof (struct ContentHashKey);
89   GNUNET_assert (0 == (off % DBLOCK_SIZE));
90   ioff = (off / DBLOCK_SIZE);
91   for (i=treedepth-1;i>depth;i--)
92     {
93       loff += lsize;
94       lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE;
95       GNUNET_assert (lsize > 0);
96       GNUNET_assert (0 == (ioff % CHK_PER_INODE));
97       ioff /= CHK_PER_INODE;
98     }
99   return loff + ioff * sizeof (struct ContentHashKey);
100 }
101
102
103 /**
104  * Given a file of the specified treedepth and a block at the given
105  * offset and depth, calculate the offset for the CHK at the given
106  * index.
107  *
108  * @param offset the offset of the first
109  *        DBLOCK in the subtree of the 
110  *        identified IBLOCK
111  * @param depth the depth of the IBLOCK in the tree
112  * @param treedepth overall depth of the tree
113  * @param k which CHK in the IBLOCK are we 
114  *        talking about
115  * @return offset if k=0, otherwise an appropriately
116  *         larger value (i.e., if depth = treedepth-1,
117  *         the returned value should be offset+DBLOCK_SIZE)
118  */
119 static uint64_t
120 compute_dblock_offset (uint64_t offset,
121                        unsigned int depth,
122                        unsigned int treedepth,
123                        unsigned int k)
124 {
125   unsigned int i;
126   uint64_t lsize; /* what is the size of the sum of all DBlocks 
127                      that a CHK at depth i corresponds to? */
128
129   if (depth == treedepth)
130     return offset;
131   lsize = DBLOCK_SIZE;
132   for (i=treedepth-1;i>depth;i--)
133     lsize *= CHK_PER_INODE;
134   return offset + k * lsize;
135 }
136
137
138 /**
139  * Fill in all of the generic fields for a download event and call the
140  * callback.
141  *
142  * @param pi structure to fill in
143  * @param dc overall download context
144  */
145 void
146 GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
147                                  struct GNUNET_FS_DownloadContext *dc)
148 {
149   pi->value.download.dc = dc;
150   pi->value.download.cctx
151     = dc->client_info;
152   pi->value.download.pctx
153     = (dc->parent == NULL) ? NULL : dc->parent->client_info;
154   pi->value.download.sctx
155     = (dc->search == NULL) ? NULL : dc->search->client_info;
156   pi->value.download.uri 
157     = dc->uri;
158   pi->value.download.filename
159     = dc->filename;
160   pi->value.download.size
161     = dc->length;
162   pi->value.download.duration
163     = GNUNET_TIME_absolute_get_duration (dc->start_time);
164   pi->value.download.completed
165     = dc->completed;
166   pi->value.download.anonymity
167     = dc->anonymity;
168   pi->value.download.eta
169     = GNUNET_TIME_calculate_eta (dc->start_time,
170                                  dc->completed,
171                                  dc->length);
172   pi->value.download.is_active = (dc->client == NULL) ? GNUNET_NO : GNUNET_YES;
173   if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
174     dc->client_info = dc->h->upcb (dc->h->upcb_cls,
175                                    pi);
176   else
177     dc->client_info = GNUNET_FS_search_probe_progress_ (NULL,
178                                                         pi);
179 }
180
181 /**
182  * We're ready to transmit a search request to the
183  * file-sharing service.  Do it.  If there is 
184  * more than one request pending, try to send 
185  * multiple or request another transmission.
186  *
187  * @param cls closure
188  * @param size number of bytes available in buf
189  * @param buf where the callee should write the message
190  * @return number of bytes written to buf
191  */
192 static size_t
193 transmit_download_request (void *cls,
194                            size_t size, 
195                            void *buf);
196
197
198 /**
199  * Closure for iterator processing results.
200  */
201 struct ProcessResultClosure
202 {
203   
204   /**
205    * Hash of data.
206    */
207   GNUNET_HashCode query;
208
209   /**
210    * Data found in P2P network.
211    */ 
212   const void *data;
213
214   /**
215    * Our download context.
216    */
217   struct GNUNET_FS_DownloadContext *dc;
218                 
219   /**
220    * Number of bytes in data.
221    */
222   size_t size;
223
224   /**
225    * Type of data.
226    */
227   enum GNUNET_BLOCK_Type type;
228
229   /**
230    * Flag to indicate if this block should be stored on disk.
231    */
232   int do_store;
233   
234 };
235
236
237 /**
238  * Iterator over entries in the pending requests in the 'active' map for the
239  * reply that we just got.
240  *
241  * @param cls closure (our 'struct ProcessResultClosure')
242  * @param key query for the given value / request
243  * @param value value in the hash map (a 'struct DownloadRequest')
244  * @return GNUNET_YES (we should continue to iterate); unless serious error
245  */
246 static int
247 process_result_with_request (void *cls,
248                              const GNUNET_HashCode * key,
249                              void *value);
250
251
252 /**
253  * We've found a matching block without downloading it.
254  * Encrypt it and pass it to our "receive" function as
255  * if we had received it from the network.
256  * 
257  * @param dc download in question
258  * @param chk request this relates to
259  * @param sm request details
260  * @param block plaintext data matching request
261  * @param len number of bytes in block
262  * @param depth depth of the block
263  * @param do_store should we still store the block on disk?
264  * @return GNUNET_OK on success
265  */
266 static int
267 encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
268                         const struct ContentHashKey *chk,
269                         struct DownloadRequest *sm,
270                         const char * block,                    
271                         size_t len,
272                         int depth,
273                         int do_store)
274 {
275   struct ProcessResultClosure prc;
276   char enc[len];
277   struct GNUNET_CRYPTO_AesSessionKey sk;
278   struct GNUNET_CRYPTO_AesInitializationVector iv;
279   GNUNET_HashCode query;
280   
281   GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
282   if (-1 == GNUNET_CRYPTO_aes_encrypt (block, len,
283                                        &sk,
284                                        &iv,
285                                        enc))
286     {
287       GNUNET_break (0);
288       return GNUNET_SYSERR;
289     }
290   GNUNET_CRYPTO_hash (enc, len, &query);
291   if (0 != memcmp (&query,
292                    &chk->query,
293                    sizeof (GNUNET_HashCode)))
294     {
295       GNUNET_break_op (0);
296       return GNUNET_SYSERR;
297     }
298 #if DEBUG_DOWNLOAD
299   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300               "Matching block already present, no need for download!\n");
301 #endif
302   /* already got it! */
303   prc.dc = dc;
304   prc.data = enc;
305   prc.size = len;
306   prc.type = (dc->treedepth == depth) 
307     ? GNUNET_BLOCK_TYPE_FS_DBLOCK 
308     : GNUNET_BLOCK_TYPE_FS_IBLOCK;
309   prc.query = chk->query;
310   prc.do_store = do_store;
311   process_result_with_request (&prc,
312                                &chk->key,
313                                sm);
314   return GNUNET_OK;
315 }
316
317
318 /**
319  * Closure for match_full_data.
320  */
321 struct MatchDataContext 
322 {
323   /**
324    * CHK we are looking for.
325    */
326   const struct ContentHashKey *chk;
327
328   /**
329    * Download we're processing.
330    */
331   struct GNUNET_FS_DownloadContext *dc;
332
333   /**
334    * Request details.
335    */
336   struct DownloadRequest *sm;
337
338   /**
339    * Overall offset in the file.
340    */
341   uint64_t offset;
342
343   /**
344    * Desired length of the block.
345    */
346   size_t len;
347
348   /**
349    * Flag set to GNUNET_YES on success.
350    */
351   int done;
352 };
353
354 /**
355  * Type of a function that libextractor calls for each
356  * meta data item found.
357  *
358  * @param cls closure (user-defined)
359  * @param plugin_name name of the plugin that produced this value;
360  *        special values can be used (i.e. '<zlib>' for zlib being
361  *        used in the main libextractor library and yielding
362  *        meta data).
363  * @param type libextractor-type describing the meta data
364  * @param format basic format information about data 
365  * @param data_mime_type mime-type of data (not of the original file);
366  *        can be NULL (if mime-type is not known)
367  * @param data actual meta-data found
368  * @param data_len number of bytes in data
369  * @return 0 to continue extracting, 1 to abort
370  */ 
371 static int
372 match_full_data (void *cls,
373                  const char *plugin_name,
374                  enum EXTRACTOR_MetaType type,
375                  enum EXTRACTOR_MetaFormat format,
376                  const char *data_mime_type,
377                  const char *data,
378                  size_t data_len)
379 {
380   struct MatchDataContext *mdc = cls;
381   GNUNET_HashCode key;
382
383   if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA) 
384     {
385       if ( (mdc->offset > data_len) ||
386            (mdc->offset + mdc->len > data_len) )
387         return 1;
388       GNUNET_CRYPTO_hash (&data[mdc->offset],
389                           mdc->len,
390                           &key);
391       if (0 != memcmp (&key,
392                        &mdc->chk->key,
393                        sizeof (GNUNET_HashCode)))
394         {
395           GNUNET_break_op (0);
396           return 1;
397         }
398       /* match found! */
399       if (GNUNET_OK !=
400           encrypt_existing_match (mdc->dc,
401                                   mdc->chk,
402                                   mdc->sm,
403                                   &data[mdc->offset],
404                                   mdc->len,
405                                   0,
406                                   GNUNET_YES))
407         {
408           GNUNET_break_op (0);
409           return 1;
410         }
411       mdc->done = GNUNET_YES;
412       return 1;
413     }
414   return 0;
415 }
416
417
418 /**
419  * Schedule the download of the specified block in the tree.
420  *
421  * @param dc overall download this block belongs to
422  * @param chk content-hash-key of the block
423  * @param offset offset of the block in the file
424  *         (for IBlocks, the offset is the lowest
425  *          offset of any DBlock in the subtree under
426  *          the IBlock)
427  * @param depth depth of the block, 0 is the root of the tree
428  */
429 static void
430 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
431                          const struct ContentHashKey *chk,
432                          uint64_t offset,
433                          unsigned int depth)
434 {
435   struct DownloadRequest *sm;
436   uint64_t total;
437   uint64_t off;
438   size_t len;
439   char block[DBLOCK_SIZE];
440   GNUNET_HashCode key;
441   struct MatchDataContext mdc;
442   struct GNUNET_DISK_FileHandle *fh;
443
444   total = GNUNET_ntohll (dc->uri->data.chk.file_length);
445   len = GNUNET_FS_tree_calculate_block_size (total,
446                                              dc->treedepth,
447                                              offset,
448                                              depth);
449   off = compute_disk_offset (total,
450                              offset,
451                              depth,
452                              dc->treedepth);
453   sm = GNUNET_malloc (sizeof (struct DownloadRequest));
454   sm->chk = *chk;
455   sm->offset = offset;
456   sm->depth = depth;
457   sm->is_pending = GNUNET_YES;
458   sm->next = dc->pending;
459   dc->pending = sm;
460   GNUNET_CONTAINER_multihashmap_put (dc->active,
461                                      &chk->query,
462                                      sm,
463                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
464   if ( (dc->tried_full_data == GNUNET_NO) &&
465        (depth == 0) )
466     {      
467       mdc.dc = dc;
468       mdc.sm = sm;
469       mdc.chk = chk;
470       mdc.offset = offset;
471       mdc.len = len;
472       mdc.done = GNUNET_NO;
473       GNUNET_CONTAINER_meta_data_iterate (dc->meta,
474                                           &match_full_data,
475                                           &mdc);
476       if (mdc.done == GNUNET_YES)
477         return;
478       dc->tried_full_data = GNUNET_YES; 
479     }
480 #if DEBUG_DOWNLOAD
481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               "Scheduling download at offset %llu and depth %u for `%s'\n",
483               (unsigned long long) offset,
484               depth,
485               GNUNET_h2s (&chk->query));
486 #endif
487   fh = NULL;
488   if ( (dc->old_file_size > off) &&
489        (dc->filename != NULL) )    
490     fh = GNUNET_DISK_file_open (dc->filename,
491                                 GNUNET_DISK_OPEN_READ,
492                                 GNUNET_DISK_PERM_NONE);    
493   if ( (fh != NULL) &&
494        (off  == 
495         GNUNET_DISK_file_seek (fh,
496                                off,
497                                GNUNET_DISK_SEEK_SET) ) &&
498        (len == 
499         GNUNET_DISK_file_read (fh,
500                                block,
501                                len)) )
502     {
503       GNUNET_CRYPTO_hash (block, len, &key);
504       if ( (0 == memcmp (&key,
505                          &chk->key,
506                          sizeof (GNUNET_HashCode))) &&
507            (GNUNET_OK ==
508             encrypt_existing_match (dc,
509                                     chk,
510                                     sm,
511                                     block,
512                                     len,
513                                     depth,
514                                     GNUNET_NO)) )
515         {
516           GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
517           return;
518         }
519     }
520   if (fh != NULL)
521     GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
522   if (depth < dc->treedepth)
523     {
524       // FIXME: try if we could
525       // reconstitute this IBLOCK
526       // from the existing blocks on disk (can wait)
527       // (read block(s), encode, compare with
528       // query; if matches, simply return)
529     }
530
531   if ( (dc->th == NULL) &&
532        (dc->client != NULL) )
533     {
534 #if DEBUG_DOWNLOAD
535       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536                   "Asking for transmission to FS service\n");
537 #endif
538       dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
539                                                     sizeof (struct SearchMessage),
540                                                     GNUNET_CONSTANTS_SERVICE_TIMEOUT,
541                                                     GNUNET_NO,
542                                                     &transmit_download_request,
543                                                     dc);
544     }
545   else
546     {
547 #if DEBUG_DOWNLOAD
548       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
549                   "Transmission request not issued (%p %p)\n",
550                   dc->th, 
551                   dc->client);
552 #endif
553
554     }
555
556 }
557
558
559
560 /**
561  * Suggest a filename based on given metadata.
562  * 
563  * @param md given meta data
564  * @return NULL if meta data is useless for suggesting a filename
565  */
566 char *
567 GNUNET_FS_meta_data_suggest_filename (const struct GNUNET_CONTAINER_MetaData *md)
568 {
569   static const char *mimeMap[][2] = {
570     {"application/bz2", ".bz2"},
571     {"application/gnunet-directory", ".gnd"},
572     {"application/java", ".class"},
573     {"application/msword", ".doc"},
574     {"application/ogg", ".ogg"},
575     {"application/pdf", ".pdf"},
576     {"application/pgp-keys", ".key"},
577     {"application/pgp-signature", ".pgp"},
578     {"application/postscript", ".ps"},
579     {"application/rar", ".rar"},
580     {"application/rtf", ".rtf"},
581     {"application/xml", ".xml"},
582     {"application/x-debian-package", ".deb"},
583     {"application/x-dvi", ".dvi"},
584     {"applixation/x-flac", ".flac"},
585     {"applixation/x-gzip", ".gz"},
586     {"application/x-java-archive", ".jar"},
587     {"application/x-java-vm", ".class"},
588     {"application/x-python-code", ".pyc"},
589     {"application/x-redhat-package-manager", ".rpm"},
590     {"application/x-rpm", ".rpm"},
591     {"application/x-tar", ".tar"},
592     {"application/x-tex-pk", ".pk"},
593     {"application/x-texinfo", ".texinfo"},
594     {"application/x-xcf", ".xcf"},
595     {"application/x-xfig", ".xfig"},
596     {"application/zip", ".zip"},
597     
598     {"audio/midi", ".midi"},
599     {"audio/mpeg", ".mp3"},
600     {"audio/real", ".rm"},
601     {"audio/x-wav", ".wav"},
602     
603     {"image/gif", ".gif"},
604     {"image/jpeg", ".jpg"},
605     {"image/pcx", ".pcx"},
606     {"image/png", ".png"},
607     {"image/tiff", ".tiff"},
608     {"image/x-ms-bmp", ".bmp"},
609     {"image/x-xpixmap", ".xpm"},
610     
611     {"text/css", ".css"},
612     {"text/html", ".html"},
613     {"text/plain", ".txt"},
614     {"text/rtf", ".rtf"},
615     {"text/x-c++hdr", ".h++"},
616     {"text/x-c++src", ".c++"},
617     {"text/x-chdr", ".h"},
618     {"text/x-csrc", ".c"},
619     {"text/x-java", ".java"},
620     {"text/x-moc", ".moc"},
621     {"text/x-pascal", ".pas"},
622     {"text/x-perl", ".pl"},
623     {"text/x-python", ".py"},
624     {"text/x-tex", ".tex"},
625     
626     {"video/avi", ".avi"},
627     {"video/mpeg", ".mpeg"},
628     {"video/quicktime", ".qt"},
629     {"video/real", ".rm"},
630     {"video/x-msvideo", ".avi"},
631     {NULL, NULL},
632   };
633   char *ret;
634   unsigned int i;
635   char *mime;
636   char *base;
637   const char *ext;
638
639   ret = GNUNET_CONTAINER_meta_data_get_by_type (md,
640                                                 EXTRACTOR_METATYPE_FILENAME);
641   if (ret != NULL)
642     return ret;  
643   ext = NULL;
644   mime = GNUNET_CONTAINER_meta_data_get_by_type (md,
645                                                  EXTRACTOR_METATYPE_MIMETYPE);
646   if (mime != NULL)
647     {
648       i = 0;
649       while ( (mimeMap[i][0] != NULL) && 
650               (0 != strcmp (mime, mimeMap[i][0])))
651         i++;
652       if (mimeMap[i][1] == NULL)
653         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | 
654                     GNUNET_ERROR_TYPE_BULK,
655                     _("Did not find mime type `%s' in extension list.\n"),
656                     mime);
657       else
658         ext = mimeMap[i][1];
659       GNUNET_free (mime);
660     }
661   base = GNUNET_CONTAINER_meta_data_get_first_by_types (md,
662                                                         EXTRACTOR_METATYPE_TITLE,
663                                                         EXTRACTOR_METATYPE_BOOK_TITLE,
664                                                         EXTRACTOR_METATYPE_ORIGINAL_TITLE,
665                                                         EXTRACTOR_METATYPE_PACKAGE_NAME,
666                                                         EXTRACTOR_METATYPE_URL,
667                                                         EXTRACTOR_METATYPE_URI, 
668                                                         EXTRACTOR_METATYPE_DESCRIPTION,
669                                                         EXTRACTOR_METATYPE_ISRC,
670                                                         EXTRACTOR_METATYPE_JOURNAL_NAME,
671                                                         EXTRACTOR_METATYPE_AUTHOR_NAME,
672                                                         EXTRACTOR_METATYPE_SUBJECT,
673                                                         EXTRACTOR_METATYPE_ALBUM,
674                                                         EXTRACTOR_METATYPE_ARTIST,
675                                                         EXTRACTOR_METATYPE_KEYWORDS,
676                                                         EXTRACTOR_METATYPE_COMMENT,
677                                                         EXTRACTOR_METATYPE_UNKNOWN,
678                                                         -1);
679   if ( (base == NULL) &&
680        (ext == NULL) )
681     return NULL;
682   if (base == NULL)
683     return GNUNET_strdup (ext);
684   if (ext == NULL)
685     return base;
686   GNUNET_asprintf (&ret,
687                    "%s%s",
688                    base,
689                    ext);
690   GNUNET_free (base);
691   return ret;
692 }
693
694
695 /**
696  * We've lost our connection with the FS service.
697  * Re-establish it and re-transmit all of our
698  * pending requests.
699  *
700  * @param dc download context that is having trouble
701  */
702 static void
703 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
704
705
706 /**
707  * We found an entry in a directory.  Check if the respective child
708  * already exists and if not create the respective child download.
709  *
710  * @param cls the parent download
711  * @param filename name of the file in the directory
712  * @param uri URI of the file (CHK or LOC)
713  * @param meta meta data of the file
714  * @param length number of bytes in data
715  * @param data contents of the file (or NULL if they were not inlined)
716  */
717 static void 
718 trigger_recursive_download (void *cls,
719                             const char *filename,
720                             const struct GNUNET_FS_Uri *uri,
721                             const struct GNUNET_CONTAINER_MetaData *meta,
722                             size_t length,
723                             const void *data);
724
725
726 /**
727  * We're done downloading a directory.  Open the file and
728  * trigger all of the (remaining) child downloads.
729  *
730  * @param dc context of download that just completed
731  */
732 static void
733 full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
734 {
735   size_t size;
736   uint64_t size64;
737   void *data;
738   struct GNUNET_DISK_FileHandle *h;
739   struct GNUNET_DISK_MapHandle *m;
740   
741   size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
742   size = (size_t) size64;
743   if (size64 != (uint64_t) size)
744     {
745       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
746                   _("Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
747       return;
748     }
749   if (dc->filename != NULL)
750     {
751       h = GNUNET_DISK_file_open (dc->filename,
752                                  GNUNET_DISK_OPEN_READ,
753                                  GNUNET_DISK_PERM_NONE);
754     }
755   else
756     {
757       GNUNET_assert (dc->temp_filename != NULL);
758       h = GNUNET_DISK_file_open (dc->temp_filename,
759                                  GNUNET_DISK_OPEN_READ,
760                                  GNUNET_DISK_PERM_NONE);
761     }
762   if (h == NULL)
763     return; /* oops */
764   data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
765   if (data == NULL)
766     {
767       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
768                   _("Directory too large for system address space\n"));
769     }
770   else
771     {
772       GNUNET_FS_directory_list_contents (size,
773                                          data,
774                                          0,
775                                          &trigger_recursive_download,
776                                          dc);         
777       GNUNET_DISK_file_unmap (m);
778     }
779   GNUNET_DISK_file_close (h);
780   if (dc->filename == NULL)
781     {
782       if (0 != UNLINK (dc->temp_filename))
783         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
784                                   "unlink",
785                                   dc->temp_filename);
786       GNUNET_free (dc->temp_filename);
787       dc->temp_filename = NULL;
788     }
789 }
790
791
792 /**
793  * Check if all child-downloads have completed and
794  * if so, signal completion (and possibly recurse to
795  * parent).
796  */
797 static void
798 check_completed (struct GNUNET_FS_DownloadContext *dc)
799 {
800   struct GNUNET_FS_ProgressInfo pi;
801   struct GNUNET_FS_DownloadContext *pos;
802
803   pos = dc->child_head;
804   while (pos != NULL)
805     {
806       if ( (pos->emsg == NULL) &&
807            (pos->completed < pos->length) )
808         return; /* not done yet */
809       if ( (pos->child_head != NULL) &&
810            (pos->has_finished != GNUNET_YES) )
811         return; /* not transitively done yet */
812       pos = pos->next;
813     }
814   dc->has_finished = GNUNET_YES;
815   GNUNET_FS_download_sync_ (dc);
816   /* signal completion */
817   pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
818   GNUNET_FS_download_make_status_ (&pi, dc);
819   if (dc->parent != NULL)
820     check_completed (dc->parent);  
821 }
822
823
824 /**
825  * We found an entry in a directory.  Check if the respective child
826  * already exists and if not create the respective child download.
827  *
828  * @param cls the parent download
829  * @param filename name of the file in the directory
830  * @param uri URI of the file (CHK or LOC)
831  * @param meta meta data of the file
832  * @param length number of bytes in data
833  * @param data contents of the file (or NULL if they were not inlined)
834  */
835 static void 
836 trigger_recursive_download (void *cls,
837                             const char *filename,
838                             const struct GNUNET_FS_Uri *uri,
839                             const struct GNUNET_CONTAINER_MetaData *meta,
840                             size_t length,
841                             const void *data)
842 {
843   struct GNUNET_FS_DownloadContext *dc = cls;  
844   struct GNUNET_FS_DownloadContext *cpos;
845   struct GNUNET_DISK_FileHandle *fh;
846   char *temp_name;
847   const char *real_name;
848   char *fn;
849   char *us;
850   char *ext;
851   char *dn;
852   char *pos;
853   char *full_name;
854
855   if (NULL == uri)
856     return; /* entry for the directory itself */
857   cpos = dc->child_head;
858   while (cpos != NULL)
859     {
860       if ( (GNUNET_FS_uri_test_equal (uri,
861                                       cpos->uri)) ||
862            ( (filename != NULL) &&
863              (0 == strcmp (cpos->filename,
864                            filename)) ) )
865         break;  
866       cpos = cpos->next;
867     }
868   if (cpos != NULL)
869     return; /* already exists */
870   fn = NULL;
871   if (NULL == filename)
872     {
873       fn = GNUNET_FS_meta_data_suggest_filename (meta);
874       if (fn == NULL)
875         {
876           us = GNUNET_FS_uri_to_string (uri);
877           fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_PREFIX 
878                                            GNUNET_FS_URI_CHK_INFIX)]);
879           GNUNET_free (us);
880         }
881       else if (fn[0] == '.')
882         {
883           ext = fn;
884           us = GNUNET_FS_uri_to_string (uri);
885           GNUNET_asprintf (&fn,
886                            "%s%s",
887                            &us[strlen (GNUNET_FS_URI_PREFIX 
888                                        GNUNET_FS_URI_CHK_INFIX)], ext);
889           GNUNET_free (ext);
890           GNUNET_free (us);
891         }
892       /* change '\' to '/' (this should have happened
893        during insertion, but malicious peers may
894        not have done this) */
895       while (NULL != (pos = strstr (fn, "\\")))
896         *pos = '/';
897       /* remove '../' everywhere (again, well-behaved
898          peers don't do this, but don't trust that
899          we did not get something nasty) */
900       while (NULL != (pos = strstr (fn, "../")))
901         {
902           pos[0] = '_';
903           pos[1] = '_';
904           pos[2] = '_';
905         }
906       filename = fn;
907     }
908   if (dc->filename == NULL)
909     {
910       full_name = NULL;
911     }
912   else
913     {
914       dn = GNUNET_strdup (dc->filename);
915       GNUNET_break ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
916                      (NULL !=
917                       strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
918                               GNUNET_FS_DIRECTORY_EXT)) );
919       if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
920            (NULL !=
921             strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
922                     GNUNET_FS_DIRECTORY_EXT)) )      
923         dn[strlen(dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';      
924       if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
925            ( (strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
926              (NULL ==
927               strstr (filename + strlen(filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
928                       GNUNET_FS_DIRECTORY_EXT)) ) )
929         {
930           GNUNET_asprintf (&full_name,
931                            "%s%s%s%s",
932                            dn,
933                            DIR_SEPARATOR_STR,
934                            filename,
935                            GNUNET_FS_DIRECTORY_EXT);
936         }
937       else
938         {
939           GNUNET_asprintf (&full_name,
940                            "%s%s%s",
941                            dn,
942                            DIR_SEPARATOR_STR,
943                            filename);
944         }
945       GNUNET_free (dn);
946     }
947   if ( (full_name != NULL) &&
948        (GNUNET_OK !=
949         GNUNET_DISK_directory_create_for_file (full_name)) )
950     {
951       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
952                   _("Failed to create directory for recursive download of `%s'\n"),
953                   full_name);
954       GNUNET_free (full_name);
955       GNUNET_free_non_null (fn);
956       return;
957     }
958
959   temp_name = NULL;
960   if ( (data != NULL) &&
961        (GNUNET_FS_uri_chk_get_file_size (uri) == length) )
962     {
963       if (full_name == NULL)
964         {
965           temp_name = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
966           real_name = temp_name;
967         }
968       else
969         {
970           real_name = full_name;
971         }
972       /* write to disk, then trigger normal download which will instantly progress to completion */
973       fh = GNUNET_DISK_file_open (real_name,
974                                   GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE,
975                                   GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
976       if (fh == NULL)
977         {
978           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
979                                     "open",
980                                     real_name);       
981           GNUNET_free (full_name);
982           GNUNET_free_non_null (fn);
983           return;
984         }
985       if (length != 
986           GNUNET_DISK_file_write (fh,
987                                   data,
988                                   length))
989         {
990           GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
991                                     "write",
992                                     full_name);       
993         }
994       GNUNET_DISK_file_close (fh);
995     }
996   GNUNET_FS_download_start (dc->h,
997                             uri,
998                             meta,
999                             full_name, temp_name,
1000                             0,
1001                             GNUNET_FS_uri_chk_get_file_size (uri),
1002                             dc->anonymity,
1003                             dc->options,
1004                             NULL,
1005                             dc);
1006   GNUNET_free_non_null (full_name);
1007   GNUNET_free_non_null (temp_name);
1008   GNUNET_free_non_null (fn);
1009 }
1010
1011
1012 /**
1013  * Free entries in the map.
1014  *
1015  * @param cls unused (NULL)
1016  * @param key unused
1017  * @param entry entry of type "struct DownloadRequest" which is freed
1018  * @return GNUNET_OK
1019  */
1020 static int
1021 free_entry (void *cls,
1022             const GNUNET_HashCode *key,
1023             void *entry)
1024 {
1025   GNUNET_free (entry);
1026   return GNUNET_OK;
1027 }
1028
1029
1030 /**
1031  * Iterator over entries in the pending requests in the 'active' map for the
1032  * reply that we just got.
1033  *
1034  * @param cls closure (our 'struct ProcessResultClosure')
1035  * @param key query for the given value / request
1036  * @param value value in the hash map (a 'struct DownloadRequest')
1037  * @return GNUNET_YES (we should continue to iterate); unless serious error
1038  */
1039 static int
1040 process_result_with_request (void *cls,
1041                              const GNUNET_HashCode * key,
1042                              void *value)
1043 {
1044   struct ProcessResultClosure *prc = cls;
1045   struct DownloadRequest *sm = value;
1046   struct DownloadRequest *ppos;
1047   struct DownloadRequest *pprev;
1048   struct GNUNET_DISK_FileHandle *fh;
1049   struct GNUNET_FS_DownloadContext *dc = prc->dc;
1050   struct GNUNET_CRYPTO_AesSessionKey skey;
1051   struct GNUNET_CRYPTO_AesInitializationVector iv;
1052   char pt[prc->size];
1053   struct GNUNET_FS_ProgressInfo pi;
1054   uint64_t off;
1055   size_t bs;
1056   size_t app;
1057   int i;
1058   struct ContentHashKey *chk;
1059
1060   fh = NULL;
1061   bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (dc->uri->data.chk.file_length),
1062                                             dc->treedepth,
1063                                             sm->offset,
1064                                             sm->depth);
1065   if (prc->size != bs)
1066     {
1067 #if DEBUG_DOWNLOAD
1068       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069                   "Internal error or bogus download URI (expected %u bytes, got %u)\n",
1070                   bs,
1071                   prc->size);
1072 #endif
1073       dc->emsg = GNUNET_strdup ("Internal error or bogus download URI");
1074       goto signal_error;
1075     }
1076   GNUNET_assert (GNUNET_YES ==
1077                  GNUNET_CONTAINER_multihashmap_remove (dc->active,
1078                                                        &prc->query,
1079                                                        sm));
1080   /* if this request is on the pending list, remove it! */
1081   pprev = NULL;
1082   ppos = dc->pending;
1083   while (ppos != NULL)
1084     {
1085       if (ppos == sm)
1086         {
1087           if (pprev == NULL)
1088             dc->pending = ppos->next;
1089           else
1090             pprev->next = ppos->next;
1091           break;
1092         }
1093       pprev = ppos;
1094       ppos = ppos->next;
1095     }
1096   GNUNET_CRYPTO_hash_to_aes_key (&sm->chk.key, &skey, &iv);
1097   if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
1098                                        prc->size,
1099                                        &skey,
1100                                        &iv,
1101                                        pt))
1102     {
1103       GNUNET_break (0);
1104       dc->emsg = GNUNET_strdup ("internal error decrypting content");
1105       goto signal_error;
1106     }
1107   off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1108                              sm->offset,
1109                              sm->depth,
1110                              dc->treedepth);
1111   /* save to disk */
1112   if ( ( GNUNET_YES == prc->do_store) &&
1113        ( (dc->filename != NULL) ||
1114          (is_recursive_download (dc)) ) &&
1115        ( (sm->depth == dc->treedepth) ||
1116          (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1117     {
1118       fh = GNUNET_DISK_file_open (dc->filename != NULL 
1119                                   ? dc->filename 
1120                                   : dc->temp_filename, 
1121                                   GNUNET_DISK_OPEN_READWRITE | 
1122                                   GNUNET_DISK_OPEN_CREATE,
1123                                   GNUNET_DISK_PERM_USER_READ |
1124                                   GNUNET_DISK_PERM_USER_WRITE |
1125                                   GNUNET_DISK_PERM_GROUP_READ |
1126                                   GNUNET_DISK_PERM_OTHER_READ);
1127     }
1128   if ( (NULL == fh) &&
1129        (GNUNET_YES == prc->do_store) &&
1130        ( (dc->filename != NULL) ||
1131          (is_recursive_download (dc)) ) &&
1132        ( (sm->depth == dc->treedepth) ||
1133          (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1134     {
1135       GNUNET_asprintf (&dc->emsg,
1136                        _("Download failed: could not open file `%s': %s\n"),
1137                        dc->filename,
1138                        STRERROR (errno));
1139       goto signal_error;
1140     }
1141   if (fh != NULL)
1142     {
1143 #if DEBUG_DOWNLOAD
1144       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1145                   "Saving decrypted block to disk at offset %llu\n",
1146                   (unsigned long long) off);
1147 #endif
1148       if ( (off  != 
1149             GNUNET_DISK_file_seek (fh,
1150                                    off,
1151                                    GNUNET_DISK_SEEK_SET) ) )
1152         {
1153           GNUNET_asprintf (&dc->emsg,
1154                            _("Failed to seek to offset %llu in file `%s': %s\n"),
1155                            (unsigned long long) off,
1156                            dc->filename,
1157                            STRERROR (errno));
1158           goto signal_error;
1159         }
1160       if (prc->size !=
1161           GNUNET_DISK_file_write (fh,
1162                                   pt,
1163                                   prc->size))
1164         {
1165           GNUNET_asprintf (&dc->emsg,
1166                            _("Failed to write block of %u bytes at offset %llu in file `%s': %s\n"),
1167                            (unsigned int) prc->size,
1168                            (unsigned long long) off,
1169                            dc->filename,
1170                            STRERROR (errno));
1171           goto signal_error;
1172         }
1173       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1174       fh = NULL;
1175     }
1176   if (sm->depth == dc->treedepth) 
1177     {
1178       app = prc->size;
1179       if (sm->offset < dc->offset)
1180         {
1181           /* starting offset begins in the middle of pt,
1182              do not count first bytes as progress */
1183           GNUNET_assert (app > (dc->offset - sm->offset));
1184           app -= (dc->offset - sm->offset);       
1185         }
1186       if (sm->offset + prc->size > dc->offset + dc->length)
1187         {
1188           /* end of block is after relevant range,
1189              do not count last bytes as progress */
1190           GNUNET_assert (app > (sm->offset + prc->size) - (dc->offset + dc->length));
1191           app -= (sm->offset + prc->size) - (dc->offset + dc->length);
1192         }
1193       dc->completed += app;
1194
1195       /* do recursive download if option is set and either meta data
1196          says it is a directory or if no meta data is given AND filename 
1197          ends in '.gnd' (top-level case) */
1198       if (is_recursive_download (dc))
1199         GNUNET_FS_directory_list_contents (prc->size,
1200                                            pt,
1201                                            off,
1202                                            &trigger_recursive_download,
1203                                            dc);         
1204             
1205     }
1206   pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1207   pi.value.download.specifics.progress.data = pt;
1208   pi.value.download.specifics.progress.offset = sm->offset;
1209   pi.value.download.specifics.progress.data_len = prc->size;
1210   pi.value.download.specifics.progress.depth = sm->depth;
1211   GNUNET_FS_download_make_status_ (&pi, dc);
1212   GNUNET_assert (dc->completed <= dc->length);
1213   if (dc->completed == dc->length)
1214     {
1215 #if DEBUG_DOWNLOAD
1216       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1217                   "Download completed, truncating file to desired length %llu\n",
1218                   (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1219 #endif
1220       /* truncate file to size (since we store IBlocks at the end) */
1221       if (dc->filename != NULL)
1222         {
1223           if (0 != truncate (dc->filename,
1224                              GNUNET_ntohll (dc->uri->data.chk.file_length)))
1225             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1226                                       "truncate",
1227                                       dc->filename);
1228         }
1229       if (dc->job_queue != NULL)
1230         {
1231           GNUNET_FS_dequeue_ (dc->job_queue);
1232           dc->job_queue = NULL;
1233         }
1234       if (is_recursive_download (dc))
1235         full_recursive_download (dc);
1236       if (dc->child_head == NULL)
1237         {
1238           /* signal completion */
1239           pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
1240           GNUNET_FS_download_make_status_ (&pi, dc);
1241           if (dc->parent != NULL)
1242             check_completed (dc->parent);
1243         }
1244       GNUNET_assert (sm->depth == dc->treedepth);
1245     }
1246   if (sm->depth == dc->treedepth) 
1247     {
1248       GNUNET_FS_download_sync_ (dc);
1249       GNUNET_free (sm);      
1250       return GNUNET_YES;
1251     }
1252 #if DEBUG_DOWNLOAD
1253   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1254               "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1255               sm->depth,
1256               (unsigned long long) sm->offset);
1257 #endif
1258   GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1259   chk = (struct ContentHashKey*) pt;
1260   for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
1261     {
1262       off = compute_dblock_offset (sm->offset,
1263                                    sm->depth,
1264                                    dc->treedepth,
1265                                    i);
1266       if ( (off + DBLOCK_SIZE >= dc->offset) &&
1267            (off < dc->offset + dc->length) ) 
1268         schedule_block_download (dc,
1269                                  &chk[i],
1270                                  off,
1271                                  sm->depth + 1);
1272     }
1273   GNUNET_free (sm);
1274   GNUNET_FS_download_sync_ (dc);
1275   return GNUNET_YES;
1276
1277  signal_error:
1278   if (fh != NULL)
1279     GNUNET_DISK_file_close (fh);
1280   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1281   pi.value.download.specifics.error.message = dc->emsg;
1282   GNUNET_FS_download_make_status_ (&pi, dc);
1283   /* abort all pending requests */
1284   if (NULL != dc->th)
1285     {
1286       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1287       dc->th = NULL;
1288     }
1289   GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1290   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1291                                          &free_entry,
1292                                          NULL);
1293   dc->pending = NULL;
1294   dc->client = NULL;
1295   GNUNET_free (sm);
1296   GNUNET_FS_download_sync_ (dc);
1297   return GNUNET_NO;
1298 }
1299
1300
1301 /**
1302  * Process a download result.
1303  *
1304  * @param dc our download context
1305  * @param type type of the result
1306  * @param data the (encrypted) response
1307  * @param size size of data
1308  */
1309 static void
1310 process_result (struct GNUNET_FS_DownloadContext *dc,
1311                 enum GNUNET_BLOCK_Type type,
1312                 const void *data,
1313                 size_t size)
1314 {
1315   struct ProcessResultClosure prc;
1316
1317   prc.dc = dc;
1318   prc.data = data;
1319   prc.size = size;
1320   prc.type = type;
1321   prc.do_store = GNUNET_YES;
1322   GNUNET_CRYPTO_hash (data, size, &prc.query);
1323 #if DEBUG_DOWNLOAD
1324   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1325               "Received result for query `%s' from `%s'-service\n",
1326               GNUNET_h2s (&prc.query),
1327               "FS");
1328 #endif
1329   GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1330                                               &prc.query,
1331                                               &process_result_with_request,
1332                                               &prc);
1333 }
1334
1335
1336 /**
1337  * Type of a function to call when we receive a message
1338  * from the service.
1339  *
1340  * @param cls closure
1341  * @param msg message received, NULL on timeout or fatal error
1342  */
1343 static void 
1344 receive_results (void *cls,
1345                  const struct GNUNET_MessageHeader * msg)
1346 {
1347   struct GNUNET_FS_DownloadContext *dc = cls;
1348   const struct PutMessage *cm;
1349   uint16_t msize;
1350
1351   if ( (NULL == msg) ||
1352        (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
1353        (sizeof (struct PutMessage) > ntohs(msg->size)) )
1354     {
1355       GNUNET_break (msg == NULL);       
1356       try_reconnect (dc);
1357       return;
1358     }
1359   msize = ntohs(msg->size);
1360   cm = (const struct PutMessage*) msg;
1361   process_result (dc, 
1362                   ntohl (cm->type),
1363                   &cm[1],
1364                   msize - sizeof (struct PutMessage));
1365   if (dc->client == NULL)
1366     return; /* fatal error */
1367   /* continue receiving */
1368   GNUNET_CLIENT_receive (dc->client,
1369                          &receive_results,
1370                          dc,
1371                          GNUNET_TIME_UNIT_FOREVER_REL);
1372 }
1373
1374
1375
1376 /**
1377  * We're ready to transmit a search request to the
1378  * file-sharing service.  Do it.  If there is 
1379  * more than one request pending, try to send 
1380  * multiple or request another transmission.
1381  *
1382  * @param cls closure
1383  * @param size number of bytes available in buf
1384  * @param buf where the callee should write the message
1385  * @return number of bytes written to buf
1386  */
1387 static size_t
1388 transmit_download_request (void *cls,
1389                            size_t size, 
1390                            void *buf)
1391 {
1392   struct GNUNET_FS_DownloadContext *dc = cls;
1393   size_t msize;
1394   struct SearchMessage *sm;
1395
1396   dc->th = NULL;
1397   if (NULL == buf)
1398     {
1399 #if DEBUG_DOWNLOAD
1400       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1401                   "Transmitting download request failed, trying to reconnect\n");
1402 #endif
1403       try_reconnect (dc);
1404       return 0;
1405     }
1406   GNUNET_assert (size >= sizeof (struct SearchMessage));
1407   msize = 0;
1408   sm = buf;
1409   while ( (dc->pending != NULL) &&
1410           (size >= msize + sizeof (struct SearchMessage)) )
1411     {
1412 #if DEBUG_DOWNLOAD
1413       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1414                   "Transmitting download request for `%s' to `%s'-service\n",
1415                   GNUNET_h2s (&dc->pending->chk.query),
1416                   "FS");
1417 #endif
1418       memset (sm, 0, sizeof (struct SearchMessage));
1419       sm->header.size = htons (sizeof (struct SearchMessage));
1420       sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1421       if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
1422         sm->options = htonl (1);
1423       else
1424         sm->options = htonl (0);      
1425       if (dc->pending->depth == dc->treedepth)
1426         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
1427       else
1428         sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
1429       sm->anonymity_level = htonl (dc->anonymity);
1430       sm->target = dc->target.hashPubKey;
1431       sm->query = dc->pending->chk.query;
1432       dc->pending->is_pending = GNUNET_NO;
1433       dc->pending = dc->pending->next;
1434       msize += sizeof (struct SearchMessage);
1435       sm++;
1436     }
1437   if (dc->pending != NULL)
1438     dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1439                                                   sizeof (struct SearchMessage),
1440                                                   GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1441                                                   GNUNET_NO,
1442                                                   &transmit_download_request,
1443                                                   dc); 
1444   return msize;
1445 }
1446
1447
1448 /**
1449  * Reconnect to the FS service and transmit our queries NOW.
1450  *
1451  * @param cls our download context
1452  * @param tc unused
1453  */
1454 static void
1455 do_reconnect (void *cls,
1456               const struct GNUNET_SCHEDULER_TaskContext *tc)
1457 {
1458   struct GNUNET_FS_DownloadContext *dc = cls;
1459   struct GNUNET_CLIENT_Connection *client;
1460   
1461   dc->task = GNUNET_SCHEDULER_NO_TASK;
1462   client = GNUNET_CLIENT_connect (dc->h->sched,
1463                                   "fs",
1464                                   dc->h->cfg);
1465   if (NULL == client)
1466     {
1467       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1468                   "Connecting to `%s'-service failed, will try again.\n",
1469                   "FS");
1470       try_reconnect (dc);
1471       return;
1472     }
1473   dc->client = client;
1474   dc->th = GNUNET_CLIENT_notify_transmit_ready (client,
1475                                                 sizeof (struct SearchMessage),
1476                                                 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1477                                                 GNUNET_NO,
1478                                                 &transmit_download_request,
1479                                                 dc);  
1480   GNUNET_CLIENT_receive (client,
1481                          &receive_results,
1482                          dc,
1483                          GNUNET_TIME_UNIT_FOREVER_REL);
1484 }
1485
1486
1487 /**
1488  * Add entries that are not yet pending back to the pending list.
1489  *
1490  * @param cls our download context
1491  * @param key unused
1492  * @param entry entry of type "struct DownloadRequest"
1493  * @return GNUNET_OK
1494  */
1495 static int
1496 retry_entry (void *cls,
1497              const GNUNET_HashCode *key,
1498              void *entry)
1499 {
1500   struct GNUNET_FS_DownloadContext *dc = cls;
1501   struct DownloadRequest *dr = entry;
1502
1503   if (! dr->is_pending)
1504     {
1505       dr->next = dc->pending;
1506       dr->is_pending = GNUNET_YES;
1507       dc->pending = entry;
1508     }
1509   return GNUNET_OK;
1510 }
1511
1512
1513 /**
1514  * We've lost our connection with the FS service.
1515  * Re-establish it and re-transmit all of our
1516  * pending requests.
1517  *
1518  * @param dc download context that is having trouble
1519  */
1520 static void
1521 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1522 {
1523   
1524   if (NULL != dc->client)
1525     {
1526 #if DEBUG_DOWNLOAD
1527       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1528                   "Moving all requests back to pending list\n");
1529 #endif
1530       if (NULL != dc->th)
1531         {
1532           GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1533           dc->th = NULL;
1534         }
1535       GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1536                                              &retry_entry,
1537                                              dc);
1538       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1539       dc->client = NULL;
1540     }
1541 #if DEBUG_DOWNLOAD
1542   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1543               "Will try to reconnect in 1s\n");
1544 #endif
1545   dc->task
1546     = GNUNET_SCHEDULER_add_delayed (dc->h->sched,
1547                                     GNUNET_TIME_UNIT_SECONDS,
1548                                     &do_reconnect,
1549                                     dc);
1550 }
1551
1552
1553
1554 /**
1555  * We're allowed to ask the FS service for our blocks.  Start the download.
1556  *
1557  * @param cls the 'struct GNUNET_FS_DownloadContext'
1558  * @param client handle to use for communcation with FS (we must destroy it!)
1559  */
1560 static void
1561 activate_fs_download (void *cls,
1562                       struct GNUNET_CLIENT_Connection *client)
1563 {
1564   struct GNUNET_FS_DownloadContext *dc = cls;
1565   struct GNUNET_FS_ProgressInfo pi;
1566
1567 #if DEBUG_DOWNLOAD
1568   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1569               "Download activated\n");
1570 #endif
1571   GNUNET_assert (NULL != client);
1572   GNUNET_assert (dc->client == NULL);
1573   GNUNET_assert (dc->th == NULL);
1574   dc->client = client;
1575   GNUNET_CLIENT_receive (client,
1576                          &receive_results,
1577                          dc,
1578                          GNUNET_TIME_UNIT_FOREVER_REL);
1579   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1580   GNUNET_FS_download_make_status_ (&pi, dc);
1581   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1582                                          &retry_entry,
1583                                          dc);
1584 #if DEBUG_DOWNLOAD
1585   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1586               "Asking for transmission to FS service\n");
1587 #endif
1588   dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1589                                                 sizeof (struct SearchMessage),
1590                                                 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1591                                                 GNUNET_NO,
1592                                                 &transmit_download_request,
1593                                                 dc);    
1594   GNUNET_assert (dc->th != NULL);
1595 }
1596
1597
1598 /**
1599  * We must stop to ask the FS service for our blocks.  Pause the download.
1600  *
1601  * @param cls the 'struct GNUNET_FS_DownloadContext'
1602  */
1603 static void
1604 deactivate_fs_download (void *cls)
1605 {
1606   struct GNUNET_FS_DownloadContext *dc = cls;
1607   struct GNUNET_FS_ProgressInfo pi;
1608
1609 #if DEBUG_DOWNLOAD
1610   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1611               "Download deactivated\n");
1612 #endif  
1613   if (NULL != dc->th)
1614     {
1615       GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1616       dc->th = NULL;
1617     }
1618   if (NULL != dc->client)
1619     {
1620       GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1621       dc->client = NULL;
1622     }
1623   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1624   GNUNET_FS_download_make_status_ (&pi, dc);
1625 }
1626
1627
1628 /**
1629  * Create SUSPEND event for the given download operation
1630  * and then clean up our state (without stop signal).
1631  *
1632  * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
1633  */
1634 void
1635 GNUNET_FS_download_signal_suspend_ (void *cls)
1636 {
1637   struct GNUNET_FS_DownloadContext *dc = cls;
1638   struct GNUNET_FS_ProgressInfo pi;
1639   
1640   if (dc->top != NULL)
1641     GNUNET_FS_end_top (dc->h, dc->top);
1642   while (NULL != dc->child_head)
1643     GNUNET_FS_download_signal_suspend_ (dc->child_head);  
1644   if (dc->search != NULL)
1645     {
1646       dc->search->download = NULL;
1647       dc->search = NULL;
1648     }
1649   if (dc->job_queue != NULL)
1650     {
1651       GNUNET_FS_dequeue_ (dc->job_queue);
1652       dc->job_queue = NULL;
1653     }
1654   if (dc->parent != NULL)
1655     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
1656                                  dc->parent->child_tail,
1657                                  dc);  
1658   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
1659   GNUNET_FS_download_make_status_ (&pi, dc);
1660   if (GNUNET_SCHEDULER_NO_TASK != dc->task)
1661     GNUNET_SCHEDULER_cancel (dc->h->sched,
1662                              dc->task);
1663   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1664                                          &free_entry,
1665                                          NULL);
1666   GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1667   GNUNET_free_non_null (dc->filename);
1668   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
1669   GNUNET_FS_uri_destroy (dc->uri);
1670   GNUNET_free_non_null (dc->temp_filename);
1671   GNUNET_free_non_null (dc->serialization);
1672   GNUNET_free (dc);
1673 }
1674
1675
1676 /**
1677  * Download parts of a file.  Note that this will store
1678  * the blocks at the respective offset in the given file.  Also, the
1679  * download is still using the blocking of the underlying FS
1680  * encoding.  As a result, the download may *write* outside of the
1681  * given boundaries (if offset and length do not match the 32k FS
1682  * block boundaries). <p>
1683  *
1684  * This function should be used to focus a download towards a
1685  * particular portion of the file (optimization), not to strictly
1686  * limit the download to exactly those bytes.
1687  *
1688  * @param h handle to the file sharing subsystem
1689  * @param uri the URI of the file (determines what to download); CHK or LOC URI
1690  * @param meta known metadata for the file (can be NULL)
1691  * @param filename where to store the file, maybe NULL (then no file is
1692  *        created on disk and data must be grabbed from the callbacks)
1693  * @param tempname where to store temporary file data, not used if filename is non-NULL;
1694  *        can be NULL (in which case we will pick a name if needed); the temporary file
1695  *        may already exist, in which case we will try to use the data that is there and
1696  *        if it is not what is desired, will overwrite it
1697  * @param offset at what offset should we start the download (typically 0)
1698  * @param length how many bytes should be downloaded starting at offset
1699  * @param anonymity anonymity level to use for the download
1700  * @param options various options
1701  * @param cctx initial value for the client context for this download
1702  * @param parent parent download to associate this download with (use NULL
1703  *        for top-level downloads; useful for manually-triggered recursive downloads)
1704  * @return context that can be used to control this download
1705  */
1706 struct GNUNET_FS_DownloadContext *
1707 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
1708                           const struct GNUNET_FS_Uri *uri,
1709                           const struct GNUNET_CONTAINER_MetaData *meta,
1710                           const char *filename,
1711                           const char *tempname,
1712                           uint64_t offset,
1713                           uint64_t length,
1714                           uint32_t anonymity,
1715                           enum GNUNET_FS_DownloadOptions options,
1716                           void *cctx,
1717                           struct GNUNET_FS_DownloadContext *parent)
1718 {
1719   struct GNUNET_FS_ProgressInfo pi;
1720   struct GNUNET_FS_DownloadContext *dc;
1721
1722   GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
1723                  GNUNET_FS_uri_test_loc (uri) );
1724                  
1725   if ( (offset + length < offset) ||
1726        (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
1727     {      
1728       GNUNET_break (0);
1729       return NULL;
1730     }
1731 #if DEBUG_DOWNLOAD
1732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1733               "Starting download `%s' of %llu bytes\n",
1734               filename,
1735               (unsigned long long) length);
1736 #endif
1737   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
1738   dc->h = h;
1739   dc->parent = parent;
1740   if (parent != NULL)
1741     {
1742       GNUNET_CONTAINER_DLL_insert (parent->child_head,
1743                                    parent->child_tail,
1744                                    dc);
1745     }
1746   dc->uri = GNUNET_FS_uri_dup (uri);
1747   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
1748   dc->client_info = cctx;
1749   dc->start_time = GNUNET_TIME_absolute_get ();
1750   if (NULL != filename)
1751     {
1752       dc->filename = GNUNET_strdup (filename);
1753       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1754         GNUNET_DISK_file_size (filename,
1755                                &dc->old_file_size,
1756                                GNUNET_YES);
1757     }
1758   if (GNUNET_FS_uri_test_loc (dc->uri))
1759     GNUNET_assert (GNUNET_OK ==
1760                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
1761                                                         &dc->target));
1762   dc->offset = offset;
1763   dc->length = length;
1764   dc->anonymity = anonymity;
1765   dc->options = options;
1766   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
1767   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
1768   if ( (filename == NULL) &&
1769        (is_recursive_download (dc) ) )
1770     {
1771       if (tempname != NULL)
1772         dc->temp_filename = GNUNET_strdup (tempname);
1773       else
1774         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
1775     }
1776
1777 #if DEBUG_DOWNLOAD
1778   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1779               "Download tree has depth %u\n",
1780               dc->treedepth);
1781 #endif
1782   if (parent == NULL)
1783     {
1784       dc->top = GNUNET_FS_make_top (dc->h,
1785                                     &GNUNET_FS_download_signal_suspend_,
1786                                     dc);
1787     }
1788   pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1789   pi.value.download.specifics.start.meta = meta;
1790   GNUNET_FS_download_make_status_ (&pi, dc);
1791   schedule_block_download (dc, 
1792                            (dc->uri->type == chk) 
1793                            ? &dc->uri->data.chk.chk
1794                            : &dc->uri->data.loc.fi.chk,
1795                            0, 
1796                            1 /* 0 == CHK, 1 == top */); 
1797   GNUNET_FS_download_sync_ (dc);
1798   GNUNET_FS_download_start_downloading_ (dc);
1799   return dc;
1800 }
1801
1802
1803 /**
1804  * Download parts of a file based on a search result.  The download
1805  * will be associated with the search result (and the association
1806  * will be preserved when serializing/deserializing the state).
1807  * If the search is stopped, the download will not be aborted but
1808  * be 'promoted' to a stand-alone download.
1809  *
1810  * As with the other download function, this will store
1811  * the blocks at the respective offset in the given file.  Also, the
1812  * download is still using the blocking of the underlying FS
1813  * encoding.  As a result, the download may *write* outside of the
1814  * given boundaries (if offset and length do not match the 32k FS
1815  * block boundaries). <p>
1816  *
1817  * The given range can be used to focus a download towards a
1818  * particular portion of the file (optimization), not to strictly
1819  * limit the download to exactly those bytes.
1820  *
1821  * @param h handle to the file sharing subsystem
1822  * @param sr the search result to use for the download (determines uri and
1823  *        meta data and associations)
1824  * @param filename where to store the file, maybe NULL (then no file is
1825  *        created on disk and data must be grabbed from the callbacks)
1826  * @param tempname where to store temporary file data, not used if filename is non-NULL;
1827  *        can be NULL (in which case we will pick a name if needed); the temporary file
1828  *        may already exist, in which case we will try to use the data that is there and
1829  *        if it is not what is desired, will overwrite it
1830  * @param offset at what offset should we start the download (typically 0)
1831  * @param length how many bytes should be downloaded starting at offset
1832  * @param anonymity anonymity level to use for the download
1833  * @param options various download options
1834  * @param cctx initial value for the client context for this download
1835  * @return context that can be used to control this download
1836  */
1837 struct GNUNET_FS_DownloadContext *
1838 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
1839                                       struct GNUNET_FS_SearchResult *sr,
1840                                       const char *filename,
1841                                       const char *tempname,
1842                                       uint64_t offset,
1843                                       uint64_t length,
1844                                       uint32_t anonymity,
1845                                       enum GNUNET_FS_DownloadOptions options,
1846                                       void *cctx)
1847 {
1848   struct GNUNET_FS_ProgressInfo pi;
1849   struct GNUNET_FS_DownloadContext *dc;
1850
1851   if ( (sr == NULL) ||
1852        (sr->download != NULL) )
1853     {
1854       GNUNET_break (0);
1855       return NULL;
1856     }
1857   GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
1858                  GNUNET_FS_uri_test_loc (sr->uri) );             
1859   if ( (offset + length < offset) ||
1860        (offset + length > sr->uri->data.chk.file_length) )
1861     {      
1862       GNUNET_break (0);
1863       return NULL;
1864     }
1865 #if DEBUG_DOWNLOAD
1866   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1867               "Starting download `%s' of %llu bytes\n",
1868               filename,
1869               (unsigned long long) length);
1870 #endif
1871   dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
1872   dc->h = h;
1873   dc->search = sr;
1874   sr->download = dc;
1875   if (sr->probe_ctx != NULL)
1876     {
1877       GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
1878       sr->probe_ctx = NULL;      
1879     }
1880   dc->uri = GNUNET_FS_uri_dup (sr->uri);
1881   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
1882   dc->client_info = cctx;
1883   dc->start_time = GNUNET_TIME_absolute_get ();
1884   if (NULL != filename)
1885     {
1886       dc->filename = GNUNET_strdup (filename);
1887       if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1888         GNUNET_DISK_file_size (filename,
1889                                &dc->old_file_size,
1890                                GNUNET_YES);
1891     }
1892   if (GNUNET_FS_uri_test_loc (dc->uri))
1893     GNUNET_assert (GNUNET_OK ==
1894                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
1895                                                         &dc->target));
1896   dc->offset = offset;
1897   dc->length = length;
1898   dc->anonymity = anonymity;
1899   dc->options = options;
1900   dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
1901   dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
1902   if ( (filename == NULL) &&
1903        (is_recursive_download (dc) ) )
1904     {
1905       if (tempname != NULL)
1906         dc->temp_filename = GNUNET_strdup (tempname);
1907       else
1908         dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");    
1909     }
1910
1911 #if DEBUG_DOWNLOAD
1912   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913               "Download tree has depth %u\n",
1914               dc->treedepth);
1915 #endif
1916   pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1917   pi.value.download.specifics.start.meta = dc->meta;
1918   GNUNET_FS_download_make_status_ (&pi, dc);
1919   schedule_block_download (dc, 
1920                            &dc->uri->data.chk.chk,
1921                            0, 
1922                            1 /* 0 == CHK, 1 == top */); 
1923   GNUNET_FS_download_sync_ (dc);
1924   GNUNET_FS_download_start_downloading_ (dc);
1925   return dc;  
1926 }
1927
1928
1929 /**
1930  * Start the downloading process (by entering the queue).
1931  *
1932  * @param dc our download context
1933  */
1934 void
1935 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
1936 {
1937   GNUNET_assert (dc->job_queue == NULL);
1938   dc->job_queue = GNUNET_FS_queue_ (dc->h, 
1939                                     &activate_fs_download,
1940                                     &deactivate_fs_download,
1941                                     dc,
1942                                     (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
1943 }
1944
1945
1946 /**
1947  * Stop a download (aborts if download is incomplete).
1948  *
1949  * @param dc handle for the download
1950  * @param do_delete delete files of incomplete downloads
1951  */
1952 void
1953 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
1954                          int do_delete)
1955 {
1956   struct GNUNET_FS_ProgressInfo pi;
1957   int have_children;
1958
1959   if (dc->top != NULL)
1960     GNUNET_FS_end_top (dc->h, dc->top);
1961   if (dc->search != NULL)
1962     {
1963       dc->search->download = NULL;
1964       dc->search = NULL;
1965     }
1966   if (dc->job_queue != NULL)
1967     {
1968       GNUNET_FS_dequeue_ (dc->job_queue);
1969       dc->job_queue = NULL;
1970     }
1971   have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
1972   while (NULL != dc->child_head)
1973     GNUNET_FS_download_stop (dc->child_head, 
1974                              do_delete);
1975   if (dc->parent != NULL)
1976     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
1977                                  dc->parent->child_tail,
1978                                  dc);  
1979   if (dc->serialization != NULL)
1980     GNUNET_FS_remove_sync_file_ (dc->h,
1981                                  ( (dc->parent != NULL)  || (dc->search != NULL) )
1982                                  ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
1983                                  : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD , 
1984                                  dc->serialization);
1985   if ( (GNUNET_YES == have_children) &&
1986        (dc->parent == NULL) )
1987     GNUNET_FS_remove_sync_dir_ (dc->h, 
1988                                 (dc->search != NULL) 
1989                                 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD 
1990                                 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
1991                                 dc->serialization);  
1992   pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
1993   GNUNET_FS_download_make_status_ (&pi, dc);
1994   if (GNUNET_SCHEDULER_NO_TASK != dc->task)
1995     GNUNET_SCHEDULER_cancel (dc->h->sched,
1996                              dc->task);
1997   GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1998                                          &free_entry,
1999                                          NULL);
2000   GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2001   if (dc->filename != NULL)
2002     {
2003       if ( (dc->completed != dc->length) &&
2004            (GNUNET_YES == do_delete) )
2005         {
2006           if (0 != UNLINK (dc->filename))
2007             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2008                                       "unlink",
2009                                       dc->filename);
2010         }
2011       GNUNET_free (dc->filename);
2012     }
2013   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2014   GNUNET_FS_uri_destroy (dc->uri);
2015   if (NULL != dc->temp_filename)
2016     {
2017       if (0 != UNLINK (dc->temp_filename))
2018         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2019                                   "unlink",
2020                                   dc->temp_filename);
2021       GNUNET_free (dc->temp_filename);
2022     }
2023   GNUNET_free_non_null (dc->serialization);
2024   GNUNET_free (dc);
2025 }
2026
2027 /* end of fs_download.c */