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", __VA_ARGS__)
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;
169 * Meta data to associate with a file, directory or namespace.
171 struct GNUNET_CONTAINER_MetaData
174 * Head of linked list of the meta data items.
176 struct MetaItem *items_head;
179 * Tail of linked list of the meta data items.
181 struct MetaItem *items_tail;
184 * Complete serialized and compressed buffer of the items.
185 * NULL if we have not computed that buffer yet.
190 * Number of bytes in 'sbuf'. 0 if the buffer is stale.
195 * Number of items in the linked list.
197 unsigned int item_count;
203 * Create a fresh struct CONTAINER_MetaData token.
205 * @return empty meta-data container
207 struct GNUNET_CONTAINER_MetaData *
208 GNUNET_CONTAINER_meta_data_create ()
210 return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
215 * Free meta data item.
217 * @param mi item to free
220 meta_item_free (struct MetaItem *mi)
222 GNUNET_free_non_null (mi->plugin_name);
223 GNUNET_free_non_null (mi->mime_type);
224 GNUNET_free_non_null (mi->data);
230 * The meta data has changed, invalidate its serialization
233 * @param md meta data that changed
236 invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
238 if (NULL == md->sbuf)
240 GNUNET_free (md->sbuf);
249 * @param md what to free
252 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
254 struct MetaItem *pos;
258 while (NULL != (pos = md->items_head))
260 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
261 meta_item_free (pos);
263 GNUNET_free_non_null (md->sbuf);
269 * Remove all items in the container.
271 * @param md metadata to manipulate
274 GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
280 while (NULL != (mi = md->items_head))
282 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
285 GNUNET_free_non_null (md->sbuf);
286 memset (md, 0, sizeof (struct GNUNET_CONTAINER_MetaData));
291 * Test if two MDs are equal. We consider them equal if
292 * the meta types, formats and content match (we do not
293 * include the mime types and plugins names in this
296 * @param md1 first value to check
297 * @param md2 other value to check
298 * @return #GNUNET_YES if they are equal
301 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
303 const struct GNUNET_CONTAINER_MetaData
312 if (md1->item_count != md2->item_count)
314 for (i = md1->items_head; NULL != i; i = i->next)
317 for (j = md2->items_head; NULL != j; j = j->next)
319 if ((i->type == j->type) && (i->format == j->format) &&
320 (i->data_size == j->data_size) &&
321 (0 == memcmp (i->data, j->data, i->data_size)))
326 if (j->data_size < i->data_size)
327 break; /* elements are sorted by (decreasing) size... */
329 if (GNUNET_NO == found)
337 * Extend metadata. Note that the list of meta data items is
338 * sorted by size (largest first).
340 * @param md metadata to extend
341 * @param plugin_name name of the plugin that produced this value;
342 * special values can be used (i.e. '<zlib>' for zlib being
343 * used in the main libextractor library and yielding
345 * @param type libextractor-type describing the meta data
346 * @param format basic format information about data
347 * @param data_mime_type mime-type of data (not of the original file);
348 * can be NULL (if mime-type is not known)
349 * @param data actual meta-data found
350 * @param data_size number of bytes in @a data
351 * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
352 * data_mime_type and plugin_name are not considered for "exists" checks
355 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
356 const char *plugin_name,
357 enum EXTRACTOR_MetaType type,
358 enum EXTRACTOR_MetaFormat format,
359 const char *data_mime_type, const char *data,
362 struct MetaItem *pos;
366 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
367 (EXTRACTOR_METAFORMAT_C_STRING == format))
368 GNUNET_break ('\0' == data[data_size - 1]);
370 for (pos = md->items_head; NULL != pos; pos = pos->next)
372 if (pos->data_size < data_size)
373 break; /* elements are sorted by size in the list */
374 if ((pos->type == type) && (pos->data_size == data_size) &&
375 (0 == memcmp (pos->data, data, data_size)))
377 if ((NULL == pos->mime_type) && (NULL != data_mime_type))
379 pos->mime_type = GNUNET_strdup (data_mime_type);
380 invalidate_sbuf (md);
382 if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
383 (EXTRACTOR_METAFORMAT_UTF8 == format))
385 pos->format = EXTRACTOR_METAFORMAT_UTF8;
386 invalidate_sbuf (md);
388 return GNUNET_SYSERR;
392 mi = GNUNET_new (struct MetaItem);
395 mi->data_size = data_size;
397 GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
401 GNUNET_CONTAINER_DLL_insert_after (md->items_head,
406 (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
407 mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
408 mi->data = GNUNET_malloc (data_size);
409 GNUNET_memcpy (mi->data, data, data_size);
410 /* change all dir separators to POSIX style ('/') */
411 if ( (EXTRACTOR_METATYPE_FILENAME == type) ||
412 (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) )
415 while (('\0' != *p) && (p < mi->data + data_size))
422 invalidate_sbuf (md);
428 * Merge given meta data.
430 * @param cls the `struct GNUNET_CONTAINER_MetaData` to merge into
431 * @param plugin_name name of the plugin that produced this value;
432 * special values can be used (i.e. '<zlib>' for zlib being
433 * used in the main libextractor library and yielding
435 * @param type libextractor-type describing the meta data
436 * @param format basic format information about data
437 * @param data_mime_type mime-type of data (not of the original file);
438 * can be NULL (if mime-type is not known)
439 * @param data actual meta-data found
440 * @param data_size number of bytes in @a data
441 * @return 0 (to continue)
444 merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
445 enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
446 const char *data, size_t data_size)
448 struct GNUNET_CONTAINER_MetaData *md = cls;
450 (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
451 data_mime_type, data, data_size);
457 * Extend metadata. Merges the meta data from the second argument
458 * into the first, discarding duplicate key-value pairs.
460 * @param md metadata to extend
461 * @param in metadata to merge
464 GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
465 const struct GNUNET_CONTAINER_MetaData *in)
467 GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
474 * @param md metadata to manipulate
475 * @param type type of the item to remove
476 * @param data specific value to remove, NULL to remove all
477 * entries of the given type
478 * @param data_size number of bytes in @a data
479 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the item does not exist in md
482 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
483 enum EXTRACTOR_MetaType type,
484 const char *data, size_t data_size)
486 struct MetaItem *pos;
488 for (pos = md->items_head; NULL != pos; pos = pos->next)
490 if (pos->data_size < data_size)
491 break; /* items are sorted by (decreasing) size */
492 if ((pos->type == type) &&
494 ((pos->data_size == data_size) &&
495 (0 == memcmp (pos->data, data, data_size)))))
497 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
498 meta_item_free (pos);
500 invalidate_sbuf (md);
504 return GNUNET_SYSERR;
509 * Add the current time as the publication date
512 * @param md metadata to modify
515 GNUNET_CONTAINER_meta_data_add_publication_date (struct
516 GNUNET_CONTAINER_MetaData *md)
519 struct GNUNET_TIME_Absolute t;
521 t = GNUNET_TIME_absolute_get ();
522 GNUNET_CONTAINER_meta_data_delete (md,
523 EXTRACTOR_METATYPE_PUBLICATION_DATE,
525 dat = GNUNET_STRINGS_absolute_time_to_string (t);
526 GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
527 EXTRACTOR_METATYPE_PUBLICATION_DATE,
528 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
529 dat, strlen (dat) + 1);
534 * Iterate over MD entries.
536 * @param md metadata to inspect
537 * @param iter function to call on each entry
538 * @param iter_cls closure for iterator
539 * @return number of entries
542 GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
543 EXTRACTOR_MetaDataProcessor iter,
546 struct MetaItem *pos;
551 return md->item_count;
552 for (pos = md->items_head; NULL != pos; pos = pos->next)
554 iter (iter_cls, pos->plugin_name, pos->type, pos->format,
555 pos->mime_type, pos->data, pos->data_size))
556 return md->item_count;
557 return md->item_count;
562 * Get the first MD entry of the given type. Caller
563 * is responsible for freeing the return value.
564 * Also, only meta data items that are strings (0-terminated)
565 * are returned by this function.
567 * @param md metadata to inspect
568 * @param type type to look for
569 * @return NULL if no entry was found
572 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData *md,
573 enum EXTRACTOR_MetaType type)
575 struct MetaItem *pos;
579 for (pos = md->items_head; NULL != pos; pos = pos->next)
580 if ((type == pos->type) &&
581 ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
582 (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
583 return GNUNET_strdup (pos->data);
589 * Get the first matching MD entry of the given types. Caller is
590 * responsible for freeing the return value. Also, only meta data
591 * items that are strings (0-terminated) are returned by this
594 * @param md metadata to inspect
595 * @param ... -1-terminated list of types
596 * @return NULL if we do not have any such entry,
597 * otherwise client is responsible for freeing the value!
600 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
601 GNUNET_CONTAINER_MetaData *md,
614 type = va_arg (args, int);
617 if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
626 * Get a thumbnail from the meta-data (if present).
628 * @param md metadata to get the thumbnail from
629 * @param thumb will be set to the thumbnail data. Must be
630 * freed by the caller!
631 * @return number of bytes in thumbnail, 0 if not available
634 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
635 * md, unsigned char **thumb)
637 struct MetaItem *pos;
638 struct MetaItem *match;
643 for (pos = md->items_head; NULL != pos; pos = pos->next)
645 if ((NULL != pos->mime_type) &&
646 (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
647 (EXTRACTOR_METAFORMAT_BINARY == pos->format))
651 else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
652 (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
656 if ((NULL == match) || (0 == match->data_size))
658 *thumb = GNUNET_malloc (match->data_size);
659 GNUNET_memcpy (*thumb, match->data, match->data_size);
660 return match->data_size;
665 * Duplicate a `struct GNUNET_CONTAINER_MetaData`.
667 * @param md what to duplicate
668 * @return duplicate meta-data container
670 struct GNUNET_CONTAINER_MetaData *
671 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
674 struct GNUNET_CONTAINER_MetaData *ret;
675 struct MetaItem *pos;
679 ret = GNUNET_CONTAINER_meta_data_create ();
680 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
681 GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
682 pos->format, pos->mime_type, pos->data,
689 * Flag in 'version' that indicates compressed meta-data.
691 #define HEADER_COMPRESSED 0x80000000
695 * Bits in 'version' that give the version number.
697 #define HEADER_VERSION_MASK 0x7FFFFFFF
701 * Header for serialized meta data.
703 struct MetaDataHeader
706 * The version of the MD serialization. The highest bit is used to
707 * indicate compression.
709 * Version 0 is traditional (pre-0.9) meta data (unsupported)
710 * Version is 1 for a NULL pointer
711 * Version 2 is for 0.9.x (and possibly higher)
712 * Other version numbers are not yet defined.
717 * How many MD entries are there?
722 * Size of the decompressed meta data.
727 * This is followed by 'entries' values of type 'struct MetaDataEntry'
728 * and then by 'entry' plugin names, mime-types and data blocks
729 * as specified in those meta data entries.
735 * Entry of serialized meta data.
740 * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
745 * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
750 * Number of bytes of meta data.
755 * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
757 uint32_t plugin_name_len;
760 * Number of bytes in the mime type including 0-terminator. 0 for NULL.
762 uint32_t mime_type_len;
768 * Serialize meta-data to target.
770 * @param md metadata to serialize
771 * @param target where to write the serialized metadata;
772 * *target can be NULL, in which case memory is allocated
773 * @param max maximum number of bytes available in target
774 * @param opt is it ok to just write SOME of the
775 * meta-data to match the size constraint,
776 * possibly discarding some data?
777 * @return number of bytes written on success,
778 * #GNUNET_SYSERR on error (typically: not enough
782 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
783 *md, char **target, size_t max,
785 GNUNET_CONTAINER_MetaDataSerializationOptions
788 struct GNUNET_CONTAINER_MetaData *vmd;
789 struct MetaItem *pos;
790 struct MetaDataHeader ihdr;
791 struct MetaDataHeader *hdr;
792 struct MetaDataEntry *ent;
807 if (max < sizeof (struct MetaDataHeader))
808 return GNUNET_SYSERR; /* far too small */
812 if (NULL != md->sbuf)
814 /* try to use serialization cache */
815 if (md->sbuf_size <= max)
818 *target = GNUNET_malloc (md->sbuf_size);
819 GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
820 return md->sbuf_size;
822 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
823 return GNUNET_SYSERR; /* can say that this will fail */
824 /* need to compute a partial serialization, sbuf useless ... */
828 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
830 msize += sizeof (struct MetaDataEntry);
831 msize += pos->data_size;
832 if (NULL != pos->plugin_name)
833 msize += strlen (pos->plugin_name) + 1;
834 if (NULL != pos->mime_type)
835 msize += strlen (pos->mime_type) + 1;
837 size = (size_t) msize;
840 GNUNET_break (0); /* integer overflow */
841 return GNUNET_SYSERR;
843 if (size >= GNUNET_MAX_MALLOC_CHECKED)
845 /* too large to be processed */
846 return GNUNET_SYSERR;
848 ent = GNUNET_malloc (size);
849 mdata = (char *) &ent[md->item_count];
850 off = size - (md->item_count * sizeof (struct MetaDataEntry));
852 for (pos = md->items_head; NULL != pos; pos = pos->next)
854 ent[i].type = htonl ((uint32_t) pos->type);
855 ent[i].format = htonl ((uint32_t) pos->format);
856 ent[i].data_size = htonl ((uint32_t) pos->data_size);
857 if (NULL == pos->plugin_name)
860 plen = strlen (pos->plugin_name) + 1;
861 ent[i].plugin_name_len = htonl ((uint32_t) plen);
862 if (NULL == pos->mime_type)
865 mlen = strlen (pos->mime_type) + 1;
866 ent[i].mime_type_len = htonl ((uint32_t) mlen);
867 off -= pos->data_size;
868 if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
869 (EXTRACTOR_METAFORMAT_C_STRING == pos->format))
870 GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
871 GNUNET_memcpy (&mdata[off], pos->data, pos->data_size);
873 if (NULL != pos->plugin_name)
874 GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
876 if (NULL != pos->mime_type)
877 GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
880 GNUNET_assert (0 == off);
886 for (pos = md->items_head; NULL != pos; pos = pos->next)
889 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
890 comp = GNUNET_try_compression ((const char *) &ent[i],
895 if ((NULL == md->sbuf) && (0 == i))
897 /* fill 'sbuf'; this "modifies" md, but since this is only
898 * an internal cache we will cast away the 'const' instead
899 * of making the API look strange. */
900 vmd = (struct GNUNET_CONTAINER_MetaData *) md;
901 hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
902 hdr->size = htonl (left);
903 hdr->entries = htonl (md->item_count);
904 if (GNUNET_YES == comp)
906 GNUNET_assert (clen < left);
907 hdr->version = htonl (2 | HEADER_COMPRESSED);
908 GNUNET_memcpy (&hdr[1], cdata, clen);
909 vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
913 hdr->version = htonl (2);
914 GNUNET_memcpy (&hdr[1], &ent[0], left);
915 vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
917 vmd->sbuf = (char *) hdr;
920 if (((left + sizeof (struct MetaDataHeader)) <= max) ||
921 ((GNUNET_YES == comp) && (clen <= max)))
923 /* success, this now fits! */
924 if (GNUNET_YES == comp)
927 dst = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
928 hdr = (struct MetaDataHeader *) dst;
929 hdr->version = htonl (2 | HEADER_COMPRESSED);
930 hdr->size = htonl (left);
931 hdr->entries = htonl (md->item_count - i);
932 GNUNET_memcpy (&dst[sizeof (struct MetaDataHeader)], cdata, clen);
936 rlen = clen + sizeof (struct MetaDataHeader);
941 dst = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
942 hdr = (struct MetaDataHeader *) dst;
943 hdr->version = htonl (2);
944 hdr->entries = htonl (md->item_count - i);
945 hdr->size = htonl (left);
946 GNUNET_memcpy (&dst[sizeof (struct MetaDataHeader)], &ent[i], left);
948 rlen = left + sizeof (struct MetaDataHeader);
952 if (GNUNET_YES == comp)
953 GNUNET_memcpy (*target, dst, clen + sizeof (struct MetaDataHeader));
955 GNUNET_memcpy (*target, dst, left + sizeof (struct MetaDataHeader));
965 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
969 return GNUNET_SYSERR;
972 /* next iteration: ignore the corresponding meta data at the
973 * end and try again without it */
974 left -= sizeof (struct MetaDataEntry);
975 left -= pos->data_size;
976 if (NULL != pos->plugin_name)
977 left -= strlen (pos->plugin_name) + 1;
978 if (NULL != pos->mime_type)
979 left -= strlen (pos->mime_type) + 1;
981 GNUNET_free_non_null (cdata);
988 /* nothing fit, only write header! */
989 ihdr.version = htonl (2);
990 ihdr.entries = htonl (0);
991 ihdr.size = htonl (0);
993 *target = (char *) GNUNET_new (struct MetaDataHeader);
994 GNUNET_memcpy (*target, &ihdr, sizeof (struct MetaDataHeader));
995 return sizeof (struct MetaDataHeader);
1000 * Get the size of the full meta-data in serialized form.
1002 * @param md metadata to inspect
1003 * @return number of bytes needed for serialization, -1 on error
1006 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
1007 GNUNET_CONTAINER_MetaData *md)
1012 if (NULL != md->sbuf)
1013 return md->sbuf_size;
1016 GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
1017 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
1025 * Deserialize meta-data. Initializes md.
1027 * @param input buffer with the serialized metadata
1028 * @param size number of bytes available in input
1029 * @return MD on success, NULL on error (i.e.
1032 struct GNUNET_CONTAINER_MetaData *
1033 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1035 struct GNUNET_CONTAINER_MetaData *md;
1036 struct MetaDataHeader hdr;
1037 struct MetaDataEntry ent;
1050 const char *meta_data;
1051 const char *plugin_name;
1052 const char *mime_type;
1053 enum EXTRACTOR_MetaFormat format;
1055 if (size < sizeof (struct MetaDataHeader))
1057 GNUNET_memcpy (&hdr, input, sizeof (struct MetaDataHeader));
1058 version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1059 compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1062 return NULL; /* null pointer */
1065 GNUNET_break_op (0); /* unsupported version */
1069 ic = ntohl (hdr.entries);
1070 dataSize = ntohl (hdr.size);
1071 if ( ((sizeof (struct MetaDataEntry) * ic) > dataSize) ||
1073 (dataSize / ic < sizeof (struct MetaDataEntry)) ) )
1075 GNUNET_break_op (0);
1081 if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1083 /* make sure we don't blow our memory limit because of a mal-formed
1085 GNUNET_break_op (0);
1089 GNUNET_decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
1090 size - sizeof (struct MetaDataHeader),
1094 GNUNET_break_op (0);
1102 cdata = (const char *) &input[sizeof (struct MetaDataHeader)];
1103 if (dataSize != size - sizeof (struct MetaDataHeader))
1105 GNUNET_break_op (0);
1110 md = GNUNET_CONTAINER_meta_data_create ();
1111 left = dataSize - ic * sizeof (struct MetaDataEntry);
1112 mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
1113 for (i = 0; i < ic; i++)
1115 GNUNET_memcpy (&ent, &cdata[i * sizeof (struct MetaDataEntry)],
1116 sizeof (struct MetaDataEntry));
1117 format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1118 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1119 (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1120 (EXTRACTOR_METAFORMAT_BINARY != format))
1122 GNUNET_break_op (0);
1125 dlen = ntohl (ent.data_size);
1126 plen = ntohl (ent.plugin_name_len);
1127 mlen = ntohl (ent.mime_type_len);
1130 GNUNET_break_op (0);
1134 meta_data = &mdata[left];
1135 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1136 (EXTRACTOR_METAFORMAT_C_STRING == format))
1140 GNUNET_break_op (0);
1143 if ('\0' != meta_data[dlen - 1])
1145 GNUNET_break_op (0);
1151 GNUNET_break_op (0);
1155 if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1157 GNUNET_break_op (0);
1163 plugin_name = &mdata[left];
1167 GNUNET_break_op (0);
1171 if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1173 GNUNET_break_op (0);
1179 mime_type = &mdata[left];
1180 GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1181 (enum EXTRACTOR_MetaType)
1182 ntohl (ent.type), format, mime_type,
1185 GNUNET_free_non_null (data);
1190 /* end of container_meta_data.c */