/*
This file is part of GNUnet.
Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file util/container_meta_data.c
* @brief Storing of meta data
* @author Christian Grothoff
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#if HAVE_EXTRACTOR_H
#include
#endif
#include
#define LOG(kind, ...) GNUNET_log_from (kind, "util-container-meta-data", \
__VA_ARGS__)
/**
* Try to compress the given block of data using libz. Only returns
* the compressed block if compression worked and the new block is
* actually smaller. Decompress using #GNUNET_decompress().
*
* @param data block to compress; if compression
* resulted in a smaller block, the first
* bytes of data are updated to the compressed
* data
* @param old_size number of bytes in data
* @param[out] result set to the compressed data, if compression worked
* @param[out] new_size set to size of result, if compression worked
* @return #GNUNET_YES if compression reduce the size,
* #GNUNET_NO if compression did not help
*/
int
GNUNET_try_compression (const char *data,
size_t old_size,
char **result,
size_t *new_size)
{
char *tmp;
uLongf dlen;
*result = NULL;
*new_size = 0;
#ifdef compressBound
dlen = compressBound (old_size);
#else
dlen = old_size + (old_size / 100) + 20;
/* documentation says 100.1% oldSize + 12 bytes, but we
* should be able to overshoot by more to be safe */
#endif
tmp = GNUNET_malloc (dlen);
if (Z_OK ==
compress2 ((Bytef *) tmp,
&dlen,
(const Bytef *) data,
old_size, 9))
{
if (dlen < old_size)
{
*result = tmp;
*new_size = dlen;
return GNUNET_YES;
}
}
GNUNET_free (tmp);
return GNUNET_NO;
}
/**
* Decompress input, return the decompressed data as output. Dual to
* #GNUNET_try_compression(). Caller must set @a output_size to the
* number of bytes that were originally compressed.
*
* @param input compressed data
* @param input_size number of bytes in input
* @param output_size expected size of the output
* @return NULL on error, buffer of @a output_size decompressed bytes otherwise
*/
char *
GNUNET_decompress (const char *input,
size_t input_size,
size_t output_size)
{
char *output;
uLongf olen;
olen = output_size;
output = GNUNET_malloc (olen);
if (Z_OK ==
uncompress ((Bytef *) output,
&olen,
(const Bytef *) input,
input_size))
return output;
GNUNET_free (output);
return NULL;
}
/**
* Meta data item.
*/
struct MetaItem
{
/**
* This is a doubly linked list.
*/
struct MetaItem *next;
/**
* This is a doubly linked list.
*/
struct MetaItem *prev;
/**
* Name of the extracting plugin.
*/
char *plugin_name;
/**
* Mime-type of data.
*/
char *mime_type;
/**
* The actual meta data.
*/
char *data;
/**
* Number of bytes in 'data'.
*/
size_t data_size;
/**
* Type of the meta data.
*/
enum EXTRACTOR_MetaType type;
/**
* Format of the meta data.
*/
enum EXTRACTOR_MetaFormat format;
};
/**
* Meta data to associate with a file, directory or namespace.
*/
struct GNUNET_CONTAINER_MetaData
{
/**
* Head of linked list of the meta data items.
*/
struct MetaItem *items_head;
/**
* Tail of linked list of the meta data items.
*/
struct MetaItem *items_tail;
/**
* Complete serialized and compressed buffer of the items.
* NULL if we have not computed that buffer yet.
*/
char *sbuf;
/**
* Number of bytes in 'sbuf'. 0 if the buffer is stale.
*/
size_t sbuf_size;
/**
* Number of items in the linked list.
*/
unsigned int item_count;
};
/**
* Create a fresh struct CONTAINER_MetaData token.
*
* @return empty meta-data container
*/
struct GNUNET_CONTAINER_MetaData *
GNUNET_CONTAINER_meta_data_create ()
{
return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
}
/**
* Free meta data item.
*
* @param mi item to free
*/
static void
meta_item_free (struct MetaItem *mi)
{
GNUNET_free_non_null (mi->plugin_name);
GNUNET_free_non_null (mi->mime_type);
GNUNET_free_non_null (mi->data);
GNUNET_free (mi);
}
/**
* The meta data has changed, invalidate its serialization
* buffer.
*
* @param md meta data that changed
*/
static void
invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
{
if (NULL == md->sbuf)
return;
GNUNET_free (md->sbuf);
md->sbuf = NULL;
md->sbuf_size = 0;
}
/**
* Free meta data.
*
* @param md what to free
*/
void
GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
{
struct MetaItem *pos;
if (NULL == md)
return;
while (NULL != (pos = md->items_head))
{
GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
meta_item_free (pos);
}
GNUNET_free_non_null (md->sbuf);
GNUNET_free (md);
}
/**
* Remove all items in the container.
*
* @param md metadata to manipulate
*/
void
GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
{
struct MetaItem *mi;
if (NULL == md)
return;
while (NULL != (mi = md->items_head))
{
GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
meta_item_free (mi);
}
GNUNET_free_non_null (md->sbuf);
memset (md, 0, sizeof(struct GNUNET_CONTAINER_MetaData));
}
/**
* Test if two MDs are equal. We consider them equal if
* the meta types, formats and content match (we do not
* include the mime types and plugins names in this
* consideration).
*
* @param md1 first value to check
* @param md2 other value to check
* @return #GNUNET_YES if they are equal
*/
int
GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
*md1,
const struct GNUNET_CONTAINER_MetaData
*md2)
{
struct MetaItem *i;
struct MetaItem *j;
int found;
if (md1 == md2)
return GNUNET_YES;
if (md1->item_count != md2->item_count)
return GNUNET_NO;
for (i = md1->items_head; NULL != i; i = i->next)
{
found = GNUNET_NO;
for (j = md2->items_head; NULL != j; j = j->next)
{
if ((i->type == j->type) && (i->format == j->format) &&
(i->data_size == j->data_size) &&
(0 == memcmp (i->data, j->data, i->data_size)))
{
found = GNUNET_YES;
break;
}
if (j->data_size < i->data_size)
break; /* elements are sorted by (decreasing) size... */
}
if (GNUNET_NO == found)
return GNUNET_NO;
}
return GNUNET_YES;
}
/**
* Extend metadata. Note that the list of meta data items is
* sorted by size (largest first).
*
* @param md metadata to extend
* @param plugin_name name of the plugin that produced this value;
* special values can be used (i.e. '<zlib>' for zlib being
* used in the main libextractor library and yielding
* meta data).
* @param type libextractor-type describing the meta data
* @param format basic format information about data
* @param data_mime_type mime-type of data (not of the original file);
* can be NULL (if mime-type is not known)
* @param data actual meta-data found
* @param data_size number of bytes in @a data
* @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
* data_mime_type and plugin_name are not considered for "exists" checks
*/
int
GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
const char *plugin_name,
enum EXTRACTOR_MetaType type,
enum EXTRACTOR_MetaFormat format,
const char *data_mime_type, const char *data,
size_t data_size)
{
struct MetaItem *pos;
struct MetaItem *mi;
char *p;
if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
(EXTRACTOR_METAFORMAT_C_STRING == format))
GNUNET_break ('\0' == data[data_size - 1]);
for (pos = md->items_head; NULL != pos; pos = pos->next)
{
if (pos->data_size < data_size)
break; /* elements are sorted by size in the list */
if ((pos->type == type) && (pos->data_size == data_size) &&
(0 == memcmp (pos->data, data, data_size)))
{
if ((NULL == pos->mime_type) && (NULL != data_mime_type))
{
pos->mime_type = GNUNET_strdup (data_mime_type);
invalidate_sbuf (md);
}
if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
(EXTRACTOR_METAFORMAT_UTF8 == format))
{
pos->format = EXTRACTOR_METAFORMAT_UTF8;
invalidate_sbuf (md);
}
return GNUNET_SYSERR;
}
}
md->item_count++;
mi = GNUNET_new (struct MetaItem);
mi->type = type;
mi->format = format;
mi->data_size = data_size;
if (NULL == pos)
GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
md->items_tail,
mi);
else
GNUNET_CONTAINER_DLL_insert_after (md->items_head,
md->items_tail,
pos->prev,
mi);
mi->mime_type =
(NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
mi->data = GNUNET_malloc (data_size);
GNUNET_memcpy (mi->data, data, data_size);
/* change all dir separators to POSIX style ('/') */
if ((EXTRACTOR_METATYPE_FILENAME == type) ||
(EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type))
{
p = mi->data;
while (('\0' != *p) && (p < mi->data + data_size))
{
if ('\\' == *p)
*p = '/';
p++;
}
}
invalidate_sbuf (md);
return GNUNET_OK;
}
/**
* Merge given meta data.
*
* @param cls the `struct GNUNET_CONTAINER_MetaData` to merge into
* @param plugin_name name of the plugin that produced this value;
* special values can be used (i.e. '<zlib>' for zlib being
* used in the main libextractor library and yielding
* meta data).
* @param type libextractor-type describing the meta data
* @param format basic format information about data
* @param data_mime_type mime-type of data (not of the original file);
* can be NULL (if mime-type is not known)
* @param data actual meta-data found
* @param data_size number of bytes in @a data
* @return 0 (to continue)
*/
static int
merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
const char *data, size_t data_size)
{
struct GNUNET_CONTAINER_MetaData *md = cls;
(void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
data_mime_type, data, data_size);
return 0;
}
/**
* Extend metadata. Merges the meta data from the second argument
* into the first, discarding duplicate key-value pairs.
*
* @param md metadata to extend
* @param in metadata to merge
*/
void
GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
const struct GNUNET_CONTAINER_MetaData *in)
{
GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
}
/**
* Remove an item.
*
* @param md metadata to manipulate
* @param type type of the item to remove
* @param data specific value to remove, NULL to remove all
* entries of the given type
* @param data_size number of bytes in @a data
* @return #GNUNET_OK on success, #GNUNET_SYSERR if the item does not exist in md
*/
int
GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
enum EXTRACTOR_MetaType type,
const char *data, size_t data_size)
{
struct MetaItem *pos;
for (pos = md->items_head; NULL != pos; pos = pos->next)
{
if (pos->data_size < data_size)
break; /* items are sorted by (decreasing) size */
if ((pos->type == type) &&
((NULL == data) ||
((pos->data_size == data_size) &&
(0 == memcmp (pos->data, data, data_size)))))
{
GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
meta_item_free (pos);
md->item_count--;
invalidate_sbuf (md);
return GNUNET_OK;
}
}
return GNUNET_SYSERR;
}
/**
* Add the current time as the publication date
* to the meta-data.
*
* @param md metadata to modify
*/
void
GNUNET_CONTAINER_meta_data_add_publication_date (struct
GNUNET_CONTAINER_MetaData *md)
{
const char *dat;
struct GNUNET_TIME_Absolute t;
t = GNUNET_TIME_absolute_get ();
GNUNET_CONTAINER_meta_data_delete (md,
EXTRACTOR_METATYPE_PUBLICATION_DATE,
NULL, 0);
dat = GNUNET_STRINGS_absolute_time_to_string (t);
GNUNET_CONTAINER_meta_data_insert (md, "",
EXTRACTOR_METATYPE_PUBLICATION_DATE,
EXTRACTOR_METAFORMAT_UTF8, "text/plain",
dat, strlen (dat) + 1);
}
/**
* Iterate over MD entries.
*
* @param md metadata to inspect
* @param iter function to call on each entry
* @param iter_cls closure for iterator
* @return number of entries
*/
int
GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
EXTRACTOR_MetaDataProcessor iter,
void *iter_cls)
{
struct MetaItem *pos;
if (NULL == md)
return 0;
if (NULL == iter)
return md->item_count;
for (pos = md->items_head; NULL != pos; pos = pos->next)
if (0 !=
iter (iter_cls, pos->plugin_name, pos->type, pos->format,
pos->mime_type, pos->data, pos->data_size))
return md->item_count;
return md->item_count;
}
/**
* Get the first MD entry of the given type. Caller
* is responsible for freeing the return value.
* Also, only meta data items that are strings (0-terminated)
* are returned by this function.
*
* @param md metadata to inspect
* @param type type to look for
* @return NULL if no entry was found
*/
char *
GNUNET_CONTAINER_meta_data_get_by_type (const struct
GNUNET_CONTAINER_MetaData *md,
enum EXTRACTOR_MetaType type)
{
struct MetaItem *pos;
if (NULL == md)
return NULL;
for (pos = md->items_head; NULL != pos; pos = pos->next)
if ((type == pos->type) &&
((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
(pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
return GNUNET_strdup (pos->data);
return NULL;
}
/**
* Get the first matching MD entry of the given types. Caller is
* responsible for freeing the return value. Also, only meta data
* items that are strings (0-terminated) are returned by this
* function.
*
* @param md metadata to inspect
* @param ... -1-terminated list of types
* @return NULL if we do not have any such entry,
* otherwise client is responsible for freeing the value!
*/
char *
GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
GNUNET_CONTAINER_MetaData *md,
...)
{
char *ret;
va_list args;
int type;
if (NULL == md)
return NULL;
ret = NULL;
va_start (args, md);
while (1)
{
type = va_arg (args, int);
if (-1 == type)
break;
if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
break;
}
va_end (args);
return ret;
}
/**
* Get a thumbnail from the meta-data (if present).
*
* @param md metadata to get the thumbnail from
* @param thumb will be set to the thumbnail data. Must be
* freed by the caller!
* @return number of bytes in thumbnail, 0 if not available
*/
size_t
GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
*md, unsigned char **thumb)
{
struct MetaItem *pos;
struct MetaItem *match;
if (NULL == md)
return 0;
match = NULL;
for (pos = md->items_head; NULL != pos; pos = pos->next)
{
if ((NULL != pos->mime_type) &&
(0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
(EXTRACTOR_METAFORMAT_BINARY == pos->format))
{
if (NULL == match)
match = pos;
else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
(pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
match = pos;
}
}
if ((NULL == match) || (0 == match->data_size))
return 0;
*thumb = GNUNET_malloc (match->data_size);
GNUNET_memcpy (*thumb, match->data, match->data_size);
return match->data_size;
}
/**
* Duplicate a `struct GNUNET_CONTAINER_MetaData`.
*
* @param md what to duplicate
* @return duplicate meta-data container
*/
struct GNUNET_CONTAINER_MetaData *
GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
*md)
{
struct GNUNET_CONTAINER_MetaData *ret;
struct MetaItem *pos;
if (NULL == md)
return NULL;
ret = GNUNET_CONTAINER_meta_data_create ();
for (pos = md->items_tail; NULL != pos; pos = pos->prev)
GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
pos->format, pos->mime_type, pos->data,
pos->data_size);
return ret;
}
/**
* Flag in 'version' that indicates compressed meta-data.
*/
#define HEADER_COMPRESSED 0x80000000
/**
* Bits in 'version' that give the version number.
*/
#define HEADER_VERSION_MASK 0x7FFFFFFF
/**
* Header for serialized meta data.
*/
struct MetaDataHeader
{
/**
* The version of the MD serialization. The highest bit is used to
* indicate compression.
*
* Version 0 is traditional (pre-0.9) meta data (unsupported)
* Version is 1 for a NULL pointer
* Version 2 is for 0.9.x (and possibly higher)
* Other version numbers are not yet defined.
*/
uint32_t version;
/**
* How many MD entries are there?
*/
uint32_t entries;
/**
* Size of the decompressed meta data.
*/
uint32_t size;
/**
* This is followed by 'entries' values of type 'struct MetaDataEntry'
* and then by 'entry' plugin names, mime-types and data blocks
* as specified in those meta data entries.
*/
};
/**
* Entry of serialized meta data.
*/
struct MetaDataEntry
{
/**
* Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
*/
uint32_t type;
/**
* Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
*/
uint32_t format;
/**
* Number of bytes of meta data.
*/
uint32_t data_size;
/**
* Number of bytes in the plugin name including 0-terminator. 0 for NULL.
*/
uint32_t plugin_name_len;
/**
* Number of bytes in the mime type including 0-terminator. 0 for NULL.
*/
uint32_t mime_type_len;
};
/**
* Serialize meta-data to target.
*
* @param md metadata to serialize
* @param target where to write the serialized metadata;
* *target can be NULL, in which case memory is allocated
* @param max maximum number of bytes available in target
* @param opt is it ok to just write SOME of the
* meta-data to match the size constraint,
* possibly discarding some data?
* @return number of bytes written on success,
* #GNUNET_SYSERR on error (typically: not enough
* space)
*/
ssize_t
GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
*md, char **target, size_t max,
enum
GNUNET_CONTAINER_MetaDataSerializationOptions
opt)
{
struct GNUNET_CONTAINER_MetaData *vmd;
struct MetaItem *pos;
struct MetaDataHeader ihdr;
struct MetaDataHeader *hdr;
struct MetaDataEntry *ent;
char *dst;
unsigned int i;
uint64_t msize;
size_t off;
char *mdata;
char *cdata;
size_t mlen;
size_t plen;
size_t size;
size_t left;
size_t clen;
size_t rlen;
int comp;
if (max < sizeof(struct MetaDataHeader))
return GNUNET_SYSERR; /* far too small */
if (NULL == md)
return 0;
if (NULL != md->sbuf)
{
/* try to use serialization cache */
if (md->sbuf_size <= max)
{
if (NULL == *target)
*target = GNUNET_malloc (md->sbuf_size);
GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
return md->sbuf_size;
}
if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
return GNUNET_SYSERR; /* can say that this will fail */
/* need to compute a partial serialization, sbuf useless ... */
}
dst = NULL;
msize = 0;
for (pos = md->items_tail; NULL != pos; pos = pos->prev)
{
msize += sizeof(struct MetaDataEntry);
msize += pos->data_size;
if (NULL != pos->plugin_name)
msize += strlen (pos->plugin_name) + 1;
if (NULL != pos->mime_type)
msize += strlen (pos->mime_type) + 1;
}
size = (size_t) msize;
if (size != msize)
{
GNUNET_break (0); /* integer overflow */
return GNUNET_SYSERR;
}
if (size >= GNUNET_MAX_MALLOC_CHECKED)
{
/* too large to be processed */
return GNUNET_SYSERR;
}
ent = GNUNET_malloc (size);
mdata = (char *) &ent[md->item_count];
off = size - (md->item_count * sizeof(struct MetaDataEntry));
i = 0;
for (pos = md->items_head; NULL != pos; pos = pos->next)
{
ent[i].type = htonl ((uint32_t) pos->type);
ent[i].format = htonl ((uint32_t) pos->format);
ent[i].data_size = htonl ((uint32_t) pos->data_size);
if (NULL == pos->plugin_name)
plen = 0;
else
plen = strlen (pos->plugin_name) + 1;
ent[i].plugin_name_len = htonl ((uint32_t) plen);
if (NULL == pos->mime_type)
mlen = 0;
else
mlen = strlen (pos->mime_type) + 1;
ent[i].mime_type_len = htonl ((uint32_t) mlen);
off -= pos->data_size;
if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
(EXTRACTOR_METAFORMAT_C_STRING == pos->format))
GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
GNUNET_memcpy (&mdata[off], pos->data, pos->data_size);
off -= plen;
if (NULL != pos->plugin_name)
GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
off -= mlen;
if (NULL != pos->mime_type)
GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
i++;
}
GNUNET_assert (0 == off);
clen = 0;
cdata = NULL;
left = size;
i = 0;
for (pos = md->items_head; NULL != pos; pos = pos->next)
{
comp = GNUNET_NO;
if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
comp = GNUNET_try_compression ((const char *) &ent[i],
left,
&cdata,
&clen);
if ((NULL == md->sbuf) && (0 == i))
{
/* fill 'sbuf'; this "modifies" md, but since this is only
* an internal cache we will cast away the 'const' instead
* of making the API look strange. */
vmd = (struct GNUNET_CONTAINER_MetaData *) md;
hdr = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
hdr->size = htonl (left);
hdr->entries = htonl (md->item_count);
if (GNUNET_YES == comp)
{
GNUNET_assert (clen < left);
hdr->version = htonl (2 | HEADER_COMPRESSED);
GNUNET_memcpy (&hdr[1], cdata, clen);
vmd->sbuf_size = clen + sizeof(struct MetaDataHeader);
}
else
{
hdr->version = htonl (2);
GNUNET_memcpy (&hdr[1], &ent[0], left);
vmd->sbuf_size = left + sizeof(struct MetaDataHeader);
}
vmd->sbuf = (char *) hdr;
}
if (((left + sizeof(struct MetaDataHeader)) <= max) ||
((GNUNET_YES == comp) && (clen <= max)))
{
/* success, this now fits! */
if (GNUNET_YES == comp)
{
if (NULL == dst)
dst = GNUNET_malloc (clen + sizeof(struct MetaDataHeader));
hdr = (struct MetaDataHeader *) dst;
hdr->version = htonl (2 | HEADER_COMPRESSED);
hdr->size = htonl (left);
hdr->entries = htonl (md->item_count - i);
GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], cdata, clen);
GNUNET_free (cdata);
cdata = NULL;
GNUNET_free (ent);
rlen = clen + sizeof(struct MetaDataHeader);
}
else
{
if (NULL == dst)
dst = GNUNET_malloc (left + sizeof(struct MetaDataHeader));
hdr = (struct MetaDataHeader *) dst;
hdr->version = htonl (2);
hdr->entries = htonl (md->item_count - i);
hdr->size = htonl (left);
GNUNET_memcpy (&dst[sizeof(struct MetaDataHeader)], &ent[i], left);
GNUNET_free (ent);
rlen = left + sizeof(struct MetaDataHeader);
}
if (NULL != *target)
{
if (GNUNET_YES == comp)
GNUNET_memcpy (*target, dst, clen + sizeof(struct MetaDataHeader));
else
GNUNET_memcpy (*target, dst, left + sizeof(struct MetaDataHeader));
GNUNET_free (dst);
}
else
{
*target = dst;
}
return rlen;
}
if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
{
/* does not fit! */
GNUNET_free (ent);
return GNUNET_SYSERR;
}
/* next iteration: ignore the corresponding meta data at the
* end and try again without it */
left -= sizeof(struct MetaDataEntry);
left -= pos->data_size;
if (NULL != pos->plugin_name)
left -= strlen (pos->plugin_name) + 1;
if (NULL != pos->mime_type)
left -= strlen (pos->mime_type) + 1;
GNUNET_free_non_null (cdata);
cdata = NULL;
i++;
}
GNUNET_free (ent);
/* nothing fit, only write header! */
ihdr.version = htonl (2);
ihdr.entries = htonl (0);
ihdr.size = htonl (0);
if (NULL == *target)
*target = (char *) GNUNET_new (struct MetaDataHeader);
GNUNET_memcpy (*target, &ihdr, sizeof(struct MetaDataHeader));
return sizeof(struct MetaDataHeader);
}
/**
* Get the size of the full meta-data in serialized form.
*
* @param md metadata to inspect
* @return number of bytes needed for serialization, -1 on error
*/
ssize_t
GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
GNUNET_CONTAINER_MetaData *md)
{
ssize_t ret;
char *ptr;
if (NULL != md->sbuf)
return md->sbuf_size;
ptr = NULL;
ret =
GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
if (-1 != ret)
GNUNET_free (ptr);
return ret;
}
/**
* Deserialize meta-data. Initializes md.
*
* @param input buffer with the serialized metadata
* @param size number of bytes available in input
* @return MD on success, NULL on error (i.e.
* bad format)
*/
struct GNUNET_CONTAINER_MetaData *
GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
{
struct GNUNET_CONTAINER_MetaData *md;
struct MetaDataHeader hdr;
struct MetaDataEntry ent;
uint32_t ic;
uint32_t i;
char *data;
const char *cdata;
uint32_t version;
uint32_t dataSize;
int compressed;
size_t left;
uint32_t mlen;
uint32_t plen;
uint32_t dlen;
const char *mdata;
const char *meta_data;
const char *plugin_name;
const char *mime_type;
enum EXTRACTOR_MetaFormat format;
if (size < sizeof(struct MetaDataHeader))
return NULL;
GNUNET_memcpy (&hdr, input, sizeof(struct MetaDataHeader));
version = ntohl (hdr.version) & HEADER_VERSION_MASK;
compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
if (1 == version)
return NULL; /* null pointer */
if (2 != version)
{
GNUNET_break_op (0); /* unsupported version */
return NULL;
}
ic = ntohl (hdr.entries);
dataSize = ntohl (hdr.size);
if (((sizeof(struct MetaDataEntry) * ic) > dataSize) ||
((0 != ic) &&
(dataSize / ic < sizeof(struct MetaDataEntry))))
{
GNUNET_break_op (0);
return NULL;
}
if (compressed)
{
if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
{
/* make sure we don't blow our memory limit because of a mal-formed
* message... */
GNUNET_break_op (0);
return NULL;
}
data =
GNUNET_decompress ((const char *) &input[sizeof(struct MetaDataHeader)],
size - sizeof(struct MetaDataHeader),
dataSize);
if (NULL == data)
{
GNUNET_break_op (0);
return NULL;
}
cdata = data;
}
else
{
data = NULL;
cdata = (const char *) &input[sizeof(struct MetaDataHeader)];
if (dataSize != size - sizeof(struct MetaDataHeader))
{
GNUNET_break_op (0);
return NULL;
}
}
md = GNUNET_CONTAINER_meta_data_create ();
left = dataSize - ic * sizeof(struct MetaDataEntry);
mdata = &cdata[ic * sizeof(struct MetaDataEntry)];
for (i = 0; i < ic; i++)
{
GNUNET_memcpy (&ent, &cdata[i * sizeof(struct MetaDataEntry)],
sizeof(struct MetaDataEntry));
format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
(EXTRACTOR_METAFORMAT_C_STRING != format) &&
(EXTRACTOR_METAFORMAT_BINARY != format))
{
GNUNET_break_op (0);
break;
}
dlen = ntohl (ent.data_size);
plen = ntohl (ent.plugin_name_len);
mlen = ntohl (ent.mime_type_len);
if (dlen > left)
{
GNUNET_break_op (0);
break;
}
left -= dlen;
meta_data = &mdata[left];
if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
(EXTRACTOR_METAFORMAT_C_STRING == format))
{
if (0 == dlen)
{
GNUNET_break_op (0);
break;
}
if ('\0' != meta_data[dlen - 1])
{
GNUNET_break_op (0);
break;
}
}
if (plen > left)
{
GNUNET_break_op (0);
break;
}
left -= plen;
if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
{
GNUNET_break_op (0);
break;
}
if (0 == plen)
plugin_name = NULL;
else
plugin_name = &mdata[left];
if (mlen > left)
{
GNUNET_break_op (0);
break;
}
left -= mlen;
if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
{
GNUNET_break_op (0);
break;
}
if (0 == mlen)
mime_type = NULL;
else
mime_type = &mdata[left];
GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
(enum EXTRACTOR_MetaType)
ntohl (ent.type), format, mime_type,
meta_data, dlen);
}
GNUNET_free_non_null (data);
return md;
}
/* end of container_meta_data.c */