2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
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.
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.
17 * @file util/container_meta_data.c
18 * @brief Storing of meta data
19 * @author Christian Grothoff
23 #include "gnunet_util_lib.h"
25 #include <extractor.h>
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-container-meta-data", __VA_ARGS__)
34 * Try to compress the given block of data using libz. Only returns
35 * the compressed block if compression worked and the new block is
36 * actually smaller. Decompress using #GNUNET_decompress().
38 * @param data block to compress; if compression
39 * resulted in a smaller block, the first
40 * bytes of data are updated to the compressed
42 * @param old_size number of bytes in data
43 * @param[out] result set to the compressed data, if compression worked
44 * @param[out] new_size set to size of result, if compression worked
45 * @return #GNUNET_YES if compression reduce the size,
46 * #GNUNET_NO if compression did not help
49 GNUNET_try_compression (const char *data,
60 dlen = compressBound (old_size);
62 dlen = old_size + (old_size / 100) + 20;
63 /* documentation says 100.1% oldSize + 12 bytes, but we
64 * should be able to overshoot by more to be safe */
66 tmp = GNUNET_malloc (dlen);
68 compress2 ((Bytef *) tmp,
86 * Decompress input, return the decompressed data as output. Dual to
87 * #GNUNET_try_compression(). Caller must set @a output_size to the
88 * number of bytes that were originally compressed.
90 * @param input compressed data
91 * @param input_size number of bytes in input
92 * @param output_size expected size of the output
93 * @return NULL on error, buffer of @a output_size decompressed bytes otherwise
96 GNUNET_decompress (const char *input,
104 output = GNUNET_malloc (olen);
106 uncompress ((Bytef *) output,
108 (const Bytef *) input,
111 GNUNET_free (output);
122 * This is a doubly linked list.
124 struct MetaItem *next;
127 * This is a doubly linked list.
129 struct MetaItem *prev;
132 * Name of the extracting plugin.
142 * The actual meta data.
147 * Number of bytes in 'data'.
152 * Type of the meta data.
154 enum EXTRACTOR_MetaType type;
157 * Format of the meta data.
159 enum EXTRACTOR_MetaFormat format;
164 * Meta data to associate with a file, directory or namespace.
166 struct GNUNET_CONTAINER_MetaData
169 * Head of linked list of the meta data items.
171 struct MetaItem *items_head;
174 * Tail of linked list of the meta data items.
176 struct MetaItem *items_tail;
179 * Complete serialized and compressed buffer of the items.
180 * NULL if we have not computed that buffer yet.
185 * Number of bytes in 'sbuf'. 0 if the buffer is stale.
190 * Number of items in the linked list.
192 unsigned int item_count;
198 * Create a fresh struct CONTAINER_MetaData token.
200 * @return empty meta-data container
202 struct GNUNET_CONTAINER_MetaData *
203 GNUNET_CONTAINER_meta_data_create ()
205 return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
210 * Free meta data item.
212 * @param mi item to free
215 meta_item_free (struct MetaItem *mi)
217 GNUNET_free_non_null (mi->plugin_name);
218 GNUNET_free_non_null (mi->mime_type);
219 GNUNET_free_non_null (mi->data);
225 * The meta data has changed, invalidate its serialization
228 * @param md meta data that changed
231 invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
233 if (NULL == md->sbuf)
235 GNUNET_free (md->sbuf);
244 * @param md what to free
247 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
249 struct MetaItem *pos;
253 while (NULL != (pos = md->items_head))
255 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
256 meta_item_free (pos);
258 GNUNET_free_non_null (md->sbuf);
264 * Remove all items in the container.
266 * @param md metadata to manipulate
269 GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
275 while (NULL != (mi = md->items_head))
277 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
280 GNUNET_free_non_null (md->sbuf);
281 memset (md, 0, sizeof (struct GNUNET_CONTAINER_MetaData));
286 * Test if two MDs are equal. We consider them equal if
287 * the meta types, formats and content match (we do not
288 * include the mime types and plugins names in this
291 * @param md1 first value to check
292 * @param md2 other value to check
293 * @return #GNUNET_YES if they are equal
296 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
298 const struct GNUNET_CONTAINER_MetaData
307 if (md1->item_count != md2->item_count)
309 for (i = md1->items_head; NULL != i; i = i->next)
312 for (j = md2->items_head; NULL != j; j = j->next)
314 if ((i->type == j->type) && (i->format == j->format) &&
315 (i->data_size == j->data_size) &&
316 (0 == memcmp (i->data, j->data, i->data_size)))
321 if (j->data_size < i->data_size)
322 break; /* elements are sorted by (decreasing) size... */
324 if (GNUNET_NO == found)
332 * Extend metadata. Note that the list of meta data items is
333 * sorted by size (largest first).
335 * @param md metadata to extend
336 * @param plugin_name name of the plugin that produced this value;
337 * special values can be used (i.e. '<zlib>' for zlib being
338 * used in the main libextractor library and yielding
340 * @param type libextractor-type describing the meta data
341 * @param format basic format information about data
342 * @param data_mime_type mime-type of data (not of the original file);
343 * can be NULL (if mime-type is not known)
344 * @param data actual meta-data found
345 * @param data_size number of bytes in @a data
346 * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
347 * data_mime_type and plugin_name are not considered for "exists" checks
350 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
351 const char *plugin_name,
352 enum EXTRACTOR_MetaType type,
353 enum EXTRACTOR_MetaFormat format,
354 const char *data_mime_type, const char *data,
357 struct MetaItem *pos;
361 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
362 (EXTRACTOR_METAFORMAT_C_STRING == format))
363 GNUNET_break ('\0' == data[data_size - 1]);
365 for (pos = md->items_head; NULL != pos; pos = pos->next)
367 if (pos->data_size < data_size)
368 break; /* elements are sorted by size in the list */
369 if ((pos->type == type) && (pos->data_size == data_size) &&
370 (0 == memcmp (pos->data, data, data_size)))
372 if ((NULL == pos->mime_type) && (NULL != data_mime_type))
374 pos->mime_type = GNUNET_strdup (data_mime_type);
375 invalidate_sbuf (md);
377 if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
378 (EXTRACTOR_METAFORMAT_UTF8 == format))
380 pos->format = EXTRACTOR_METAFORMAT_UTF8;
381 invalidate_sbuf (md);
383 return GNUNET_SYSERR;
387 mi = GNUNET_new (struct MetaItem);
390 mi->data_size = data_size;
392 GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
396 GNUNET_CONTAINER_DLL_insert_after (md->items_head,
401 (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
402 mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
403 mi->data = GNUNET_malloc (data_size);
404 GNUNET_memcpy (mi->data, data, data_size);
405 /* change all dir separators to POSIX style ('/') */
406 if ( (EXTRACTOR_METATYPE_FILENAME == type) ||
407 (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) )
410 while (('\0' != *p) && (p < mi->data + data_size))
417 invalidate_sbuf (md);
423 * Merge given meta data.
425 * @param cls the `struct GNUNET_CONTAINER_MetaData` to merge into
426 * @param plugin_name name of the plugin that produced this value;
427 * special values can be used (i.e. '<zlib>' for zlib being
428 * used in the main libextractor library and yielding
430 * @param type libextractor-type describing the meta data
431 * @param format basic format information about data
432 * @param data_mime_type mime-type of data (not of the original file);
433 * can be NULL (if mime-type is not known)
434 * @param data actual meta-data found
435 * @param data_size number of bytes in @a data
436 * @return 0 (to continue)
439 merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
440 enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
441 const char *data, size_t data_size)
443 struct GNUNET_CONTAINER_MetaData *md = cls;
445 (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
446 data_mime_type, data, data_size);
452 * Extend metadata. Merges the meta data from the second argument
453 * into the first, discarding duplicate key-value pairs.
455 * @param md metadata to extend
456 * @param in metadata to merge
459 GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
460 const struct GNUNET_CONTAINER_MetaData *in)
462 GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
469 * @param md metadata to manipulate
470 * @param type type of the item to remove
471 * @param data specific value to remove, NULL to remove all
472 * entries of the given type
473 * @param data_size number of bytes in @a data
474 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the item does not exist in md
477 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
478 enum EXTRACTOR_MetaType type,
479 const char *data, size_t data_size)
481 struct MetaItem *pos;
483 for (pos = md->items_head; NULL != pos; pos = pos->next)
485 if (pos->data_size < data_size)
486 break; /* items are sorted by (decreasing) size */
487 if ((pos->type == type) &&
489 ((pos->data_size == data_size) &&
490 (0 == memcmp (pos->data, data, data_size)))))
492 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
493 meta_item_free (pos);
495 invalidate_sbuf (md);
499 return GNUNET_SYSERR;
504 * Add the current time as the publication date
507 * @param md metadata to modify
510 GNUNET_CONTAINER_meta_data_add_publication_date (struct
511 GNUNET_CONTAINER_MetaData *md)
514 struct GNUNET_TIME_Absolute t;
516 t = GNUNET_TIME_absolute_get ();
517 GNUNET_CONTAINER_meta_data_delete (md,
518 EXTRACTOR_METATYPE_PUBLICATION_DATE,
520 dat = GNUNET_STRINGS_absolute_time_to_string (t);
521 GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
522 EXTRACTOR_METATYPE_PUBLICATION_DATE,
523 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
524 dat, strlen (dat) + 1);
529 * Iterate over MD entries.
531 * @param md metadata to inspect
532 * @param iter function to call on each entry
533 * @param iter_cls closure for iterator
534 * @return number of entries
537 GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
538 EXTRACTOR_MetaDataProcessor iter,
541 struct MetaItem *pos;
546 return md->item_count;
547 for (pos = md->items_head; NULL != pos; pos = pos->next)
549 iter (iter_cls, pos->plugin_name, pos->type, pos->format,
550 pos->mime_type, pos->data, pos->data_size))
551 return md->item_count;
552 return md->item_count;
557 * Get the first MD entry of the given type. Caller
558 * is responsible for freeing the return value.
559 * Also, only meta data items that are strings (0-terminated)
560 * are returned by this function.
562 * @param md metadata to inspect
563 * @param type type to look for
564 * @return NULL if no entry was found
567 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData *md,
568 enum EXTRACTOR_MetaType type)
570 struct MetaItem *pos;
574 for (pos = md->items_head; NULL != pos; pos = pos->next)
575 if ((type == pos->type) &&
576 ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
577 (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
578 return GNUNET_strdup (pos->data);
584 * Get the first matching MD entry of the given types. Caller is
585 * responsible for freeing the return value. Also, only meta data
586 * items that are strings (0-terminated) are returned by this
589 * @param md metadata to inspect
590 * @param ... -1-terminated list of types
591 * @return NULL if we do not have any such entry,
592 * otherwise client is responsible for freeing the value!
595 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
596 GNUNET_CONTAINER_MetaData *md,
609 type = va_arg (args, int);
612 if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
621 * Get a thumbnail from the meta-data (if present).
623 * @param md metadata to get the thumbnail from
624 * @param thumb will be set to the thumbnail data. Must be
625 * freed by the caller!
626 * @return number of bytes in thumbnail, 0 if not available
629 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
630 * md, unsigned char **thumb)
632 struct MetaItem *pos;
633 struct MetaItem *match;
638 for (pos = md->items_head; NULL != pos; pos = pos->next)
640 if ((NULL != pos->mime_type) &&
641 (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
642 (EXTRACTOR_METAFORMAT_BINARY == pos->format))
646 else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
647 (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
651 if ((NULL == match) || (0 == match->data_size))
653 *thumb = GNUNET_malloc (match->data_size);
654 GNUNET_memcpy (*thumb, match->data, match->data_size);
655 return match->data_size;
660 * Duplicate a `struct GNUNET_CONTAINER_MetaData`.
662 * @param md what to duplicate
663 * @return duplicate meta-data container
665 struct GNUNET_CONTAINER_MetaData *
666 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
669 struct GNUNET_CONTAINER_MetaData *ret;
670 struct MetaItem *pos;
674 ret = GNUNET_CONTAINER_meta_data_create ();
675 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
676 GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
677 pos->format, pos->mime_type, pos->data,
684 * Flag in 'version' that indicates compressed meta-data.
686 #define HEADER_COMPRESSED 0x80000000
690 * Bits in 'version' that give the version number.
692 #define HEADER_VERSION_MASK 0x7FFFFFFF
696 * Header for serialized meta data.
698 struct MetaDataHeader
701 * The version of the MD serialization. The highest bit is used to
702 * indicate compression.
704 * Version 0 is traditional (pre-0.9) meta data (unsupported)
705 * Version is 1 for a NULL pointer
706 * Version 2 is for 0.9.x (and possibly higher)
707 * Other version numbers are not yet defined.
712 * How many MD entries are there?
717 * Size of the decompressed meta data.
722 * This is followed by 'entries' values of type 'struct MetaDataEntry'
723 * and then by 'entry' plugin names, mime-types and data blocks
724 * as specified in those meta data entries.
730 * Entry of serialized meta data.
735 * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
740 * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
745 * Number of bytes of meta data.
750 * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
752 uint32_t plugin_name_len;
755 * Number of bytes in the mime type including 0-terminator. 0 for NULL.
757 uint32_t mime_type_len;
763 * Serialize meta-data to target.
765 * @param md metadata to serialize
766 * @param target where to write the serialized metadata;
767 * *target can be NULL, in which case memory is allocated
768 * @param max maximum number of bytes available in target
769 * @param opt is it ok to just write SOME of the
770 * meta-data to match the size constraint,
771 * possibly discarding some data?
772 * @return number of bytes written on success,
773 * #GNUNET_SYSERR on error (typically: not enough
777 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
778 *md, char **target, size_t max,
780 GNUNET_CONTAINER_MetaDataSerializationOptions
783 struct GNUNET_CONTAINER_MetaData *vmd;
784 struct MetaItem *pos;
785 struct MetaDataHeader ihdr;
786 struct MetaDataHeader *hdr;
787 struct MetaDataEntry *ent;
802 if (max < sizeof (struct MetaDataHeader))
803 return GNUNET_SYSERR; /* far too small */
807 if (NULL != md->sbuf)
809 /* try to use serialization cache */
810 if (md->sbuf_size <= max)
813 *target = GNUNET_malloc (md->sbuf_size);
814 GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
815 return md->sbuf_size;
817 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
818 return GNUNET_SYSERR; /* can say that this will fail */
819 /* need to compute a partial serialization, sbuf useless ... */
823 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
825 msize += sizeof (struct MetaDataEntry);
826 msize += pos->data_size;
827 if (NULL != pos->plugin_name)
828 msize += strlen (pos->plugin_name) + 1;
829 if (NULL != pos->mime_type)
830 msize += strlen (pos->mime_type) + 1;
832 size = (size_t) msize;
835 GNUNET_break (0); /* integer overflow */
836 return GNUNET_SYSERR;
838 if (size >= GNUNET_MAX_MALLOC_CHECKED)
840 /* too large to be processed */
841 return GNUNET_SYSERR;
843 ent = GNUNET_malloc (size);
844 mdata = (char *) &ent[md->item_count];
845 off = size - (md->item_count * sizeof (struct MetaDataEntry));
847 for (pos = md->items_head; NULL != pos; pos = pos->next)
849 ent[i].type = htonl ((uint32_t) pos->type);
850 ent[i].format = htonl ((uint32_t) pos->format);
851 ent[i].data_size = htonl ((uint32_t) pos->data_size);
852 if (NULL == pos->plugin_name)
855 plen = strlen (pos->plugin_name) + 1;
856 ent[i].plugin_name_len = htonl ((uint32_t) plen);
857 if (NULL == pos->mime_type)
860 mlen = strlen (pos->mime_type) + 1;
861 ent[i].mime_type_len = htonl ((uint32_t) mlen);
862 off -= pos->data_size;
863 if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
864 (EXTRACTOR_METAFORMAT_C_STRING == pos->format))
865 GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
866 GNUNET_memcpy (&mdata[off], pos->data, pos->data_size);
868 if (NULL != pos->plugin_name)
869 GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
871 if (NULL != pos->mime_type)
872 GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
875 GNUNET_assert (0 == off);
881 for (pos = md->items_head; NULL != pos; pos = pos->next)
884 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
885 comp = GNUNET_try_compression ((const char *) &ent[i],
890 if ((NULL == md->sbuf) && (0 == i))
892 /* fill 'sbuf'; this "modifies" md, but since this is only
893 * an internal cache we will cast away the 'const' instead
894 * of making the API look strange. */
895 vmd = (struct GNUNET_CONTAINER_MetaData *) md;
896 hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
897 hdr->size = htonl (left);
898 hdr->entries = htonl (md->item_count);
899 if (GNUNET_YES == comp)
901 GNUNET_assert (clen < left);
902 hdr->version = htonl (2 | HEADER_COMPRESSED);
903 GNUNET_memcpy (&hdr[1], cdata, clen);
904 vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
908 hdr->version = htonl (2);
909 GNUNET_memcpy (&hdr[1], &ent[0], left);
910 vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
912 vmd->sbuf = (char *) hdr;
915 if (((left + sizeof (struct MetaDataHeader)) <= max) ||
916 ((GNUNET_YES == comp) && (clen <= max)))
918 /* success, this now fits! */
919 if (GNUNET_YES == comp)
922 dst = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
923 hdr = (struct MetaDataHeader *) dst;
924 hdr->version = htonl (2 | HEADER_COMPRESSED);
925 hdr->size = htonl (left);
926 hdr->entries = htonl (md->item_count - i);
927 GNUNET_memcpy (&dst[sizeof (struct MetaDataHeader)], cdata, clen);
931 rlen = clen + sizeof (struct MetaDataHeader);
936 dst = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
937 hdr = (struct MetaDataHeader *) dst;
938 hdr->version = htonl (2);
939 hdr->entries = htonl (md->item_count - i);
940 hdr->size = htonl (left);
941 GNUNET_memcpy (&dst[sizeof (struct MetaDataHeader)], &ent[i], left);
943 rlen = left + sizeof (struct MetaDataHeader);
947 if (GNUNET_YES == comp)
948 GNUNET_memcpy (*target, dst, clen + sizeof (struct MetaDataHeader));
950 GNUNET_memcpy (*target, dst, left + sizeof (struct MetaDataHeader));
960 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
964 return GNUNET_SYSERR;
967 /* next iteration: ignore the corresponding meta data at the
968 * end and try again without it */
969 left -= sizeof (struct MetaDataEntry);
970 left -= pos->data_size;
971 if (NULL != pos->plugin_name)
972 left -= strlen (pos->plugin_name) + 1;
973 if (NULL != pos->mime_type)
974 left -= strlen (pos->mime_type) + 1;
976 GNUNET_free_non_null (cdata);
983 /* nothing fit, only write header! */
984 ihdr.version = htonl (2);
985 ihdr.entries = htonl (0);
986 ihdr.size = htonl (0);
988 *target = (char *) GNUNET_new (struct MetaDataHeader);
989 GNUNET_memcpy (*target, &ihdr, sizeof (struct MetaDataHeader));
990 return sizeof (struct MetaDataHeader);
995 * Get the size of the full meta-data in serialized form.
997 * @param md metadata to inspect
998 * @return number of bytes needed for serialization, -1 on error
1001 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
1002 GNUNET_CONTAINER_MetaData *md)
1007 if (NULL != md->sbuf)
1008 return md->sbuf_size;
1011 GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
1012 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
1020 * Deserialize meta-data. Initializes md.
1022 * @param input buffer with the serialized metadata
1023 * @param size number of bytes available in input
1024 * @return MD on success, NULL on error (i.e.
1027 struct GNUNET_CONTAINER_MetaData *
1028 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1030 struct GNUNET_CONTAINER_MetaData *md;
1031 struct MetaDataHeader hdr;
1032 struct MetaDataEntry ent;
1045 const char *meta_data;
1046 const char *plugin_name;
1047 const char *mime_type;
1048 enum EXTRACTOR_MetaFormat format;
1050 if (size < sizeof (struct MetaDataHeader))
1052 GNUNET_memcpy (&hdr, input, sizeof (struct MetaDataHeader));
1053 version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1054 compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1057 return NULL; /* null pointer */
1060 GNUNET_break_op (0); /* unsupported version */
1064 ic = ntohl (hdr.entries);
1065 dataSize = ntohl (hdr.size);
1066 if ( ((sizeof (struct MetaDataEntry) * ic) > dataSize) ||
1068 (dataSize / ic < sizeof (struct MetaDataEntry)) ) )
1070 GNUNET_break_op (0);
1076 if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1078 /* make sure we don't blow our memory limit because of a mal-formed
1080 GNUNET_break_op (0);
1084 GNUNET_decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
1085 size - sizeof (struct MetaDataHeader),
1089 GNUNET_break_op (0);
1097 cdata = (const char *) &input[sizeof (struct MetaDataHeader)];
1098 if (dataSize != size - sizeof (struct MetaDataHeader))
1100 GNUNET_break_op (0);
1105 md = GNUNET_CONTAINER_meta_data_create ();
1106 left = dataSize - ic * sizeof (struct MetaDataEntry);
1107 mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
1108 for (i = 0; i < ic; i++)
1110 GNUNET_memcpy (&ent, &cdata[i * sizeof (struct MetaDataEntry)],
1111 sizeof (struct MetaDataEntry));
1112 format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1113 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1114 (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1115 (EXTRACTOR_METAFORMAT_BINARY != format))
1117 GNUNET_break_op (0);
1120 dlen = ntohl (ent.data_size);
1121 plen = ntohl (ent.plugin_name_len);
1122 mlen = ntohl (ent.mime_type_len);
1125 GNUNET_break_op (0);
1129 meta_data = &mdata[left];
1130 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1131 (EXTRACTOR_METAFORMAT_C_STRING == format))
1135 GNUNET_break_op (0);
1138 if ('\0' != meta_data[dlen - 1])
1140 GNUNET_break_op (0);
1146 GNUNET_break_op (0);
1150 if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1152 GNUNET_break_op (0);
1158 plugin_name = &mdata[left];
1162 GNUNET_break_op (0);
1166 if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1168 GNUNET_break_op (0);
1174 mime_type = &mdata[left];
1175 GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1176 (enum EXTRACTOR_MetaType)
1177 ntohl (ent.type), format, mime_type,
1180 GNUNET_free_non_null (data);
1185 /* end of container_meta_data.c */