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