bio test fixed
[oweals/gnunet.git] / src / util / container_meta_data.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003, 2004, 2005, 2006, 2008, 2009 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 2, 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 util/container_meta_data.c
23  * @brief Storing of meta data
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_strings_lib.h"
31 #include "gnunet_time_lib.h"
32 #include <extractor.h>
33 #include <zlib.h>
34
35 #define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
36
37 struct Item
38 {
39   EXTRACTOR_KeywordType type;
40   char *data;
41 };
42
43 /**
44  * Meta data to associate with a file, directory or namespace.
45  */
46 struct GNUNET_CONTAINER_MetaData
47 {
48   uint32_t itemCount;
49   struct Item *items;
50 };
51
52 /**
53  * Create a fresh struct CONTAINER_MetaData token.
54  */
55 struct GNUNET_CONTAINER_MetaData *
56 GNUNET_CONTAINER_meta_data_create ()
57 {
58   struct GNUNET_CONTAINER_MetaData *ret;
59   ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData));
60   ret->items = NULL;
61   ret->itemCount = 0;
62   return ret;
63 }
64
65 /**
66  * Free meta data.
67  */
68 void
69 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
70 {
71   int i;
72
73   if (md == NULL)
74     return;
75   for (i = 0; i < md->itemCount; i++)
76     GNUNET_free (md->items[i].data);
77   GNUNET_array_grow (md->items, md->itemCount, 0);
78   GNUNET_free (md);
79 }
80
81 /**
82  * Add the current time as the publication date
83  * to the meta-data.
84  */
85 void
86 GNUNET_CONTAINER_meta_data_add_publication_date (struct
87                                                  GNUNET_CONTAINER_MetaData
88                                                  *md)
89 {
90   char *dat;
91   struct GNUNET_TIME_Absolute t;
92
93   t = GNUNET_TIME_absolute_get ();
94   GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_PUBLICATION_DATE, NULL);
95   dat = GNUNET_STRINGS_absolute_time_to_string (t);
96   GNUNET_CONTAINER_meta_data_insert (md, EXTRACTOR_PUBLICATION_DATE, dat);
97   GNUNET_free (dat);
98 }
99
100 /**
101  * Extend metadata.
102  * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
103  */
104 int
105 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
106                                    EXTRACTOR_KeywordType type,
107                                    const char *data)
108 {
109   uint32_t idx;
110   char *p;
111
112   GNUNET_assert (data != NULL);
113   for (idx = 0; idx < md->itemCount; idx++)
114     {
115       if ((md->items[idx].type == type) &&
116           (0 == strcmp (md->items[idx].data, data)))
117         return GNUNET_SYSERR;
118     }
119   idx = md->itemCount;
120   GNUNET_array_grow (md->items, md->itemCount, md->itemCount + 1);
121   md->items[idx].type = type;
122   md->items[idx].data = p = GNUNET_strdup (data);
123
124   /* change OS native dir separators to unix '/' and others to '_' */
125   if (type == EXTRACTOR_FILENAME)
126     {
127       while (*p != '\0')
128         {
129           if (*p == DIR_SEPARATOR)
130             *p = '/';
131           else if (*p == '\\')
132             *p = '_';
133           p++;
134         }
135     }
136
137   return GNUNET_OK;
138 }
139
140 /**
141  * Remove an item.
142  *
143  * @param md metadata to manipulate
144  * @param type type of the item to remove
145  * @param data specific value to remove, NULL to remove all
146  *        entries of the given type
147  * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
148  */
149 int
150 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
151                                    EXTRACTOR_KeywordType type,
152                                    const char *data)
153 {
154   uint32_t idx;
155   int ret = GNUNET_SYSERR;
156   for (idx = 0; idx < md->itemCount; idx++)
157     {
158       if ((md->items[idx].type == type) &&
159           ((data == NULL) || (0 == strcmp (md->items[idx].data, data))))
160         {
161           GNUNET_free (md->items[idx].data);
162           md->items[idx] = md->items[md->itemCount - 1];
163           GNUNET_array_grow (md->items, md->itemCount, md->itemCount - 1);
164           if (data == NULL)
165             {
166               ret = GNUNET_OK;
167               continue;
168             }
169           return GNUNET_OK;
170         }
171     }
172   return ret;
173 }
174
175 /**
176  * Iterate over MD entries, excluding thumbnails.
177  *
178  * @param md metadata to inspect
179  * @param iter function to call on each entry
180  * @param iter_cls closure for iterator
181  * @return number of entries
182  */
183 int
184 GNUNET_CONTAINER_meta_data_get_contents (const struct
185                                          GNUNET_CONTAINER_MetaData *md,
186                                          GNUNET_CONTAINER_MetaDataProcessor
187                                          iter, void *iter_cls)
188 {
189   uint32_t i;
190   uint32_t sub;
191
192   sub = 0;
193   for (i = 0; i < md->itemCount; i++)
194     {
195       if (!EXTRACTOR_isBinaryType (md->items[i].type))
196         {
197           if ((iter != NULL) &&
198               (GNUNET_OK != iter (iter_cls,
199                                   md->items[i].type,
200                                   md->items[i].data)))
201             return GNUNET_SYSERR;
202         }
203       else
204         sub++;
205     }
206   return (int) (md->itemCount - sub);
207 }
208
209 /**
210  * Iterate over MD entries
211  *
212  * @return number of entries
213  */
214 char *
215 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
216                                         *md, EXTRACTOR_KeywordType type)
217 {
218   uint32_t i;
219
220   for (i = 0; i < md->itemCount; i++)
221     if (type == md->items[i].type)
222       return GNUNET_strdup (md->items[i].data);
223   return NULL;
224 }
225
226 /**
227  * Iterate over MD entries
228  *
229  * @return number of entries
230  */
231 char *
232 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
233                                                GNUNET_CONTAINER_MetaData *md,
234                                                ...)
235 {
236   char *ret;
237   va_list args;
238   EXTRACTOR_KeywordType type;
239
240   ret = NULL;
241   va_start (args, md);
242   while (1)
243     {
244       type = va_arg (args, EXTRACTOR_KeywordType);
245       if (type == -1)
246         break;
247       ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type);
248       if (ret != NULL)
249         break;
250     }
251   va_end (args);
252   return ret;
253 }
254
255 /**
256  * Get a thumbnail from the meta-data (if present).
257  *
258  * @param md metadata to get the thumbnail from
259  * @param thumb will be set to the thumbnail data.  Must be
260  *        freed by the caller!
261  * @return number of bytes in thumbnail, 0 if not available
262  */
263 size_t
264 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
265                                           GNUNET_CONTAINER_MetaData * md,
266                                           unsigned char **thumb)
267 {
268   char *encoded;
269   int ret;
270   size_t size;
271
272   encoded =
273     GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_THUMBNAIL_DATA);
274   if (encoded == NULL)
275     return 0;
276   if (strlen (encoded) == 0)
277     {
278       GNUNET_free (encoded);
279       return 0;                 /* invalid */
280     }
281   *thumb = NULL;
282   ret = EXTRACTOR_binaryDecode (encoded, thumb, &size);
283   GNUNET_free (encoded);
284   if (ret != 0)
285     return 0;
286   return size;
287 }
288
289 /**
290  * Duplicate struct GNUNET_CONTAINER_MetaData.
291  * 
292  * @param md what to duplicate
293  * @return duplicate meta-data container
294  */
295 struct GNUNET_CONTAINER_MetaData *
296 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
297                                       *md)
298 {
299   uint32_t i;
300   struct GNUNET_CONTAINER_MetaData *ret;
301
302   if (md == NULL)
303     return NULL;
304   ret = GNUNET_CONTAINER_meta_data_create ();
305   for (i = 0; i < md->itemCount; i++)
306     GNUNET_CONTAINER_meta_data_insert (ret, md->items[i].type,
307                                        md->items[i].data);
308   return ret;
309 }
310
311 /**
312  * Extract meta-data from a file.
313  *
314  * @return GNUNET_SYSERR on error, otherwise the number
315  *   of meta-data items obtained
316  */
317 int
318 GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData
319                                               *md, const char *filename,
320                                               EXTRACTOR_ExtractorList *
321                                               extractors)
322 {
323   EXTRACTOR_KeywordList *head;
324   EXTRACTOR_KeywordList *pos;
325   int ret;
326
327   if (filename == NULL)
328     return GNUNET_SYSERR;
329   if (extractors == NULL)
330     return 0;
331   head = EXTRACTOR_getKeywords (extractors, filename);
332   head = EXTRACTOR_removeDuplicateKeywords (head,
333                                             EXTRACTOR_DUPLICATES_REMOVE_UNKNOWN);
334   pos = head;
335   ret = 0;
336   while (pos != NULL)
337     {
338       if (GNUNET_OK ==
339           GNUNET_CONTAINER_meta_data_insert (md, pos->keywordType,
340                                              pos->keyword))
341         ret++;
342       pos = pos->next;
343     }
344   EXTRACTOR_freeKeywords (head);
345   return ret;
346 }
347
348
349 static unsigned int
350 tryCompression (char *data, unsigned int oldSize)
351 {
352   char *tmp;
353   uLongf dlen;
354
355 #ifdef compressBound
356   dlen = compressBound (oldSize);
357 #else
358   dlen = oldSize + (oldSize / 100) + 20;
359   /* documentation says 100.1% oldSize + 12 bytes, but we
360      should be able to overshoot by more to be safe */
361 #endif
362   tmp = GNUNET_malloc (dlen);
363   if (Z_OK == compress2 ((Bytef *) tmp,
364                          &dlen, (const Bytef *) data, oldSize, 9))
365     {
366       if (dlen < oldSize)
367         {
368           memcpy (data, tmp, dlen);
369           GNUNET_free (tmp);
370           return dlen;
371         }
372     }
373   GNUNET_free (tmp);
374   return oldSize;
375 }
376
377 /**
378  * Decompress input, return the decompressed data
379  * as output, set outputSize to the number of bytes
380  * that were found.
381  *
382  * @return NULL on error
383  */
384 static char *
385 decompress (const char *input,
386             unsigned int inputSize, unsigned int outputSize)
387 {
388   char *output;
389   uLongf olen;
390
391   olen = outputSize;
392   output = GNUNET_malloc (olen);
393   if (Z_OK == uncompress ((Bytef *) output,
394                           &olen, (const Bytef *) input, inputSize))
395     {
396       return output;
397     }
398   else
399     {
400       GNUNET_free (output);
401       return NULL;
402     }
403 }
404
405 /**
406  * Flag in 'version' that indicates compressed meta-data.
407  */
408 #define HEADER_COMPRESSED 0x80000000
409
410 /**
411  * Bits in 'version' that give the version number.
412  */
413 #define HEADER_VERSION_MASK 0x7FFFFFFF
414
415 struct MetaDataHeader
416 {
417   /**
418    * The version of the MD serialization.
419    * The highest bit is used to indicate
420    * compression.
421    *
422    * Version 0 is the current version;
423    * Version is 1 for a NULL pointer.
424    * Other version numbers are not yet defined.
425    */
426   uint32_t version;
427
428   /**
429    * How many MD entries are there?
430    */
431   uint32_t entries;
432
433   /**
434    * Size of the MD (decompressed)
435    */
436   uint32_t size;
437
438   /**
439    * This is followed by 'entries' values of type 'unsigned int' that
440    * correspond to EXTRACTOR_KeywordTypes.  After that, the meta-data
441    * keywords follow (0-terminated).  The MD block always ends with
442    * 0-termination, padding with 0 until a multiple of 8 bytes.
443    */
444
445 };
446
447 /**
448  * Serialize meta-data to target.
449  *
450  * @param md metadata to serialize
451  * @param target where to write the serialized metadata
452  * @param max maximum number of bytes available in target
453  * @param opt is it ok to just write SOME of the
454  *        meta-data to match the size constraint,
455  *        possibly discarding some data?
456  * @return number of bytes written on success,
457  *         GNUNET_SYSERR on error (typically: not enough
458  *         space)
459  */
460 ssize_t
461 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
462                                       *md, char *target, size_t max,
463                                       enum
464                                       GNUNET_CONTAINER_MetaDataSerializationOptions
465                                       opt)
466 {
467   struct MetaDataHeader *hdr;
468   size_t size;
469   size_t pos;
470   uint32_t i;
471   size_t len;
472   uint32_t ic;
473
474   if (max < sizeof (struct MetaDataHeader))
475     return GNUNET_SYSERR;       /* far too small */
476   ic = md ? md->itemCount : 0;
477   hdr = NULL;
478   while (1)
479     {
480       size = sizeof (struct MetaDataHeader);
481       size += sizeof (uint32_t) * ic;
482       for (i = 0; i < ic; i++)
483         size += 1 + strlen (md->items[i].data);
484       while (size % 8 != 0)
485         size++;
486       hdr = GNUNET_malloc (size);
487       hdr->version = htonl (md == NULL ? 1 : 0);
488       hdr->entries = htonl (ic);
489       for (i = 0; i < ic; i++)
490         ((uint32_t *) &hdr[1])[i] =
491           htonl ((uint32_t) md->items[i].type);
492       pos = sizeof (struct MetaDataHeader);
493       pos += sizeof (unsigned int) * ic;
494       for (i = 0; i < ic; i++)
495         {
496           len = strlen (md->items[i].data) + 1;
497           memcpy (&((char *) hdr)[pos], md->items[i].data, len);
498           pos += len;
499         }
500
501       hdr->size = htonl (size);
502       if ((opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
503         {
504           pos = tryCompression ((char *) &hdr[1],
505                                 size - sizeof (struct MetaDataHeader));
506         }
507       else
508         {
509           pos = size - sizeof (struct MetaDataHeader);
510         }
511       if (pos < size - sizeof (struct MetaDataHeader))
512         {
513           hdr->version = htonl (HEADER_COMPRESSED);
514           size = pos + sizeof (struct MetaDataHeader);
515         }
516       if (size <= max)
517         break;
518       GNUNET_free (hdr);
519       hdr = NULL;
520
521       if ((opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART) == 0)
522         {
523           return GNUNET_SYSERR; /* does not fit! */
524         }
525       /* partial serialization ok, try again with less meta-data */
526       if (size > 2 * max)
527         ic = ic * 2 / 3;        /* still far too big, make big reductions */
528       else
529         ic--;                   /* small steps, we're close */
530     }
531   GNUNET_assert (size <= max);
532   memcpy (target, hdr, size);
533   GNUNET_free (hdr);
534   /* extra check: deserialize! */
535 #if EXTRA_CHECKS
536   {
537     struct GNUNET_CONTAINER_MetaData *mdx;
538     mdx = GNUNET_CONTAINER_meta_data_deserialize (target, size);
539     GNUNET_assert (NULL != mdx);
540     GNUNET_CONTAINER_meta_data_destroy (mdx);
541   }
542 #endif
543   return size;
544 }
545
546 /**
547  * Estimate (!) the size of the meta-data in
548  * serialized form.  The estimate MAY be higher
549  * than what is strictly needed.
550  *
551  * @param md metadata to inspect
552  * @param opt is it ok to just write SOME of the
553  *        meta-data to match the size constraint,
554  *        possibly discarding some data?
555  * @return number of bytes needed for serialization, -1 on error
556  */
557 ssize_t
558 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
559                                                 GNUNET_CONTAINER_MetaData *md,
560                                                 enum
561                                                 GNUNET_CONTAINER_MetaDataSerializationOptions
562                                                 opt)
563 {
564   struct MetaDataHeader *hdr;
565   size_t size;
566   size_t pos;
567   uint32_t i;
568   size_t len;
569   uint32_t ic;
570
571   ic = md ? md->itemCount : 0;
572   size = sizeof (struct MetaDataHeader);
573   size += sizeof (uint32_t) * ic;
574   for (i = 0; i < ic; i++)
575     size += 1 + strlen (md->items[i].data);
576   while (size % 8 != 0)
577     size++;
578   hdr = GNUNET_malloc (size);
579   hdr->version = htonl (md == NULL ? 1 : 0);
580   hdr->entries = htonl (ic);
581   for (i = 0; i < ic; i++)
582     ((uint32_t *) &hdr[1])[i] = htonl ((uint32_t) md->items[i].type);
583   pos = sizeof (struct MetaDataHeader);
584   pos += sizeof (uint32_t) * ic;
585   for (i = 0; i < ic; i++)
586     {
587       len = strlen (md->items[i].data) + 1;
588       memcpy (&((char *) hdr)[pos], md->items[i].data, len);
589       pos += len;
590     }
591   if ((opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
592     {
593       pos =
594         tryCompression ((char *) &hdr[1],
595                         size - sizeof (struct MetaDataHeader));
596     }
597   else
598     {
599       pos = size - sizeof (struct MetaDataHeader);
600     }
601   if (pos < size - sizeof (struct MetaDataHeader))
602     size = pos + sizeof (struct MetaDataHeader);
603   GNUNET_free (hdr);
604   return size;
605 }
606
607
608 /**
609  * Deserialize meta-data.  Initializes md.
610  *
611  * @param input buffer with the serialized metadata
612  * @param size number of bytes available in input
613  * @return MD on success, NULL on error (i.e.
614  *         bad format)
615  */
616 struct GNUNET_CONTAINER_MetaData *
617 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
618 {
619   struct GNUNET_CONTAINER_MetaData *md;
620   const struct MetaDataHeader *hdr;
621   uint32_t ic;
622   char *data;
623   const char *cdata;
624   uint32_t dataSize;
625   int compressed;
626   uint32_t i;
627   size_t pos;
628   size_t len;
629   uint32_t version;
630
631   if (size < sizeof (struct MetaDataHeader))
632     return NULL;
633   hdr = (const struct MetaDataHeader *) input;
634   version = ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_VERSION_MASK;
635   if (version == 1)
636     return NULL;                /* null pointer */
637   if (version != 0)
638     {
639       GNUNET_break_op (0);      /* unsupported version */
640       return NULL;
641     }
642   ic = ntohl (MAKE_UNALIGNED (hdr->entries));
643   compressed =
644     (ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_COMPRESSED) != 0;
645   if (compressed)
646     {
647       dataSize =
648         ntohl (MAKE_UNALIGNED (hdr->size)) - sizeof (struct MetaDataHeader);
649       if (dataSize > 2 * 1042 * 1024)
650         {
651           GNUNET_break (0);
652           return NULL;          /* only 2 MB allowed [to make sure we don't blow
653                                    our memory limit because of a mal-formed
654                                    message... ] */
655         }
656       data =
657         decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
658                     size - sizeof (struct MetaDataHeader), dataSize);
659       if (data == NULL)
660         {
661           GNUNET_break_op (0);
662           return NULL;
663         }
664       cdata = data;
665     }
666   else
667     {
668       data = NULL;
669       cdata = (const char *) &hdr[1];
670       dataSize = size - sizeof (struct MetaDataHeader);
671       if (size != ntohl (MAKE_UNALIGNED (hdr->size)))
672         {
673           GNUNET_break (0);
674           return NULL;
675         }
676     }
677
678   if ((sizeof (uint32_t) * ic + ic) > dataSize)
679     {
680       GNUNET_break (0);
681       goto FAILURE;
682     }
683   if ((ic > 0) && (cdata[dataSize - 1] != '\0'))
684     {
685       GNUNET_break (0);
686       goto FAILURE;
687     }
688
689   md = GNUNET_CONTAINER_meta_data_create ();
690   GNUNET_array_grow (md->items, md->itemCount, ic);
691   i = 0;
692   pos = sizeof (uint32_t) * ic;
693   while ((pos < dataSize) && (i < ic))
694     {
695       len = strlen (&cdata[pos]) + 1;
696       md->items[i].type = (EXTRACTOR_KeywordType)
697         ntohl (MAKE_UNALIGNED (((const uint32_t *) cdata)[i]));
698       md->items[i].data = GNUNET_strdup (&cdata[pos]);
699       pos += len;
700       i++;
701     }
702   if (i < ic)
703     {                           /* oops */
704       GNUNET_CONTAINER_meta_data_destroy (md);
705       goto FAILURE;
706     }
707   GNUNET_free_non_null (data);
708   return md;
709 FAILURE:
710   GNUNET_free_non_null (data);
711   return NULL;                  /* size too small */
712 }
713
714 /**
715  * Test if two MDs are equal.
716  *
717  * @param md1 first value to check
718  * @param md2 other value to check
719  * @return GNUNET_YES if they are equal
720  */
721 int
722 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
723                                        *md1,
724                                        const struct GNUNET_CONTAINER_MetaData
725                                        *md2)
726 {
727   uint32_t i;
728   uint32_t j;
729   int found;
730
731   if (md1->itemCount != md2->itemCount)
732     return GNUNET_NO;
733   for (i = 0; i < md1->itemCount; i++)
734     {
735       found = GNUNET_NO;
736       for (j = 0; j < md2->itemCount; j++)
737         if ((md1->items[i].type == md2->items[j].type) &&
738             (0 == strcmp (md1->items[i].data, md2->items[j].data)))
739           {
740             found = GNUNET_YES;
741             break;
742           }
743       if (found == GNUNET_NO)
744         return GNUNET_NO;
745     }
746   return GNUNET_YES;
747 }
748
749
750 /* end of container_meta_data.c */