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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file util/container_meta_data.c
23 * @brief Storing of meta data
24 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
30 #include <extractor.h>
34 #define LOG(kind, ...) GNUNET_log_from (kind, "util-container-meta-data", \
39 * Try to compress the given block of data using libz. Only returns
40 * the compressed block if compression worked and the new block is
41 * actually smaller. Decompress using #GNUNET_decompress().
43 * @param data block to compress; if compression
44 * resulted in a smaller block, the first
45 * bytes of data are updated to the compressed
47 * @param old_size number of bytes in data
48 * @param[out] result set to the compressed data, if compression worked
49 * @param[out] new_size set to size of result, if compression worked
50 * @return #GNUNET_YES if compression reduce the size,
51 * #GNUNET_NO if compression did not help
54 GNUNET_try_compression (const char *data,
65 dlen = compressBound (old_size);
67 dlen = old_size + (old_size / 100) + 20;
68 /* documentation says 100.1% oldSize + 12 bytes, but we
69 * should be able to overshoot by more to be safe */
71 tmp = GNUNET_malloc (dlen);
73 compress2 ((Bytef *) tmp,
91 * Decompress input, return the decompressed data as output. Dual to
92 * #GNUNET_try_compression(). Caller must set @a output_size to the
93 * number of bytes that were originally compressed.
95 * @param input compressed data
96 * @param input_size number of bytes in input
97 * @param output_size expected size of the output
98 * @return NULL on error, buffer of @a output_size decompressed bytes otherwise
101 GNUNET_decompress (const char *input,
109 output = GNUNET_malloc (olen);
111 uncompress ((Bytef *) output,
113 (const Bytef *) input,
116 GNUNET_free (output);
127 * This is a doubly linked list.
129 struct MetaItem *next;
132 * This is a doubly linked list.
134 struct MetaItem *prev;
137 * Name of the extracting plugin.
147 * The actual meta data.
152 * Number of bytes in 'data'.
157 * Type of the meta data.
159 enum EXTRACTOR_MetaType type;
162 * Format of the meta data.
164 enum EXTRACTOR_MetaFormat format;
168 * Meta data to associate with a file, directory or namespace.
170 struct GNUNET_CONTAINER_MetaData
173 * Head of linked list of the meta data items.
175 struct MetaItem *items_head;
178 * Tail of linked list of the meta data items.
180 struct MetaItem *items_tail;
183 * Complete serialized and compressed buffer of the items.
184 * NULL if we have not computed that buffer yet.
189 * Number of bytes in 'sbuf'. 0 if the buffer is stale.
194 * Number of items in the linked list.
196 unsigned int item_count;
201 * Create a fresh struct CONTAINER_MetaData token.
203 * @return empty meta-data container
205 struct GNUNET_CONTAINER_MetaData *
206 GNUNET_CONTAINER_meta_data_create ()
208 return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
213 * Free meta data item.
215 * @param mi item to free
218 meta_item_free (struct MetaItem *mi)
220 GNUNET_free_non_null (mi->plugin_name);
221 GNUNET_free_non_null (mi->mime_type);
222 GNUNET_free_non_null (mi->data);
228 * The meta data has changed, invalidate its serialization
231 * @param md meta data that changed
234 invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
236 if (NULL == md->sbuf)
238 GNUNET_free (md->sbuf);
247 * @param md what to free
250 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
252 struct MetaItem *pos;
256 while (NULL != (pos = md->items_head))
258 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
259 meta_item_free (pos);
261 GNUNET_free_non_null (md->sbuf);
267 * Remove all items in the container.
269 * @param md metadata to manipulate
272 GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
278 while (NULL != (mi = md->items_head))
280 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
283 GNUNET_free_non_null (md->sbuf);
284 memset (md, 0, sizeof(struct GNUNET_CONTAINER_MetaData));
289 * Test if two MDs are equal. We consider them equal if
290 * the meta types, formats and content match (we do not
291 * include the mime types and plugins names in this
294 * @param md1 first value to check
295 * @param md2 other value to check
296 * @return #GNUNET_YES if they are equal
299 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
301 const struct GNUNET_CONTAINER_MetaData
310 if (md1->item_count != md2->item_count)
312 for (i = md1->items_head; NULL != i; i = i->next)
315 for (j = md2->items_head; NULL != j; j = j->next)
317 if ((i->type == j->type) && (i->format == j->format) &&
318 (i->data_size == j->data_size) &&
319 (0 == memcmp (i->data, j->data, i->data_size)))
324 if (j->data_size < i->data_size)
325 break; /* elements are sorted by (decreasing) size... */
327 if (GNUNET_NO == found)
335 * Extend metadata. Note that the list of meta data items is
336 * sorted by size (largest first).
338 * @param md metadata to extend
339 * @param plugin_name name of the plugin that produced this value;
340 * special values can be used (i.e. '<zlib>' for zlib being
341 * used in the main libextractor library and yielding
343 * @param type libextractor-type describing the meta data
344 * @param format basic format information about data
345 * @param data_mime_type mime-type of data (not of the original file);
346 * can be NULL (if mime-type is not known)
347 * @param data actual meta-data found
348 * @param data_size number of bytes in @a data
349 * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
350 * data_mime_type and plugin_name are not considered for "exists" checks
353 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
354 const char *plugin_name,
355 enum EXTRACTOR_MetaType type,
356 enum EXTRACTOR_MetaFormat format,
357 const char *data_mime_type, const char *data,
360 struct MetaItem *pos;
364 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
365 (EXTRACTOR_METAFORMAT_C_STRING == format))
366 GNUNET_break ('\0' == data[data_size - 1]);
368 for (pos = md->items_head; NULL != pos; pos = pos->next)
370 if (pos->data_size < data_size)
371 break; /* elements are sorted by size in the list */
372 if ((pos->type == type) && (pos->data_size == data_size) &&
373 (0 == memcmp (pos->data, data, data_size)))
375 if ((NULL == pos->mime_type) && (NULL != data_mime_type))
377 pos->mime_type = GNUNET_strdup (data_mime_type);
378 invalidate_sbuf (md);
380 if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
381 (EXTRACTOR_METAFORMAT_UTF8 == format))
383 pos->format = EXTRACTOR_METAFORMAT_UTF8;
384 invalidate_sbuf (md);
386 return GNUNET_SYSERR;
390 mi = GNUNET_new (struct MetaItem);
393 mi->data_size = data_size;
395 GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
399 GNUNET_CONTAINER_DLL_insert_after (md->items_head,
404 (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
405 mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
406 mi->data = GNUNET_malloc (data_size);
407 GNUNET_memcpy (mi->data, data, data_size);
408 /* change all dir separators to POSIX style ('/') */
409 if ((EXTRACTOR_METATYPE_FILENAME == type) ||
410 (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type))
413 while (('\0' != *p) && (p < mi->data + data_size))
420 invalidate_sbuf (md);
426 * Merge given meta data.
428 * @param cls the `struct GNUNET_CONTAINER_MetaData` to merge into
429 * @param plugin_name name of the plugin that produced this value;
430 * special values can be used (i.e. '<zlib>' for zlib being
431 * used in the main libextractor library and yielding
433 * @param type libextractor-type describing the meta data
434 * @param format basic format information about data
435 * @param data_mime_type mime-type of data (not of the original file);
436 * can be NULL (if mime-type is not known)
437 * @param data actual meta-data found
438 * @param data_size number of bytes in @a data
439 * @return 0 (to continue)
442 merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
443 enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
444 const char *data, size_t data_size)
446 struct GNUNET_CONTAINER_MetaData *md = cls;
448 (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
449 data_mime_type, data, data_size);
455 * Extend metadata. Merges the meta data from the second argument
456 * into the first, discarding duplicate key-value pairs.
458 * @param md metadata to extend
459 * @param in metadata to merge
462 GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
463 const struct GNUNET_CONTAINER_MetaData *in)
465 GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
472 * @param md metadata to manipulate
473 * @param type type of the item to remove
474 * @param data specific value to remove, NULL to remove all
475 * entries of the given type
476 * @param data_size number of bytes in @a data
477 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the item does not exist in md
480 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
481 enum EXTRACTOR_MetaType type,
482 const char *data, size_t data_size)
484 struct MetaItem *pos;
486 for (pos = md->items_head; NULL != pos; pos = pos->next)
488 if (pos->data_size < data_size)
489 break; /* items are sorted by (decreasing) size */
490 if ((pos->type == type) &&
492 ((pos->data_size == data_size) &&
493 (0 == memcmp (pos->data, data, data_size)))))
495 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
496 meta_item_free (pos);
498 invalidate_sbuf (md);
502 return GNUNET_SYSERR;
507 * Add the current time as the publication date
510 * @param md metadata to modify
513 GNUNET_CONTAINER_meta_data_add_publication_date (struct
514 GNUNET_CONTAINER_MetaData *md)
517 struct GNUNET_TIME_Absolute t;
519 t = GNUNET_TIME_absolute_get ();
520 GNUNET_CONTAINER_meta_data_delete (md,
521 EXTRACTOR_METATYPE_PUBLICATION_DATE,
523 dat = GNUNET_STRINGS_absolute_time_to_string (t);
524 GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
525 EXTRACTOR_METATYPE_PUBLICATION_DATE,
526 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
527 dat, strlen (dat) + 1);
532 * Iterate over MD entries.
534 * @param md metadata to inspect
535 * @param iter function to call on each entry
536 * @param iter_cls closure for iterator
537 * @return number of entries
540 GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
541 EXTRACTOR_MetaDataProcessor iter,
544 struct MetaItem *pos;
549 return md->item_count;
550 for (pos = md->items_head; NULL != pos; pos = pos->next)
552 iter (iter_cls, pos->plugin_name, pos->type, pos->format,
553 pos->mime_type, pos->data, pos->data_size))
554 return md->item_count;
555 return md->item_count;
560 * Get the first MD entry of the given type. Caller
561 * is responsible for freeing the return value.
562 * Also, only meta data items that are strings (0-terminated)
563 * are returned by this function.
565 * @param md metadata to inspect
566 * @param type type to look for
567 * @return NULL if no entry was found
570 GNUNET_CONTAINER_meta_data_get_by_type (const struct
571 GNUNET_CONTAINER_MetaData *md,
572 enum EXTRACTOR_MetaType type)
574 struct MetaItem *pos;
578 for (pos = md->items_head; NULL != pos; pos = pos->next)
579 if ((type == pos->type) &&
580 ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
581 (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
582 return GNUNET_strdup (pos->data);
588 * Get the first matching MD entry of the given types. Caller is
589 * responsible for freeing the return value. Also, only meta data
590 * items that are strings (0-terminated) are returned by this
593 * @param md metadata to inspect
594 * @param ... -1-terminated list of types
595 * @return NULL if we do not have any such entry,
596 * otherwise client is responsible for freeing the value!
599 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
600 GNUNET_CONTAINER_MetaData *md,
613 type = va_arg (args, int);
616 if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
625 * Get a thumbnail from the meta-data (if present).
627 * @param md metadata to get the thumbnail from
628 * @param thumb will be set to the thumbnail data. Must be
629 * freed by the caller!
630 * @return number of bytes in thumbnail, 0 if not available
633 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
634 *md, unsigned char **thumb)
636 struct MetaItem *pos;
637 struct MetaItem *match;
642 for (pos = md->items_head; NULL != pos; pos = pos->next)
644 if ((NULL != pos->mime_type) &&
645 (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
646 (EXTRACTOR_METAFORMAT_BINARY == pos->format))
650 else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
651 (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
655 if ((NULL == match) || (0 == match->data_size))
657 *thumb = GNUNET_malloc (match->data_size);
658 GNUNET_memcpy (*thumb, match->data, match->data_size);
659 return match->data_size;
664 * Duplicate a `struct GNUNET_CONTAINER_MetaData`.
666 * @param md what to duplicate
667 * @return duplicate meta-data container
669 struct GNUNET_CONTAINER_MetaData *
670 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
673 struct GNUNET_CONTAINER_MetaData *ret;
674 struct MetaItem *pos;
678 ret = GNUNET_CONTAINER_meta_data_create ();
679 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
680 GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
681 pos->format, pos->mime_type, pos->data,
688 * Flag in 'version' that indicates compressed meta-data.
690 #define HEADER_COMPRESSED 0x80000000
694 * Bits in 'version' that give the version number.
696 #define HEADER_VERSION_MASK 0x7FFFFFFF
700 * Header for serialized meta data.
702 struct MetaDataHeader
705 * The version of the MD serialization. The highest bit is used to
706 * indicate compression.
708 * Version 0 is traditional (pre-0.9) meta data (unsupported)
709 * Version is 1 for a NULL pointer
710 * Version 2 is for 0.9.x (and possibly higher)
711 * Other version numbers are not yet defined.
716 * How many MD entries are there?
721 * Size of the decompressed meta data.
726 * This is followed by 'entries' values of type 'struct MetaDataEntry'
727 * and then by 'entry' plugin names, mime-types and data blocks
728 * as specified in those meta data entries.
734 * Entry of serialized meta data.
739 * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
744 * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
749 * Number of bytes of meta data.
754 * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
756 uint32_t plugin_name_len;
759 * Number of bytes in the mime type including 0-terminator. 0 for NULL.
761 uint32_t mime_type_len;
766 * Serialize meta-data to target.
768 * @param md metadata to serialize
769 * @param target where to write the serialized metadata;
770 * *target can be NULL, in which case memory is allocated
771 * @param max maximum number of bytes available in target
772 * @param opt is it ok to just write SOME of the
773 * meta-data to match the size constraint,
774 * possibly discarding some data?
775 * @return number of bytes written on success,
776 * #GNUNET_SYSERR on error (typically: not enough
780 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
781 *md, char **target, size_t max,
783 GNUNET_CONTAINER_MetaDataSerializationOptions
786 struct GNUNET_CONTAINER_MetaData *vmd;
787 struct MetaItem *pos;
788 struct MetaDataHeader ihdr;
789 struct MetaDataHeader *hdr;
790 struct MetaDataEntry *ent;
805 if (max < sizeof(struct MetaDataHeader))
806 return GNUNET_SYSERR; /* far too small */
810 if (NULL != md->sbuf)
812 /* try to use serialization cache */
813 if (md->sbuf_size <= max)
816 *target = GNUNET_malloc (md->sbuf_size);
817 GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
818 return md->sbuf_size;
820 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
821 return GNUNET_SYSERR; /* can say that this will fail */
822 /* need to compute a partial serialization, sbuf useless ... */
826 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
828 msize += sizeof(struct MetaDataEntry);
829 msize += pos->data_size;
830 if (NULL != pos->plugin_name)
831 msize += strlen (pos->plugin_name) + 1;
832 if (NULL != pos->mime_type)
833 msize += strlen (pos->mime_type) + 1;
835 size = (size_t) msize;
838 GNUNET_break (0); /* integer overflow */
839 return GNUNET_SYSERR;
841 if (size >= GNUNET_MAX_MALLOC_CHECKED)
843 /* too large to be processed */
844 return GNUNET_SYSERR;
846 ent = GNUNET_malloc (size);
847 mdata = (char *) &ent[md->item_count];
848 off = size - (md->item_count * sizeof(struct MetaDataEntry));
850 for (pos = md->items_head; NULL != pos; pos = pos->next)
852 ent[i].type = htonl ((uint32_t) pos->type);
853 ent[i].format = htonl ((uint32_t) pos->format);
854 ent[i].data_size = htonl ((uint32_t) pos->data_size);
855 if (NULL == pos->plugin_name)
858 plen = strlen (pos->plugin_name) + 1;
859 ent[i].plugin_name_len = htonl ((uint32_t) plen);
860 if (NULL == pos->mime_type)
863 mlen = strlen (pos->mime_type) + 1;
864 ent[i].mime_type_len = htonl ((uint32_t) mlen);
865 off -= pos->data_size;
866 if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
867 (EXTRACTOR_METAFORMAT_C_STRING == pos->format))
868 GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
869 GNUNET_memcpy (&mdata[off], pos->data, pos->data_size);
871 if (NULL != pos->plugin_name)
872 GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
874 if (NULL != pos->mime_type)
875 GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
878 GNUNET_assert (0 == off);
884 for (pos = md->items_head; NULL != pos; pos = pos->next)
887 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
888 comp = GNUNET_try_compression ((const char *) &ent[i],
893 if ((NULL == md->sbuf) && (0 == i))
895 /* fill 'sbuf'; this "modifies" md, but since this is only
896 * an internal cache we will cast away the 'const' instead
897 * of making the API look strange. */
898 vmd = (struct GNUNET_CONTAINER_MetaData *) md;
899 hdr = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
900 hdr->size = htonl (left);
901 hdr->entries = htonl (md->item_count);
902 if (GNUNET_YES == comp)
904 GNUNET_assert (clen < left);
905 hdr->version = htonl (2 | HEADER_COMPRESSED);
906 GNUNET_memcpy (&hdr[1], cdata, clen);
907 vmd->sbuf_size = clen + sizeof(struct MetaDataHeader);
911 hdr->version = htonl (2);
912 GNUNET_memcpy (&hdr[1], &ent[0], left);
913 vmd->sbuf_size = left + sizeof(struct MetaDataHeader);
915 vmd->sbuf = (char *) hdr;
918 if (((left + sizeof(struct MetaDataHeader)) <= max) ||
919 ((GNUNET_YES == comp) && (clen <= max)))
921 /* success, this now fits! */
922 if (GNUNET_YES == comp)
925 dst = GNUNET_malloc (clen + sizeof(struct MetaDataHeader));
926 hdr = (struct MetaDataHeader *) dst;
927 hdr->version = htonl (2 | HEADER_COMPRESSED);
928 hdr->size = htonl (left);
929 hdr->entries = htonl (md->item_count - i);
930 GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], cdata, clen);
934 rlen = clen + sizeof(struct MetaDataHeader);
939 dst = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
940 hdr = (struct MetaDataHeader *) dst;
941 hdr->version = htonl (2);
942 hdr->entries = htonl (md->item_count - i);
943 hdr->size = htonl (left);
944 GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], &ent[i], left);
946 rlen = left + sizeof(struct MetaDataHeader);
950 if (GNUNET_YES == comp)
951 GNUNET_memcpy (*target, dst, clen + sizeof(struct MetaDataHeader));
953 GNUNET_memcpy (*target, dst, left + sizeof(struct MetaDataHeader));
963 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
967 return GNUNET_SYSERR;
970 /* next iteration: ignore the corresponding meta data at the
971 * end and try again without it */
972 left -= sizeof(struct MetaDataEntry);
973 left -= pos->data_size;
974 if (NULL != pos->plugin_name)
975 left -= strlen (pos->plugin_name) + 1;
976 if (NULL != pos->mime_type)
977 left -= strlen (pos->mime_type) + 1;
979 GNUNET_free_non_null (cdata);
986 /* nothing fit, only write header! */
987 ihdr.version = htonl (2);
988 ihdr.entries = htonl (0);
989 ihdr.size = htonl (0);
991 *target = (char *) GNUNET_new (struct MetaDataHeader);
992 GNUNET_memcpy (*target, &ihdr, sizeof(struct MetaDataHeader));
993 return sizeof(struct MetaDataHeader);
998 * Get the size of the full meta-data in serialized form.
1000 * @param md metadata to inspect
1001 * @return number of bytes needed for serialization, -1 on error
1004 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
1005 GNUNET_CONTAINER_MetaData *md)
1010 if (NULL != md->sbuf)
1011 return md->sbuf_size;
1014 GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
1015 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
1023 * Deserialize meta-data. Initializes md.
1025 * @param input buffer with the serialized metadata
1026 * @param size number of bytes available in input
1027 * @return MD on success, NULL on error (i.e.
1030 struct GNUNET_CONTAINER_MetaData *
1031 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1033 struct GNUNET_CONTAINER_MetaData *md;
1034 struct MetaDataHeader hdr;
1035 struct MetaDataEntry ent;
1048 const char *meta_data;
1049 const char *plugin_name;
1050 const char *mime_type;
1051 enum EXTRACTOR_MetaFormat format;
1053 if (size < sizeof(struct MetaDataHeader))
1055 GNUNET_memcpy (&hdr, input, sizeof(struct MetaDataHeader));
1056 version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1057 compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1060 return NULL; /* null pointer */
1063 GNUNET_break_op (0); /* unsupported version */
1067 ic = ntohl (hdr.entries);
1068 dataSize = ntohl (hdr.size);
1069 if (((sizeof(struct MetaDataEntry) * ic) > dataSize) ||
1071 (dataSize / ic < sizeof(struct MetaDataEntry))))
1073 GNUNET_break_op (0);
1079 if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1081 /* make sure we don't blow our memory limit because of a mal-formed
1083 GNUNET_break_op (0);
1087 GNUNET_decompress ((const char *) &input[sizeof(struct MetaDataHeader)],
1088 size - sizeof(struct MetaDataHeader),
1092 GNUNET_break_op (0);
1100 cdata = (const char *) &input[sizeof(struct MetaDataHeader)];
1101 if (dataSize != size - sizeof(struct MetaDataHeader))
1103 GNUNET_break_op (0);
1108 md = GNUNET_CONTAINER_meta_data_create ();
1109 left = dataSize - ic * sizeof(struct MetaDataEntry);
1110 mdata = &cdata[ic * sizeof(struct MetaDataEntry)];
1111 for (i = 0; i < ic; i++)
1113 GNUNET_memcpy (&ent, &cdata[i * sizeof(struct MetaDataEntry)],
1114 sizeof(struct MetaDataEntry));
1115 format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1116 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1117 (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1118 (EXTRACTOR_METAFORMAT_BINARY != format))
1120 GNUNET_break_op (0);
1123 dlen = ntohl (ent.data_size);
1124 plen = ntohl (ent.plugin_name_len);
1125 mlen = ntohl (ent.mime_type_len);
1128 GNUNET_break_op (0);
1132 meta_data = &mdata[left];
1133 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1134 (EXTRACTOR_METAFORMAT_C_STRING == format))
1138 GNUNET_break_op (0);
1141 if ('\0' != meta_data[dlen - 1])
1143 GNUNET_break_op (0);
1149 GNUNET_break_op (0);
1153 if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1155 GNUNET_break_op (0);
1161 plugin_name = &mdata[left];
1165 GNUNET_break_op (0);
1169 if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1171 GNUNET_break_op (0);
1177 mime_type = &mdata[left];
1178 GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1179 (enum EXTRACTOR_MetaType)
1180 ntohl (ent.type), format, mime_type,
1183 GNUNET_free_non_null (data);
1188 /* end of container_meta_data.c */