uncrustify as demanded.
[oweals/gnunet.git] / src / fs / fs_file_information.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2011 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file fs/fs_file_information.c
23  * @brief  Manage information for publishing directory hierarchies
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #if HAVE_EXTRACTOR_H
28 #include <extractor.h>
29 #endif
30 #include "gnunet_fs_service.h"
31 #include "fs_api.h"
32 #include "fs_tree.h"
33
34
35 /**
36  * Obtain the name under which this file information
37  * structure is stored on disk.  Only works for top-level
38  * file information structures.
39  *
40  * @param s structure to get the filename for
41  * @return NULL on error, otherwise filename that
42  *         can be used to read this fi-struct from disk.
43  */
44 const char *
45 GNUNET_FS_file_information_get_id(struct GNUNET_FS_FileInformation *s)
46 {
47   if (NULL != s->dir)
48     return NULL;
49   return s->serialization;
50 }
51
52 /**
53  * Obtain the filename from the file information structure.
54  *
55  * @param s structure to get the filename for
56  * @return "filename" field of the structure (can be NULL)
57  */
58 const char *
59 GNUNET_FS_file_information_get_filename(struct GNUNET_FS_FileInformation *s)
60 {
61   return s->filename;
62 }
63
64
65 /**
66  * Set the filename in the file information structure.
67  * If filename was already set, frees it before setting the new one.
68  * Makes a copy of the argument.
69  *
70  * @param s structure to get the filename for
71  * @param filename filename to set
72  */
73 void
74 GNUNET_FS_file_information_set_filename(struct GNUNET_FS_FileInformation *s,
75                                         const char *filename)
76 {
77   GNUNET_free_non_null(s->filename);
78   if (filename)
79     s->filename = GNUNET_strdup(filename);
80   else
81     s->filename = NULL;
82 }
83
84
85 /**
86  * Create an entry for a file in a publish-structure.
87  *
88  * @param h handle to the file sharing subsystem
89  * @param client_info initial value for the client-info value for this entry
90  * @param filename name of the file or directory to publish
91  * @param keywords under which keywords should this file be available
92  *         directly; can be NULL
93  * @param meta metadata for the file
94  * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
95  *                #GNUNET_SYSERR for simulation
96  * @param bo block options
97  * @return publish structure entry for the file
98  */
99 struct GNUNET_FS_FileInformation *
100 GNUNET_FS_file_information_create_from_file(
101   struct GNUNET_FS_Handle *h,
102   void *client_info,
103   const char *filename,
104   const struct GNUNET_FS_Uri *keywords,
105   const struct GNUNET_CONTAINER_MetaData *meta,
106   int do_index,
107   const struct GNUNET_FS_BlockOptions *bo)
108 {
109   struct FileInfo *fi;
110   uint64_t fsize;
111   struct GNUNET_FS_FileInformation *ret;
112   const char *fn;
113   const char *ss;
114
115 #if WINDOWS
116   char fn_conv[MAX_PATH];
117 #endif
118
119   /* FIXME: should include_symbolic_links be GNUNET_NO or GNUNET_YES here? */
120   if (GNUNET_OK !=
121       GNUNET_DISK_file_size(filename, &fsize, GNUNET_NO, GNUNET_YES))
122     {
123       GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING, "stat", filename);
124       return NULL;
125     }
126   fi = GNUNET_FS_make_file_reader_context_(filename);
127   if (NULL == fi)
128     {
129       GNUNET_break(0);
130       return NULL;
131     }
132   ret =
133     GNUNET_FS_file_information_create_from_reader(h,
134                                                   client_info,
135                                                   fsize,
136                                                   &GNUNET_FS_data_reader_file_,
137                                                   fi,
138                                                   keywords,
139                                                   meta,
140                                                   do_index,
141                                                   bo);
142   if (ret == NULL)
143     return NULL;
144   ret->h = h;
145   ret->filename = GNUNET_strdup(filename);
146 #if !WINDOWS
147   fn = filename;
148 #else
149   plibc_conv_to_win_path(filename, fn_conv);
150   fn = fn_conv;
151 #endif
152   while (NULL != (ss = strstr(fn, DIR_SEPARATOR_STR)))
153     fn = ss + 1;
154 /* FIXME: If we assume that on other platforms CRT is UTF-8-aware, then
155  * this should be changed to EXTRACTOR_METAFORMAT_UTF8
156  */
157 #if !WINDOWS
158   GNUNET_CONTAINER_meta_data_insert(ret->meta,
159                                     "<gnunet>",
160                                     EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
161                                     EXTRACTOR_METAFORMAT_C_STRING,
162                                     "text/plain",
163                                     fn,
164                                     strlen(fn) + 1);
165 #else
166   GNUNET_CONTAINER_meta_data_insert(ret->meta,
167                                     "<gnunet>",
168                                     EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
169                                     EXTRACTOR_METAFORMAT_UTF8,
170                                     "text/plain",
171                                     fn,
172                                     strlen(fn) + 1);
173 #endif
174   return ret;
175 }
176
177
178 /**
179  * Create an entry for a file in a publish-structure.
180  *
181  * @param h handle to the file sharing subsystem
182  * @param client_info initial value for the client-info value for this entry
183  * @param length length of the file
184  * @param data data for the file (should not be used afterwards by
185  *        the caller; callee will "free")
186  * @param keywords under which keywords should this file be available
187  *         directly; can be NULL
188  * @param meta metadata for the file
189  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
190  *                GNUNET_SYSERR for simulation
191  * @param bo block options
192  * @return publish structure entry for the file
193  */
194 struct GNUNET_FS_FileInformation *
195 GNUNET_FS_file_information_create_from_data(
196   struct GNUNET_FS_Handle *h,
197   void *client_info,
198   uint64_t length,
199   void *data,
200   const struct GNUNET_FS_Uri *keywords,
201   const struct GNUNET_CONTAINER_MetaData *meta,
202   int do_index,
203   const struct GNUNET_FS_BlockOptions *bo)
204 {
205   if (GNUNET_YES == do_index)
206     {
207       GNUNET_break(0);
208       return NULL;
209     }
210   return GNUNET_FS_file_information_create_from_reader(h,
211                                                        client_info,
212                                                        length,
213                                                        &GNUNET_FS_data_reader_copy_,
214                                                        data,
215                                                        keywords,
216                                                        meta,
217                                                        do_index,
218                                                        bo);
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 reader function that can be used to obtain the data for the file
229  * @param reader_cls closure for "reader"
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_reader(
240   struct GNUNET_FS_Handle *h,
241   void *client_info,
242   uint64_t length,
243   GNUNET_FS_DataReader reader,
244   void *reader_cls,
245   const struct GNUNET_FS_Uri *keywords,
246   const struct GNUNET_CONTAINER_MetaData *meta,
247   int do_index,
248   const struct GNUNET_FS_BlockOptions *bo)
249 {
250   struct GNUNET_FS_FileInformation *ret;
251
252   if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_))
253     {
254       GNUNET_break(0);
255       return NULL;
256     }
257   ret = GNUNET_new(struct GNUNET_FS_FileInformation);
258   ret->h = h;
259   ret->client_info = client_info;
260   ret->meta = GNUNET_CONTAINER_meta_data_duplicate(meta);
261   if (ret->meta == NULL)
262     ret->meta = GNUNET_CONTAINER_meta_data_create();
263   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup(keywords);
264   ret->data.file.reader = reader;
265   ret->data.file.reader_cls = reader_cls;
266   ret->data.file.do_index = do_index;
267   ret->data.file.file_size = length;
268   ret->bo = *bo;
269   return ret;
270 }
271
272
273 /**
274  * Test if a given entry represents a directory.
275  *
276  * @param ent check if this FI represents a directory
277  * @return #GNUNET_YES if so, #GNUNET_NO if not
278  */
279 int
280 GNUNET_FS_file_information_is_directory(
281   const struct GNUNET_FS_FileInformation *ent)
282 {
283   return ent->is_directory;
284 }
285
286
287 /**
288  * Create an entry for an empty directory in a publish-structure.
289  *
290  * @param h handle to the file sharing subsystem
291  * @param client_info initial value for the client-info value for this entry
292  * @param meta metadata for the directory
293  * @param keywords under which keywords should this directory be available
294  *         directly; can be NULL
295  * @param bo block options
296  * @param filename name of the directory; can be NULL
297  * @return publish structure entry for the directory , NULL on error
298  */
299 struct GNUNET_FS_FileInformation *
300 GNUNET_FS_file_information_create_empty_directory(
301   struct GNUNET_FS_Handle *h,
302   void *client_info,
303   const struct GNUNET_FS_Uri *keywords,
304   const struct GNUNET_CONTAINER_MetaData *meta,
305   const struct GNUNET_FS_BlockOptions *bo,
306   const char *filename)
307 {
308   struct GNUNET_FS_FileInformation *ret;
309
310   ret = GNUNET_new(struct GNUNET_FS_FileInformation);
311   ret->h = h;
312   ret->client_info = client_info;
313   ret->meta = GNUNET_CONTAINER_meta_data_duplicate(meta);
314   ret->keywords = GNUNET_FS_uri_dup(keywords);
315   ret->bo = *bo;
316   ret->is_directory = GNUNET_YES;
317   if (filename != NULL)
318     ret->filename = GNUNET_strdup(filename);
319   return ret;
320 }
321
322
323 /**
324  * Add an entry to a directory in a publish-structure.  Clients
325  * should never modify publish structures that were passed to
326  * #GNUNET_FS_publish_start already.
327  *
328  * @param dir the directory
329  * @param ent the entry to add; the entry must not have been
330  *            added to any other directory at this point and
331  *            must not include @a dir in its structure
332  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
333  */
334 int
335 GNUNET_FS_file_information_add(struct GNUNET_FS_FileInformation *dir,
336                                struct GNUNET_FS_FileInformation *ent)
337 {
338   if ((ent->dir != NULL) || (ent->next != NULL) ||
339       (dir->is_directory != GNUNET_YES))
340     {
341       GNUNET_break(0);
342       return GNUNET_SYSERR;
343     }
344   ent->dir = dir;
345   ent->next = dir->data.dir.entries;
346   dir->data.dir.entries = ent;
347   dir->data.dir.dir_size = 0;
348   return GNUNET_OK;
349 }
350
351
352 /**
353  * Inspect a file or directory in a publish-structure.  Clients
354  * should never modify publish structures that were passed to
355  * #GNUNET_FS_publish_start already.  When called on a directory,
356  * this function will FIRST call @a proc with information about
357  * the directory itself and then for each of the files in the
358  * directory (but not for files in subdirectories).  When called
359  * on a file, @a proc will be called exactly once (with information
360  * about the specific file).
361  *
362  * @param dir the directory
363  * @param proc function to call on each entry
364  * @param proc_cls closure for @a proc
365  */
366 void
367 GNUNET_FS_file_information_inspect(struct GNUNET_FS_FileInformation *dir,
368                                    GNUNET_FS_FileInformationProcessor proc,
369                                    void *proc_cls)
370 {
371   struct GNUNET_FS_FileInformation *pos;
372   int no;
373
374   no = GNUNET_NO;
375   if (GNUNET_OK !=
376       proc(proc_cls,
377            dir,
378            (dir->is_directory == GNUNET_YES) ? dir->data.dir.dir_size
379            : dir->data.file.file_size,
380            dir->meta,
381            &dir->keywords,
382            &dir->bo,
383            (dir->is_directory == GNUNET_YES) ? &no : &dir->data.file.do_index,
384            &dir->client_info))
385     return;
386   if (dir->is_directory != GNUNET_YES)
387     return;
388   pos = dir->data.dir.entries;
389   while (pos != NULL)
390     {
391       no = GNUNET_NO;
392       if (GNUNET_OK !=
393           proc(proc_cls,
394                pos,
395                (pos->is_directory == GNUNET_YES) ? pos->data.dir.dir_size
396                : pos->data.file.file_size,
397                pos->meta,
398                &pos->keywords,
399                &pos->bo,
400                (pos->is_directory == GNUNET_YES) ? &no
401                : &pos->data.file.do_index,
402                &pos->client_info))
403         break;
404       pos = pos->next;
405     }
406 }
407
408
409 /**
410  * Destroy publish-structure.  Clients should never destroy publish
411  * structures that were passed to #GNUNET_FS_publish_start already.
412  *
413  * @param fi structure to destroy
414  * @param cleaner function to call on each entry in the structure
415  *        (useful to clean up client_info); can be NULL; return
416  *        values are ignored
417  * @param cleaner_cls closure for @a cleaner
418  */
419 void
420 GNUNET_FS_file_information_destroy(struct GNUNET_FS_FileInformation *fi,
421                                    GNUNET_FS_FileInformationProcessor cleaner,
422                                    void *cleaner_cls)
423 {
424   struct GNUNET_FS_FileInformation *pos;
425   int no;
426
427   no = GNUNET_NO;
428   if (GNUNET_YES == fi->is_directory)
429     {
430       /* clean up directory */
431       while (NULL != (pos = fi->data.dir.entries))
432         {
433           fi->data.dir.entries = pos->next;
434           GNUNET_FS_file_information_destroy(pos, cleaner, cleaner_cls);
435         }
436       /* clean up client-info */
437       if (NULL != cleaner)
438         cleaner(cleaner_cls,
439                 fi,
440                 fi->data.dir.dir_size,
441                 fi->meta,
442                 &fi->keywords,
443                 &fi->bo,
444                 &no,
445                 &fi->client_info);
446       GNUNET_free_non_null(fi->data.dir.dir_data);
447     }
448   else
449     {
450       /* call clean-up function of the reader */
451       if (NULL != fi->data.file.reader)
452         {
453           (void)fi->data.file.reader(fi->data.file.reader_cls, 0, 0, NULL, NULL);
454           fi->data.file.reader = NULL;
455         }
456       /* clean up client-info */
457       if (NULL != cleaner)
458         cleaner(cleaner_cls,
459                 fi,
460                 fi->data.file.file_size,
461                 fi->meta,
462                 &fi->keywords,
463                 &fi->bo,
464                 &fi->data.file.do_index,
465                 &fi->client_info);
466     }
467   GNUNET_free_non_null(fi->filename);
468   GNUNET_free_non_null(fi->emsg);
469   if (NULL != fi->sks_uri)
470     GNUNET_FS_uri_destroy(fi->sks_uri);
471   if (NULL != fi->chk_uri)
472     GNUNET_FS_uri_destroy(fi->chk_uri);
473   /* clean up serialization */
474   if ((NULL != fi->serialization) && (0 != unlink(fi->serialization)))
475     GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
476                              "unlink",
477                              fi->serialization);
478   if (NULL != fi->keywords)
479     GNUNET_FS_uri_destroy(fi->keywords);
480   if (NULL != fi->meta)
481     GNUNET_CONTAINER_meta_data_destroy(fi->meta);
482   GNUNET_free_non_null(fi->serialization);
483   if (NULL != fi->te)
484     {
485       GNUNET_FS_tree_encoder_finish(fi->te, NULL);
486       fi->te = NULL;
487     }
488   GNUNET_free(fi);
489 }
490
491
492 /* end of fs_file_information.c */