cade94a799baffc4c752f694d424d6da7ac903a3
[oweals/gnunet.git] / src / fs / fs_file_information.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 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 /**
22  * @file fs/fs_file_information.c
23  * @brief  Manage information for publishing directory hierarchies
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - metadata filename clean up code
28  * - metadata/ksk generation for directories from contained files
29  */
30 #include "platform.h"
31 #include <extractor.h>
32 #include "gnunet_fs_service.h"
33 #include "fs_api.h"
34 #include "fs_tree.h"
35
36
37 /**
38  * Add meta data that libextractor finds to our meta data
39  * container.
40  *
41  * @param cls closure, our meta data container
42  * @param plugin_name name of the plugin that produced this value;
43  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
44  *        used in the main libextractor library and yielding
45  *        meta data).
46  * @param type libextractor-type describing the meta data
47  * @param format basic format information about data
48  * @param data_mime_type mime-type of data (not of the original file);
49  *        can be NULL (if mime-type is not known)
50  * @param data actual meta-data found
51  * @param data_len number of bytes in data
52  * @return always 0 to continue extracting
53  */
54 static int
55 add_to_md (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
56            enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
57            const char *data, size_t data_len)
58 {
59   struct GNUNET_CONTAINER_MetaData *md = cls;
60
61   (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
62                                             data_mime_type, data, data_len);
63   return 0;
64 }
65
66
67 /**
68  * Extract meta-data from a file.
69  *
70  * @return GNUNET_SYSERR on error, otherwise the number
71  *   of meta-data items obtained
72  */
73 int
74 GNUNET_FS_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData *md,
75                                        const char *filename,
76                                        struct EXTRACTOR_PluginList *extractors)
77 {
78   int old;
79
80   if (filename == NULL)
81     return GNUNET_SYSERR;
82   if (extractors == NULL)
83     return 0;
84   old = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
85   GNUNET_assert (old >= 0);
86   EXTRACTOR_extract (extractors, filename, NULL, 0, &add_to_md, md);
87   return (GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL) - old);
88 }
89
90
91
92 /**
93  * Obtain the name under which this file information
94  * structure is stored on disk.  Only works for top-level
95  * file information structures.
96  *
97  * @param s structure to get the filename for
98  * @return NULL on error, otherwise filename that
99  *         can be passed to "GNUNET_FS_file_information_recover"
100  *         to read this fi-struct from disk.
101  */
102 const char *
103 GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
104 {
105   if (NULL != s->dir)
106     return NULL;
107   return s->serialization;
108 }
109
110 /**
111  * Obtain the filename from the file information structure.
112  *
113  * @param s structure to get the filename for
114  * @return "filename" field of the structure (can be NULL)
115  */
116 const char *
117 GNUNET_FS_file_information_get_filename (struct GNUNET_FS_FileInformation *s)
118 {
119   return s->filename;
120 }
121
122
123 /**
124  * Set the filename in the file information structure.
125  * If filename was already set, frees it before setting the new one.
126  * Makes a copy of the argument.
127  *
128  * @param s structure to get the filename for
129  * @param filename filename to set
130  */
131 void
132 GNUNET_FS_file_information_set_filename (struct GNUNET_FS_FileInformation *s,
133                                          const char *filename)
134 {
135   GNUNET_free_non_null (s->filename);
136   if (filename)
137     s->filename = GNUNET_strdup (filename);
138   else
139     s->filename = NULL;
140 }
141
142 /**
143  * Create an entry for a file in a publish-structure.
144  *
145  * @param h handle to the file sharing subsystem
146  * @param client_info initial value for the client-info value for this entry
147  * @param filename name of the file or directory to publish
148  * @param keywords under which keywords should this file be available
149  *         directly; can be NULL
150  * @param meta metadata for the file
151  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
152  *                GNUNET_SYSERR for simulation
153  * @param bo block options
154  * @return publish structure entry for the file
155  */
156 struct GNUNET_FS_FileInformation *
157 GNUNET_FS_file_information_create_from_file (struct GNUNET_FS_Handle *h,
158                                              void *client_info,
159                                              const char *filename,
160                                              const struct GNUNET_FS_Uri
161                                              *keywords,
162                                              const struct
163                                              GNUNET_CONTAINER_MetaData *meta,
164                                              int do_index,
165                                              const struct GNUNET_FS_BlockOptions
166                                              *bo)
167 {
168   struct FileInfo *fi;
169   struct stat sbuf;
170   struct GNUNET_FS_FileInformation *ret;
171   const char *fn;
172   const char *ss;
173
174 #if WINDOWS
175   char fn_conv[MAX_PATH];
176 #endif
177
178   if (0 != STAT (filename, &sbuf))
179   {
180     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
181     return NULL;
182   }
183   fi = GNUNET_FS_make_file_reader_context_ (filename);
184   if (fi == NULL)
185   {
186     GNUNET_break (0);
187     return NULL;
188   }
189   ret =
190       GNUNET_FS_file_information_create_from_reader (h, client_info,
191                                                      sbuf.st_size,
192                                                      &GNUNET_FS_data_reader_file_,
193                                                      fi, keywords, meta,
194                                                      do_index, bo);
195   if (ret == NULL)
196     return NULL;
197   ret->h = h;
198   ret->filename = GNUNET_strdup (filename);
199 #if !WINDOWS
200   fn = filename;
201 #else
202   plibc_conv_to_win_path (filename, fn_conv);
203   fn = fn_conv;
204 #endif
205   while (NULL != (ss = strstr (fn, DIR_SEPARATOR_STR)))
206     fn = ss + 1;
207 #if !WINDOWS
208   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
209                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
210                                      EXTRACTOR_METAFORMAT_C_STRING,
211                                      "text/plain", fn, strlen (fn) + 1);
212 #else
213   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
214                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
215                                      EXTRACTOR_METAFORMAT_UTF8,
216                                      "text/plain", fn, strlen (fn) + 1);
217 #endif
218   return ret;
219 }
220
221
222 /**
223  * Create an entry for a file in a publish-structure.
224  *
225  * @param h handle to the file sharing subsystem
226  * @param client_info initial value for the client-info value for this entry
227  * @param length length of the file
228  * @param data data for the file (should not be used afterwards by
229  *        the caller; callee will "free")
230  * @param keywords under which keywords should this file be available
231  *         directly; can be NULL
232  * @param meta metadata for the file
233  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
234  *                GNUNET_SYSERR for simulation
235  * @param bo block options
236  * @return publish structure entry for the file
237  */
238 struct GNUNET_FS_FileInformation *
239 GNUNET_FS_file_information_create_from_data (struct GNUNET_FS_Handle *h,
240                                              void *client_info, uint64_t length,
241                                              void *data,
242                                              const struct GNUNET_FS_Uri
243                                              *keywords,
244                                              const struct
245                                              GNUNET_CONTAINER_MetaData *meta,
246                                              int do_index,
247                                              const struct GNUNET_FS_BlockOptions
248                                              *bo)
249 {
250   if (GNUNET_YES == do_index)
251   {
252     GNUNET_break (0);
253     return NULL;
254   }
255   return GNUNET_FS_file_information_create_from_reader (h, client_info, length,
256                                                         &GNUNET_FS_data_reader_copy_,
257                                                         data, keywords, meta,
258                                                         do_index, bo);
259 }
260
261
262 /**
263  * Create an entry for a file in a publish-structure.
264  *
265  * @param h handle to the file sharing subsystem
266  * @param client_info initial value for the client-info value for this entry
267  * @param length length of the file
268  * @param reader function that can be used to obtain the data for the file
269  * @param reader_cls closure for "reader"
270  * @param keywords under which keywords should this file be available
271  *         directly; can be NULL
272  * @param meta metadata for the file
273  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
274  *                GNUNET_SYSERR for simulation
275  * @param bo block options
276  * @return publish structure entry for the file
277  */
278 struct GNUNET_FS_FileInformation *
279 GNUNET_FS_file_information_create_from_reader (struct GNUNET_FS_Handle *h,
280                                                void *client_info,
281                                                uint64_t length,
282                                                GNUNET_FS_DataReader reader,
283                                                void *reader_cls,
284                                                const struct GNUNET_FS_Uri
285                                                *keywords,
286                                                const struct
287                                                GNUNET_CONTAINER_MetaData *meta,
288                                                int do_index,
289                                                const struct
290                                                GNUNET_FS_BlockOptions *bo)
291 {
292   struct GNUNET_FS_FileInformation *ret;
293
294   if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_))
295   {
296     GNUNET_break (0);
297     return NULL;
298   }
299   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
300   ret->h = h;
301   ret->client_info = client_info;
302   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
303   if (ret->meta == NULL)
304     ret->meta = GNUNET_CONTAINER_meta_data_create ();
305   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
306   ret->data.file.reader = reader;
307   ret->data.file.reader_cls = reader_cls;
308   ret->data.file.do_index = do_index;
309   ret->data.file.file_size = length;
310   ret->bo = *bo;
311   return ret;
312 }
313
314
315 /**
316  * Closure for "dir_scan_cb".
317  */
318 struct DirScanCls
319 {
320   /**
321    * Metadata extractors to use.
322    */
323   struct EXTRACTOR_PluginList *extractors;
324
325   /**
326    * Master context.
327    */
328   struct GNUNET_FS_Handle *h;
329
330   /**
331    * Function to call on each directory entry.
332    */
333   GNUNET_FS_FileProcessor proc;
334
335   /**
336    * Closure for proc.
337    */
338   void *proc_cls;
339
340   /**
341    * Scanner to use for subdirectories.
342    */
343   GNUNET_FS_DirectoryScanner scanner;
344
345   /**
346    * Closure for scanner.
347    */
348   void *scanner_cls;
349
350   /**
351    * Set to an error message (if any).
352    */
353   char *emsg;
354
355   /**
356    * Block options.
357    */
358   const struct GNUNET_FS_BlockOptions *bo;
359
360   /**
361    * Should files be indexed?
362    */
363   int do_index;
364
365 };
366
367
368 /**
369  * Function called on each entry in a file to cause
370  * default-publishing.
371  *
372  * @param cls closure (struct DirScanCls)
373  * @param filename name of the file to be published
374  * @return GNUNET_OK on success, GNUNET_SYSERR to abort
375  */
376 static int
377 dir_scan_cb (void *cls, const char *filename)
378 {
379   struct DirScanCls *dsc = cls;
380   struct stat sbuf;
381   struct GNUNET_FS_FileInformation *fi;
382   struct GNUNET_FS_Uri *keywords;
383   struct GNUNET_CONTAINER_MetaData *meta;
384
385   if (0 != STAT (filename, &sbuf))
386   {
387     GNUNET_asprintf (&dsc->emsg, _("`%s' failed on file `%s': %s"), "stat",
388                      filename, STRERROR (errno));
389     return GNUNET_SYSERR;
390   }
391   if (S_ISDIR (sbuf.st_mode))
392   {
393     fi = GNUNET_FS_file_information_create_from_directory (dsc->h, NULL,
394                                                            filename,
395                                                            dsc->scanner,
396                                                            dsc->scanner_cls,
397                                                            dsc->do_index,
398                                                            dsc->bo, &dsc->emsg);
399     if (NULL == fi)
400     {
401       GNUNET_assert (NULL != dsc->emsg);
402       return GNUNET_SYSERR;
403     }
404   }
405   else
406   {
407     meta = GNUNET_CONTAINER_meta_data_create ();
408     GNUNET_FS_meta_data_extract_from_file (meta, filename, dsc->extractors);
409     keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
410     fi = GNUNET_FS_file_information_create_from_file (dsc->h, NULL, filename,
411                                                       keywords, meta,
412                                                       dsc->do_index, dsc->bo);
413     GNUNET_CONTAINER_meta_data_destroy (meta);
414     GNUNET_FS_uri_destroy (keywords);
415   }
416   dsc->proc (dsc->proc_cls, filename, fi);
417   return GNUNET_OK;
418 }
419
420
421 /**
422  * Simple, useful default implementation of a directory scanner
423  * (GNUNET_FS_DirectoryScanner).  This implementation expects to get a
424  * UNIX filename, will publish all files in the directory except hidden
425  * files (those starting with a ".").  Metadata will be extracted
426  * using GNU libextractor; the specific list of plugins should be
427  * specified in "cls", passing NULL will disable (!)  metadata
428  * extraction.  Keywords will be derived from the metadata and be
429  * subject to default canonicalization.  This is strictly a
430  * convenience function.
431  *
432  * @param cls must be of type "struct EXTRACTOR_Extractor*"
433  * @param h handle to the file sharing subsystem
434  * @param dirname name of the directory to scan
435  * @param do_index should files be indexed or inserted
436  * @param bo block options
437  * @param proc function called on each entry
438  * @param proc_cls closure for proc
439  * @param emsg where to store an error message (on errors)
440  * @return GNUNET_OK on success
441  */
442 int
443 GNUNET_FS_directory_scanner_default (void *cls, struct GNUNET_FS_Handle *h,
444                                      const char *dirname, int do_index,
445                                      const struct GNUNET_FS_BlockOptions *bo,
446                                      GNUNET_FS_FileProcessor proc,
447                                      void *proc_cls, char **emsg)
448 {
449   struct EXTRACTOR_PluginList *ex = cls;
450   struct DirScanCls dsc;
451
452   dsc.h = h;
453   dsc.extractors = ex;
454   dsc.proc = proc;
455   dsc.proc_cls = proc_cls;
456   dsc.scanner = &GNUNET_FS_directory_scanner_default;
457   dsc.scanner_cls = cls;
458   dsc.do_index = do_index;
459   dsc.bo = bo;
460   if (-1 == GNUNET_DISK_directory_scan (dirname, &dir_scan_cb, &dsc))
461   {
462     GNUNET_assert (NULL != dsc.emsg);
463     *emsg = dsc.emsg;
464     return GNUNET_SYSERR;
465   }
466   return GNUNET_OK;
467 }
468
469
470 /**
471  * Aggregate information we keep for meta data in each directory.
472  */
473 struct MetaValueInformation
474 {
475
476   /**
477    * Mime-type of data.
478    */
479   const char *mime_type;
480
481   /**
482    * The actual meta data.
483    */
484   const char *data;
485
486   /**
487    * Number of bytes in 'data'.
488    */
489   size_t data_size;
490
491   /**
492    * Type of the meta data.
493    */
494   enum EXTRACTOR_MetaType type;
495
496   /**
497    * Format of the meta data.
498    */
499   enum EXTRACTOR_MetaFormat format;
500
501   /**
502    * How often does this meta value occur in this directory?
503    */
504   unsigned int frequency;
505
506 };
507
508
509 /**
510  * Type of a function that libextractor calls for each
511  * meta data item found.
512  *
513  * @param cls the container multihashmap to update
514  * @param plugin_name name of the plugin that produced this value;
515  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
516  *        used in the main libextractor library and yielding
517  *        meta data).
518  * @param type libextractor-type describing the meta data
519  * @param format basic format information about data
520  * @param data_mime_type mime-type of data (not of the original file);
521  *        can be NULL (if mime-type is not known)
522  * @param data actual meta-data found
523  * @param data_len number of bytes in data
524  * @return 0 to continue extracting / iterating
525  */
526 static int
527 update_metamap (void *cls, const char *plugin_name,
528                 enum EXTRACTOR_MetaType type, enum EXTRACTOR_MetaFormat format,
529                 const char *data_mime_type, const char *data, size_t data_len)
530 {
531   struct GNUNET_CONTAINER_MultiHashMap *map = cls;
532   GNUNET_HashCode key;
533   struct MetaValueInformation *mvi;
534
535   GNUNET_CRYPTO_hash (data, data_len, &key);
536   mvi = GNUNET_CONTAINER_multihashmap_get (map, &key);
537   if (mvi == NULL)
538   {
539     mvi = GNUNET_malloc (sizeof (struct MetaValueInformation));
540     mvi->mime_type = data_mime_type;
541     mvi->data = data;
542     mvi->data_size = data_len;
543     mvi->type = type;
544     mvi->format = format;
545     GNUNET_CONTAINER_multihashmap_put (map, &key, mvi,
546                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
547   }
548   mvi->frequency++;
549   return 0;
550 }
551
552
553 /**
554  * Aggregate information we keep for keywords in each directory.
555  */
556 struct KeywordInformation
557 {
558
559   /**
560    * Mime-type of keyword.
561    */
562   const char *keyword;
563
564   /**
565    * How often does this meta value occur in this directory?
566    */
567   unsigned int frequency;
568
569 };
570
571
572 /**
573  * Closure for dirproc function.
574  */
575 struct EntryProcCls
576 {
577   /**
578    * Linked list of directory entries that is being
579    * created.
580    */
581   struct GNUNET_FS_FileInformation *entries;
582
583   /**
584    * Map describing the meta data for all entries in the
585    * directory.  Keys are the hash of the meta-value,
586    * values are of type 'struct MetaValueInformation'.
587    */
588   struct GNUNET_CONTAINER_MultiHashMap *metamap;
589
590   /**
591    * Map describing the keywords for all entries in the
592    * directory.  Keys are the hash of the keyword,
593    * values are of type 'struct KeywordInformation'.
594    */
595   struct GNUNET_CONTAINER_MultiHashMap *keywordmap;
596
597   /**
598    * Number of entries in 'entries'.
599    */
600   unsigned int count;
601
602 };
603
604
605 /**
606  * Function that processes a directory entry that
607  * was obtained from the scanner.  Adds each entry to
608  * the directory and computes directroy meta map.
609  *
610  * @param cls our closure
611  * @param filename name of the file (unused, why there???)
612  * @param fi information for publishing the file
613  */
614 static void
615 dirproc_add (void *cls, const char *filename,
616              struct GNUNET_FS_FileInformation *fi)
617 {
618   struct EntryProcCls *dc = cls;
619   unsigned int i;
620   const char *kw;
621   struct KeywordInformation *ki;
622   GNUNET_HashCode key;
623
624   GNUNET_assert (fi->next == NULL);
625   GNUNET_assert (fi->dir == NULL);
626   fi->next = dc->entries;
627   dc->entries = fi;
628   dc->count++;
629   if (NULL != fi->meta)
630     GNUNET_CONTAINER_meta_data_iterate (fi->meta, &update_metamap, dc->metamap);
631   for (i = 0; i < fi->keywords->data.ksk.keywordCount; i++)
632   {
633     kw = fi->keywords->data.ksk.keywords[i];
634     GNUNET_CRYPTO_hash (kw, strlen (kw), &key);
635     ki = GNUNET_CONTAINER_multihashmap_get (dc->keywordmap, &key);
636     if (ki == NULL)
637     {
638       ki = GNUNET_malloc (sizeof (struct KeywordInformation));
639       ki->keyword = &kw[1];
640       GNUNET_CONTAINER_multihashmap_put (dc->keywordmap, &key, ki,
641                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
642     }
643     ki->frequency++;
644   }
645 }
646
647
648 /**
649  * Closure for 'compute_directory_metadata'.
650  */
651 struct ComputeDirectoryMetadataContext
652 {
653   /**
654    * Where to store the extracted keywords.
655    */
656   struct GNUNET_FS_Uri *ksk;
657
658   /**
659    * Where to store the extracted meta data.
660    */
661   struct GNUNET_CONTAINER_MetaData *meta;
662
663   /**
664    * Threshold to apply for adding meta data.
665    */
666   unsigned int threshold;
667 };
668
669
670 /**
671  * Add metadata that occurs in more than the threshold entries of the
672  * directory to the directory itself.  For example, if most files in a
673  * directory are of the same mime-type, the directory should have that
674  * mime-type as a keyword.
675  *
676  * @param cls the 'struct ComputeDirectoryMetadataContext'
677  * @param key unused
678  * @param value the 'struct MetaValueInformation' (to be freed as well)
679  * @return GNUNET_OK
680  */
681 static int
682 compute_directory_metadata (void *cls, const GNUNET_HashCode * key, void *value)
683 {
684   struct ComputeDirectoryMetadataContext *cdmc = cls;
685   struct MetaValueInformation *mvi = value;
686
687   if (mvi->frequency > cdmc->threshold)
688   {
689     if (mvi->type != EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
690       (void) GNUNET_CONTAINER_meta_data_insert (cdmc->meta, "<children>",
691                                                 mvi->type, mvi->format,
692                                                 mvi->mime_type, mvi->data,
693                                                 mvi->data_size);
694     if ((mvi->format == EXTRACTOR_METAFORMAT_UTF8) ||
695         (mvi->format == EXTRACTOR_METAFORMAT_C_STRING))
696       GNUNET_FS_uri_ksk_add_keyword (cdmc->ksk, mvi->data, GNUNET_NO);
697   }
698   GNUNET_free (mvi);
699   return GNUNET_OK;
700 }
701
702
703 /**
704  * Add keywords that occur in more than the threshold entries of the
705  * directory to the directory itself.
706  *
707  * @param cls the 'struct ComputeDirectoryMetadataContext'
708  * @param key unused
709  * @param value the 'struct Keywordnformation' (to be freed as well)
710  * @return GNUNET_OK
711  */
712 static int
713 compute_directory_keywords (void *cls, const GNUNET_HashCode * key, void *value)
714 {
715   struct ComputeDirectoryMetadataContext *cdmc = cls;
716   struct KeywordInformation *ki = value;
717
718   if (ki->frequency > cdmc->threshold)
719     (void) GNUNET_FS_uri_ksk_add_keyword (cdmc->ksk, ki->keyword, GNUNET_NO);
720   GNUNET_free (ki);
721   return GNUNET_OK;
722 }
723
724
725 /**
726  * Create a publish-structure from an existing file hierarchy, inferring
727  * and organizing keywords and metadata as much as possible.  This
728  * function primarily performs the recursive build and re-organizes
729  * keywords and metadata; for automatically getting metadata
730  * extraction, scanning of directories and creation of the respective
731  * GNUNET_FS_FileInformation entries the default scanner should be
732  * passed (GNUNET_FS_directory_scanner_default).  This is strictly a
733  * convenience function.
734  *
735  * @param h handle to the file sharing subsystem
736  * @param client_info initial value for the client-info value for this entry
737  * @param filename name of the top-level file or directory
738  * @param scanner function used to get a list of files in a directory
739  * @param scanner_cls closure for scanner
740  * @param do_index should files in the hierarchy be indexed?
741  * @param bo block options
742  * @param emsg where to store an error message
743  * @return publish structure entry for the directory, NULL on error
744  */
745 struct GNUNET_FS_FileInformation *
746 GNUNET_FS_file_information_create_from_directory (struct GNUNET_FS_Handle *h,
747                                                   void *client_info,
748                                                   const char *filename,
749                                                   GNUNET_FS_DirectoryScanner
750                                                   scanner, void *scanner_cls,
751                                                   int do_index,
752                                                   const struct
753                                                   GNUNET_FS_BlockOptions *bo,
754                                                   char **emsg)
755 {
756   struct GNUNET_FS_FileInformation *ret;
757   struct ComputeDirectoryMetadataContext cdmc;
758   struct EntryProcCls dc;
759   const char *fn;
760   const char *ss;
761   char *dn;
762   struct GNUNET_FS_FileInformation *epos;
763   unsigned int i;
764   const char *kw;
765
766   dc.entries = NULL;
767   dc.count = 0;
768   dc.metamap = GNUNET_CONTAINER_multihashmap_create (64);
769   dc.keywordmap = GNUNET_CONTAINER_multihashmap_create (64);
770   /* update children to point to directory and generate statistics
771    * on all meta data in children */
772   scanner (scanner_cls, h, filename, do_index, bo, &dirproc_add, &dc, emsg);
773   cdmc.meta = GNUNET_CONTAINER_meta_data_create ();
774   cdmc.ksk = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
775   cdmc.ksk->type = ksk;
776   cdmc.threshold = 1 + dc.count / 2;    /* 50% threshold for now */
777   GNUNET_FS_meta_data_make_directory (cdmc.meta);
778   GNUNET_CONTAINER_multihashmap_iterate (dc.metamap,
779                                          &compute_directory_metadata, &cdmc);
780   GNUNET_CONTAINER_multihashmap_iterate (dc.keywordmap,
781                                          &compute_directory_keywords, &cdmc);
782   GNUNET_CONTAINER_multihashmap_destroy (dc.metamap);
783   GNUNET_CONTAINER_multihashmap_destroy (dc.keywordmap);
784
785   /* remove keywords in children that are already in the
786    * parent */
787   for (epos = dc.entries; NULL != epos; epos = epos->next)
788   {
789     for (i = 0; i < cdmc.ksk->data.ksk.keywordCount; i++)
790     {
791       kw = cdmc.ksk->data.ksk.keywords[i];
792       GNUNET_FS_uri_ksk_remove_keyword (epos->keywords, &kw[1]);
793     }
794   }
795   GNUNET_FS_uri_ksk_add_keyword (cdmc.ksk, GNUNET_FS_DIRECTORY_MIME, GNUNET_NO);
796   ret =
797       GNUNET_FS_file_information_create_empty_directory (h, client_info, cdmc.ksk,
798                                                          cdmc.meta, bo, filename);
799   GNUNET_CONTAINER_meta_data_destroy (cdmc.meta);
800   GNUNET_FS_uri_destroy (cdmc.ksk);
801   ret->data.dir.entries = dc.entries;
802   while (dc.entries != NULL)
803   {
804     dc.entries->dir = ret;
805     dc.entries = dc.entries->next;
806   }
807   fn = filename;
808   while ((NULL != (ss = strstr (fn, DIR_SEPARATOR_STR))) && (strlen (ss) > 1))
809     fn = ss + 1;
810   GNUNET_asprintf (&dn, "%s/", fn);
811 #if !WINDOWS
812   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
813                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
814                                      EXTRACTOR_METAFORMAT_C_STRING,
815                                      "text/plain", dn, strlen (dn) + 1);
816 #else
817   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
818                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
819                                      EXTRACTOR_METAFORMAT_UTF8,
820                                      "text/plain", dn, strlen (dn) + 1);
821 #endif
822   GNUNET_free (dn);
823  return ret;
824 }
825
826
827 /**
828  * Test if a given entry represents a directory.
829  *
830  * @param ent check if this FI represents a directory
831  * @return GNUNET_YES if so, GNUNET_NO if not
832  */
833 int
834 GNUNET_FS_file_information_is_directory (const struct GNUNET_FS_FileInformation
835                                          *ent)
836 {
837   return ent->is_directory;
838 }
839
840
841 /**
842  * Create an entry for an empty directory in a publish-structure.
843  * This function should be used by applications for which the
844  * use of "GNUNET_FS_file_information_create_from_directory"
845  * is not appropriate.
846  *
847  * @param h handle to the file sharing subsystem
848  * @param client_info initial value for the client-info value for this entry
849  * @param meta metadata for the directory
850  * @param keywords under which keywords should this directory be available
851  *         directly; can be NULL
852  * @param bo block options
853  * @param filename name of the directory; can be NULL
854  * @return publish structure entry for the directory , NULL on error
855  */
856 struct GNUNET_FS_FileInformation *
857 GNUNET_FS_file_information_create_empty_directory (struct GNUNET_FS_Handle *h,
858                                                    void *client_info,
859                                                    const struct GNUNET_FS_Uri
860                                                    *keywords,
861                                                    const struct
862                                                    GNUNET_CONTAINER_MetaData
863                                                    *meta,
864                                                    const struct
865                                                    GNUNET_FS_BlockOptions *bo,
866                                                    const char *filename)
867 {
868   struct GNUNET_FS_FileInformation *ret;
869
870   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
871   ret->h = h;
872   ret->client_info = client_info;
873   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
874   ret->keywords = GNUNET_FS_uri_dup (keywords);
875   ret->bo = *bo;
876   ret->is_directory = GNUNET_YES;
877   if (filename != NULL)
878     ret->filename = GNUNET_strdup (filename);
879   return ret;
880 }
881
882
883 /**
884  * Add an entry to a directory in a publish-structure.  Clients
885  * should never modify publish structures that were passed to
886  * "GNUNET_FS_publish_start" already.
887  *
888  * @param dir the directory
889  * @param ent the entry to add; the entry must not have been
890  *            added to any other directory at this point and
891  *            must not include "dir" in its structure
892  * @return GNUNET_OK on success, GNUNET_SYSERR on error
893  */
894 int
895 GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
896                                 struct GNUNET_FS_FileInformation *ent)
897 {
898   if ((ent->dir != NULL) || (ent->next != NULL) || (!dir->is_directory))
899   {
900     GNUNET_break (0);
901     return GNUNET_SYSERR;
902   }
903   ent->dir = dir;
904   ent->next = dir->data.dir.entries;
905   dir->data.dir.entries = ent;
906   dir->data.dir.dir_size = 0;
907   return GNUNET_OK;
908 }
909
910
911 /**
912  * Inspect a file or directory in a publish-structure.  Clients
913  * should never modify publish structures that were passed to
914  * "GNUNET_FS_publish_start" already.  When called on a directory,
915  * this function will FIRST call "proc" with information about
916  * the directory itself and then for each of the files in the
917  * directory (but not for files in subdirectories).  When called
918  * on a file, "proc" will be called exactly once (with information
919  * about the specific file).
920  *
921  * @param dir the directory
922  * @param proc function to call on each entry
923  * @param proc_cls closure for proc
924  */
925 void
926 GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
927                                     GNUNET_FS_FileInformationProcessor proc,
928                                     void *proc_cls)
929 {
930   struct GNUNET_FS_FileInformation *pos;
931   int no;
932
933   no = GNUNET_NO;
934   if (GNUNET_OK !=
935       proc (proc_cls, dir,
936             (dir->is_directory) ? dir->data.dir.dir_size : dir->data.
937             file.file_size, dir->meta, &dir->keywords, &dir->bo,
938             (dir->is_directory) ? &no : &dir->data.file.do_index,
939             &dir->client_info))
940     return;
941   if (!dir->is_directory)
942     return;
943   pos = dir->data.dir.entries;
944   while (pos != NULL)
945   {
946     no = GNUNET_NO;
947     if (GNUNET_OK !=
948         proc (proc_cls, pos,
949               (pos->is_directory) ? pos->data.dir.dir_size : pos->data.
950               file.file_size, pos->meta, &pos->keywords, &pos->bo,
951               (dir->is_directory) ? &no : &dir->data.file.do_index,
952               &pos->client_info))
953       break;
954     pos = pos->next;
955   }
956 }
957
958
959 /**
960  * Destroy publish-structure.  Clients should never destroy publish
961  * structures that were passed to "GNUNET_FS_publish_start" already.
962  *
963  * @param fi structure to destroy
964  * @param cleaner function to call on each entry in the structure
965  *        (useful to clean up client_info); can be NULL; return
966  *        values are ignored
967  * @param cleaner_cls closure for cleaner
968  */
969 void
970 GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
971                                     GNUNET_FS_FileInformationProcessor cleaner,
972                                     void *cleaner_cls)
973 {
974   struct GNUNET_FS_FileInformation *pos;
975   int no;
976
977   no = GNUNET_NO;
978   if (fi->is_directory)
979   {
980     /* clean up directory */
981     while (NULL != (pos = fi->data.dir.entries))
982     {
983       fi->data.dir.entries = pos->next;
984       GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
985     }
986     /* clean up client-info */
987     if (NULL != cleaner)
988       cleaner (cleaner_cls, fi, fi->data.dir.dir_size, fi->meta, &fi->keywords,
989                &fi->bo, &no, &fi->client_info);
990     GNUNET_free_non_null (fi->data.dir.dir_data);
991   }
992   else
993   {
994     /* call clean-up function of the reader */
995     if (fi->data.file.reader != NULL)
996       fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
997     /* clean up client-info */
998     if (NULL != cleaner)
999       cleaner (cleaner_cls, fi, fi->data.file.file_size, fi->meta,
1000                &fi->keywords, &fi->bo, &fi->data.file.do_index,
1001                &fi->client_info);
1002   }
1003   GNUNET_free_non_null (fi->filename);
1004   GNUNET_free_non_null (fi->emsg);
1005   GNUNET_free_non_null (fi->chk_uri);
1006   /* clean up serialization */
1007   if ((NULL != fi->serialization) && (0 != UNLINK (fi->serialization)))
1008     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
1009                               fi->serialization);
1010   if (NULL != fi->keywords)
1011     GNUNET_FS_uri_destroy (fi->keywords);
1012   if (NULL != fi->meta)
1013     GNUNET_CONTAINER_meta_data_destroy (fi->meta);
1014   GNUNET_free_non_null (fi->serialization);
1015   if (fi->te != NULL)
1016   {
1017     GNUNET_FS_tree_encoder_finish (fi->te, NULL, NULL);
1018     fi->te = NULL;
1019   }
1020   GNUNET_free (fi);
1021 }
1022
1023
1024 /* end of fs_file_information.c */