aboutsummaryrefslogtreecommitdiff
path: root/src/util/container_meta_data.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/container_meta_data.c')
-rw-r--r--src/util/container_meta_data.c1191
1 files changed, 0 insertions, 1191 deletions
diff --git a/src/util/container_meta_data.c b/src/util/container_meta_data.c
deleted file mode 100644
index 2c477db40..000000000
--- a/src/util/container_meta_data.c
+++ /dev/null
@@ -1,1191 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 GNUnet e.V.
4
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.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file util/container_meta_data.c
23 * @brief Storing of meta data
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#if HAVE_EXTRACTOR_H
30#include <extractor.h>
31#endif
32#include <zlib.h>
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "util-container-meta-data", \
35 __VA_ARGS__)
36
37
38/**
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().
42 *
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
46 * data
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
52 */
53int
54GNUNET_try_compression (const char *data,
55 size_t old_size,
56 char **result,
57 size_t *new_size)
58{
59 char *tmp;
60 uLongf dlen;
61
62 *result = NULL;
63 *new_size = 0;
64#ifdef compressBound
65 dlen = compressBound (old_size);
66#else
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 */
70#endif
71 tmp = GNUNET_malloc (dlen);
72 if (Z_OK ==
73 compress2 ((Bytef *) tmp,
74 &dlen,
75 (const Bytef *) data,
76 old_size, 9))
77 {
78 if (dlen < old_size)
79 {
80 *result = tmp;
81 *new_size = dlen;
82 return GNUNET_YES;
83 }
84 }
85 GNUNET_free (tmp);
86 return GNUNET_NO;
87}
88
89
90/**
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.
94 *
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
99 */
100char *
101GNUNET_decompress (const char *input,
102 size_t input_size,
103 size_t output_size)
104{
105 char *output;
106 uLongf olen;
107
108 olen = output_size;
109 output = GNUNET_malloc (olen);
110 if (Z_OK ==
111 uncompress ((Bytef *) output,
112 &olen,
113 (const Bytef *) input,
114 input_size))
115 return output;
116 GNUNET_free (output);
117 return NULL;
118}
119
120
121/**
122 * Meta data item.
123 */
124struct MetaItem
125{
126 /**
127 * This is a doubly linked list.
128 */
129 struct MetaItem *next;
130
131 /**
132 * This is a doubly linked list.
133 */
134 struct MetaItem *prev;
135
136 /**
137 * Name of the extracting plugin.
138 */
139 char *plugin_name;
140
141 /**
142 * Mime-type of data.
143 */
144 char *mime_type;
145
146 /**
147 * The actual meta data.
148 */
149 char *data;
150
151 /**
152 * Number of bytes in 'data'.
153 */
154 size_t data_size;
155
156 /**
157 * Type of the meta data.
158 */
159 enum EXTRACTOR_MetaType type;
160
161 /**
162 * Format of the meta data.
163 */
164 enum EXTRACTOR_MetaFormat format;
165};
166
167/**
168 * Meta data to associate with a file, directory or namespace.
169 */
170struct GNUNET_CONTAINER_MetaData
171{
172 /**
173 * Head of linked list of the meta data items.
174 */
175 struct MetaItem *items_head;
176
177 /**
178 * Tail of linked list of the meta data items.
179 */
180 struct MetaItem *items_tail;
181
182 /**
183 * Complete serialized and compressed buffer of the items.
184 * NULL if we have not computed that buffer yet.
185 */
186 char *sbuf;
187
188 /**
189 * Number of bytes in 'sbuf'. 0 if the buffer is stale.
190 */
191 size_t sbuf_size;
192
193 /**
194 * Number of items in the linked list.
195 */
196 unsigned int item_count;
197};
198
199
200/**
201 * Create a fresh struct CONTAINER_MetaData token.
202 *
203 * @return empty meta-data container
204 */
205struct GNUNET_CONTAINER_MetaData *
206GNUNET_CONTAINER_meta_data_create ()
207{
208 return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
209}
210
211
212/**
213 * Free meta data item.
214 *
215 * @param mi item to free
216 */
217static void
218meta_item_free (struct MetaItem *mi)
219{
220 GNUNET_free (mi->plugin_name);
221 GNUNET_free (mi->mime_type);
222 GNUNET_free (mi->data);
223 GNUNET_free (mi);
224}
225
226
227/**
228 * The meta data has changed, invalidate its serialization
229 * buffer.
230 *
231 * @param md meta data that changed
232 */
233static void
234invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
235{
236 if (NULL == md->sbuf)
237 return;
238 GNUNET_free (md->sbuf);
239 md->sbuf = NULL;
240 md->sbuf_size = 0;
241}
242
243
244/**
245 * Free meta data.
246 *
247 * @param md what to free
248 */
249void
250GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
251{
252 struct MetaItem *pos;
253
254 if (NULL == md)
255 return;
256 while (NULL != (pos = md->items_head))
257 {
258 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
259 meta_item_free (pos);
260 }
261 GNUNET_free (md->sbuf);
262 GNUNET_free (md);
263}
264
265
266/**
267 * Remove all items in the container.
268 *
269 * @param md metadata to manipulate
270 */
271void
272GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
273{
274 struct MetaItem *mi;
275
276 if (NULL == md)
277 return;
278 while (NULL != (mi = md->items_head))
279 {
280 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
281 meta_item_free (mi);
282 }
283 GNUNET_free (md->sbuf);
284 memset (md, 0, sizeof(struct GNUNET_CONTAINER_MetaData));
285}
286
287
288/**
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
292 * consideration).
293 *
294 * @param md1 first value to check
295 * @param md2 other value to check
296 * @return #GNUNET_YES if they are equal
297 */
298int
299GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
300 *md1,
301 const struct GNUNET_CONTAINER_MetaData
302 *md2)
303{
304 struct MetaItem *i;
305 struct MetaItem *j;
306 int found;
307
308 if (md1 == md2)
309 return GNUNET_YES;
310 if (md1->item_count != md2->item_count)
311 return GNUNET_NO;
312 for (i = md1->items_head; NULL != i; i = i->next)
313 {
314 found = GNUNET_NO;
315 for (j = md2->items_head; NULL != j; j = j->next)
316 {
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)))
320 {
321 found = GNUNET_YES;
322 break;
323 }
324 if (j->data_size < i->data_size)
325 break; /* elements are sorted by (decreasing) size... */
326 }
327 if (GNUNET_NO == found)
328 return GNUNET_NO;
329 }
330 return GNUNET_YES;
331}
332
333
334/**
335 * Extend metadata. Note that the list of meta data items is
336 * sorted by size (largest first).
337 *
338 * @param md metadata to extend
339 * @param plugin_name name of the plugin that produced this value;
340 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
341 * used in the main libextractor library and yielding
342 * meta data).
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
351 */
352int
353GNUNET_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,
358 size_t data_size)
359{
360 struct MetaItem *pos;
361 struct MetaItem *mi;
362 char *p;
363
364 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
365 (EXTRACTOR_METAFORMAT_C_STRING == format))
366 GNUNET_break ('\0' == data[data_size - 1]);
367
368 for (pos = md->items_head; NULL != pos; pos = pos->next)
369 {
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)))
374 {
375 if ((NULL == pos->mime_type) && (NULL != data_mime_type))
376 {
377 pos->mime_type = GNUNET_strdup (data_mime_type);
378 invalidate_sbuf (md);
379 }
380 if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
381 (EXTRACTOR_METAFORMAT_UTF8 == format))
382 {
383 pos->format = EXTRACTOR_METAFORMAT_UTF8;
384 invalidate_sbuf (md);
385 }
386 return GNUNET_SYSERR;
387 }
388 }
389 md->item_count++;
390 mi = GNUNET_new (struct MetaItem);
391 mi->type = type;
392 mi->format = format;
393 mi->data_size = data_size;
394 if (NULL == pos)
395 GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
396 md->items_tail,
397 mi);
398 else
399 GNUNET_CONTAINER_DLL_insert_after (md->items_head,
400 md->items_tail,
401 pos->prev,
402 mi);
403 mi->mime_type =
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))
411 {
412 p = mi->data;
413 while (('\0' != *p) && (p < mi->data + data_size))
414 {
415 if ('\\' == *p)
416 *p = '/';
417 p++;
418 }
419 }
420 invalidate_sbuf (md);
421 return GNUNET_OK;
422}
423
424
425/**
426 * Merge given meta data.
427 *
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 (e.g. '&lt;zlib&gt;' for zlib being
431 * used in the main libextractor library and yielding
432 * meta data).
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)
440 */
441static int
442merge_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)
445{
446 struct GNUNET_CONTAINER_MetaData *md = cls;
447
448 (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
449 data_mime_type, data, data_size);
450 return 0;
451}
452
453
454/**
455 * Extend metadata. Merges the meta data from the second argument
456 * into the first, discarding duplicate key-value pairs.
457 *
458 * @param md metadata to extend
459 * @param in metadata to merge
460 */
461void
462GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
463 const struct GNUNET_CONTAINER_MetaData *in)
464{
465 GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
466}
467
468
469/**
470 * Remove an item.
471 *
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
478 */
479int
480GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
481 enum EXTRACTOR_MetaType type,
482 const char *data, size_t data_size)
483{
484 struct MetaItem *pos;
485
486 for (pos = md->items_head; NULL != pos; pos = pos->next)
487 {
488 if (pos->data_size < data_size)
489 break; /* items are sorted by (decreasing) size */
490 if ((pos->type == type) &&
491 ((NULL == data) ||
492 ((pos->data_size == data_size) &&
493 (0 == memcmp (pos->data, data, data_size)))))
494 {
495 GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
496 meta_item_free (pos);
497 md->item_count--;
498 invalidate_sbuf (md);
499 return GNUNET_OK;
500 }
501 }
502 return GNUNET_SYSERR;
503}
504
505
506/**
507 * Add the current time as the publication date
508 * to the meta-data.
509 *
510 * @param md metadata to modify
511 */
512void
513GNUNET_CONTAINER_meta_data_add_publication_date (struct
514 GNUNET_CONTAINER_MetaData *md)
515{
516 const char *dat;
517 struct GNUNET_TIME_Absolute t;
518
519 t = GNUNET_TIME_absolute_get ();
520 GNUNET_CONTAINER_meta_data_delete (md,
521 EXTRACTOR_METATYPE_PUBLICATION_DATE,
522 NULL, 0);
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);
528}
529
530
531/**
532 * Iterate over MD entries.
533 *
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
538 */
539int
540GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
541 EXTRACTOR_MetaDataProcessor iter,
542 void *iter_cls)
543{
544 struct MetaItem *pos;
545
546 if (NULL == md)
547 return 0;
548 if (NULL == iter)
549 return md->item_count;
550 for (pos = md->items_head; NULL != pos; pos = pos->next)
551 if (0 !=
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;
556}
557
558
559/**
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.
564 *
565 * @param md metadata to inspect
566 * @param type type to look for
567 * @return NULL if no entry was found
568 */
569char *
570GNUNET_CONTAINER_meta_data_get_by_type (const struct
571 GNUNET_CONTAINER_MetaData *md,
572 enum EXTRACTOR_MetaType type)
573{
574 struct MetaItem *pos;
575
576 if (NULL == md)
577 return NULL;
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);
583 return NULL;
584}
585
586
587/**
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
591 * function.
592 *
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!
597 */
598char *
599GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
600 GNUNET_CONTAINER_MetaData *md,
601 ...)
602{
603 char *ret;
604 va_list args;
605 int type;
606
607 if (NULL == md)
608 return NULL;
609 ret = NULL;
610 va_start (args, md);
611 while (1)
612 {
613 type = va_arg (args, int);
614 if (-1 == type)
615 break;
616 if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
617 break;
618 }
619 va_end (args);
620 return ret;
621}
622
623
624/**
625 * Get a thumbnail from the meta-data (if present).
626 *
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
631 */
632size_t
633GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
634 *md, unsigned char **thumb)
635{
636 struct MetaItem *pos;
637 struct MetaItem *match;
638
639 if (NULL == md)
640 return 0;
641 match = NULL;
642 for (pos = md->items_head; NULL != pos; pos = pos->next)
643 {
644 if ((NULL != pos->mime_type) &&
645 (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
646 (EXTRACTOR_METAFORMAT_BINARY == pos->format))
647 {
648 if (NULL == match)
649 match = pos;
650 else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
651 (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
652 match = pos;
653 }
654 }
655 if ((NULL == match) || (0 == match->data_size))
656 return 0;
657 *thumb = GNUNET_malloc (match->data_size);
658 GNUNET_memcpy (*thumb, match->data, match->data_size);
659 return match->data_size;
660}
661
662
663/**
664 * Duplicate a `struct GNUNET_CONTAINER_MetaData`.
665 *
666 * @param md what to duplicate
667 * @return duplicate meta-data container
668 */
669struct GNUNET_CONTAINER_MetaData *
670GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
671 *md)
672{
673 struct GNUNET_CONTAINER_MetaData *ret;
674 struct MetaItem *pos;
675
676 if (NULL == md)
677 return NULL;
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,
682 pos->data_size);
683 return ret;
684}
685
686
687/**
688 * Flag in 'version' that indicates compressed meta-data.
689 */
690#define HEADER_COMPRESSED 0x80000000
691
692
693/**
694 * Bits in 'version' that give the version number.
695 */
696#define HEADER_VERSION_MASK 0x7FFFFFFF
697
698
699/**
700 * Header for serialized meta data.
701 */
702struct MetaDataHeader
703{
704 /**
705 * The version of the MD serialization. The highest bit is used to
706 * indicate compression.
707 *
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.
712 */
713 uint32_t version;
714
715 /**
716 * How many MD entries are there?
717 */
718 uint32_t entries;
719
720 /**
721 * Size of the decompressed meta data.
722 */
723 uint32_t size;
724
725 /**
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.
729 */
730};
731
732
733/**
734 * Entry of serialized meta data.
735 */
736struct MetaDataEntry
737{
738 /**
739 * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
740 */
741 uint32_t type;
742
743 /**
744 * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
745 */
746 uint32_t format;
747
748 /**
749 * Number of bytes of meta data.
750 */
751 uint32_t data_size;
752
753 /**
754 * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
755 */
756 uint32_t plugin_name_len;
757
758 /**
759 * Number of bytes in the mime type including 0-terminator. 0 for NULL.
760 */
761 uint32_t mime_type_len;
762};
763
764
765/**
766 * Serialize meta-data to target.
767 *
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
777 * space)
778 */
779ssize_t
780GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
781 *md, char **target, size_t max,
782 enum
783 GNUNET_CONTAINER_MetaDataSerializationOptions
784 opt)
785{
786 struct GNUNET_CONTAINER_MetaData *vmd;
787 struct MetaItem *pos;
788 struct MetaDataHeader ihdr;
789 struct MetaDataHeader *hdr;
790 struct MetaDataEntry *ent;
791 char *dst;
792 unsigned int i;
793 uint64_t msize;
794 size_t off;
795 char *mdata;
796 char *cdata;
797 size_t mlen;
798 size_t plen;
799 size_t size;
800 size_t left;
801 size_t clen;
802 size_t rlen;
803 int comp;
804
805 if (max < sizeof(struct MetaDataHeader))
806 return GNUNET_SYSERR; /* far too small */
807 if (NULL == md)
808 return 0;
809
810 if (NULL != md->sbuf)
811 {
812 /* try to use serialization cache */
813 if (md->sbuf_size <= max)
814 {
815 if (NULL == *target)
816 *target = GNUNET_malloc (md->sbuf_size);
817 GNUNET_memcpy (*target, md->sbuf, md->sbuf_size);
818 return md->sbuf_size;
819 }
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 ... */
823 }
824 dst = NULL;
825 msize = 0;
826 for (pos = md->items_tail; NULL != pos; pos = pos->prev)
827 {
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;
834 }
835 size = (size_t) msize;
836 if (size != msize)
837 {
838 GNUNET_break (0); /* integer overflow */
839 return GNUNET_SYSERR;
840 }
841 if (size >= GNUNET_MAX_MALLOC_CHECKED)
842 {
843 /* too large to be processed */
844 return GNUNET_SYSERR;
845 }
846 ent = GNUNET_malloc (size);
847 mdata = (char *) &ent[md->item_count];
848 off = size - (md->item_count * sizeof(struct MetaDataEntry));
849 i = 0;
850 for (pos = md->items_head; NULL != pos; pos = pos->next)
851 {
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)
856 plen = 0;
857 else
858 plen = strlen (pos->plugin_name) + 1;
859 ent[i].plugin_name_len = htonl ((uint32_t) plen);
860 if (NULL == pos->mime_type)
861 mlen = 0;
862 else
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);
870 off -= plen;
871 if (NULL != pos->plugin_name)
872 GNUNET_memcpy (&mdata[off], pos->plugin_name, plen);
873 off -= mlen;
874 if (NULL != pos->mime_type)
875 GNUNET_memcpy (&mdata[off], pos->mime_type, mlen);
876 i++;
877 }
878 GNUNET_assert (0 == off);
879
880 clen = 0;
881 cdata = NULL;
882 left = size;
883 i = 0;
884 for (pos = md->items_head; NULL != pos; pos = pos->next)
885 {
886 comp = GNUNET_NO;
887 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
888 comp = GNUNET_try_compression ((const char *) &ent[i],
889 left,
890 &cdata,
891 &clen);
892
893 if ((NULL == md->sbuf) && (0 == i))
894 {
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)
903 {
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);
908 }
909 else
910 {
911 hdr->version = htonl (2);
912 GNUNET_memcpy (&hdr[1], &ent[0], left);
913 vmd->sbuf_size = left + sizeof(struct MetaDataHeader);
914 }
915 vmd->sbuf = (char *) hdr;
916 }
917
918 if (((left + sizeof(struct MetaDataHeader)) <= max) ||
919 ((GNUNET_YES == comp) && (clen <= max)))
920 {
921 /* success, this now fits! */
922 if (GNUNET_YES == comp)
923 {
924 if (NULL == dst)
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);
931 GNUNET_free (cdata);
932 cdata = NULL;
933 GNUNET_free (ent);
934 rlen = clen + sizeof(struct MetaDataHeader);
935 }
936 else
937 {
938 if (NULL == dst)
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);
945 GNUNET_free (ent);
946 rlen = left + sizeof(struct MetaDataHeader);
947 }
948 if (NULL != *target)
949 {
950 if (GNUNET_YES == comp)
951 GNUNET_memcpy (*target, dst, clen + sizeof(struct MetaDataHeader));
952 else
953 GNUNET_memcpy (*target, dst, left + sizeof(struct MetaDataHeader));
954 GNUNET_free (dst);
955 }
956 else
957 {
958 *target = dst;
959 }
960 return rlen;
961 }
962
963 if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
964 {
965 /* does not fit! */
966 GNUNET_free (ent);
967 if (NULL != cdata)
968 GNUNET_free (cdata);
969 cdata = NULL;
970 return GNUNET_SYSERR;
971 }
972
973 /* next iteration: ignore the corresponding meta data at the
974 * end and try again without it */
975 left -= sizeof(struct MetaDataEntry);
976 left -= pos->data_size;
977 if (NULL != pos->plugin_name)
978 left -= strlen (pos->plugin_name) + 1;
979 if (NULL != pos->mime_type)
980 left -= strlen (pos->mime_type) + 1;
981
982 if (NULL != cdata)
983 GNUNET_free (cdata);
984 cdata = NULL;
985 i++;
986 }
987 GNUNET_free (ent);
988
989 /* nothing fit, only write header! */
990 ihdr.version = htonl (2);
991 ihdr.entries = htonl (0);
992 ihdr.size = htonl (0);
993 if (NULL == *target)
994 *target = (char *) GNUNET_new (struct MetaDataHeader);
995 GNUNET_memcpy (*target, &ihdr, sizeof(struct MetaDataHeader));
996 return sizeof(struct MetaDataHeader);
997}
998
999
1000/**
1001 * Get the size of the full meta-data in serialized form.
1002 *
1003 * @param md metadata to inspect
1004 * @return number of bytes needed for serialization, -1 on error
1005 */
1006ssize_t
1007GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
1008 GNUNET_CONTAINER_MetaData *md)
1009{
1010 ssize_t ret;
1011 char *ptr;
1012
1013 if (NULL != md->sbuf)
1014 return md->sbuf_size;
1015 ptr = NULL;
1016 ret =
1017 GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
1018 GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
1019 if (-1 != ret)
1020 GNUNET_free (ptr);
1021 return ret;
1022}
1023
1024
1025/**
1026 * Deserialize meta-data. Initializes md.
1027 *
1028 * @param input buffer with the serialized metadata
1029 * @param size number of bytes available in input
1030 * @return MD on success, NULL on error (i.e.
1031 * bad format)
1032 */
1033struct GNUNET_CONTAINER_MetaData *
1034GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1035{
1036 struct GNUNET_CONTAINER_MetaData *md;
1037 struct MetaDataHeader hdr;
1038 struct MetaDataEntry ent;
1039 uint32_t ic;
1040 uint32_t i;
1041 char *data;
1042 const char *cdata;
1043 uint32_t version;
1044 uint32_t dataSize;
1045 int compressed;
1046 size_t left;
1047 uint32_t mlen;
1048 uint32_t plen;
1049 uint32_t dlen;
1050 const char *mdata;
1051 const char *meta_data;
1052 const char *plugin_name;
1053 const char *mime_type;
1054 enum EXTRACTOR_MetaFormat format;
1055
1056 if (size < sizeof(struct MetaDataHeader))
1057 return NULL;
1058 GNUNET_memcpy (&hdr, input, sizeof(struct MetaDataHeader));
1059 version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1060 compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1061
1062 if (1 == version)
1063 return NULL; /* null pointer */
1064 if (2 != version)
1065 {
1066 GNUNET_break_op (0); /* unsupported version */
1067 return NULL;
1068 }
1069
1070 ic = ntohl (hdr.entries);
1071 dataSize = ntohl (hdr.size);
1072 if (((sizeof(struct MetaDataEntry) * ic) > dataSize) ||
1073 ((0 != ic) &&
1074 (dataSize / ic < sizeof(struct MetaDataEntry))))
1075 {
1076 GNUNET_break_op (0);
1077 return NULL;
1078 }
1079
1080 if (compressed)
1081 {
1082 if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1083 {
1084 /* make sure we don't blow our memory limit because of a mal-formed
1085 * message... */
1086 GNUNET_break_op (0);
1087 return NULL;
1088 }
1089 data =
1090 GNUNET_decompress ((const char *) &input[sizeof(struct MetaDataHeader)],
1091 size - sizeof(struct MetaDataHeader),
1092 dataSize);
1093 if (NULL == data)
1094 {
1095 GNUNET_break_op (0);
1096 return NULL;
1097 }
1098 cdata = data;
1099 }
1100 else
1101 {
1102 data = NULL;
1103 cdata = (const char *) &input[sizeof(struct MetaDataHeader)];
1104 if (dataSize != size - sizeof(struct MetaDataHeader))
1105 {
1106 GNUNET_break_op (0);
1107 return NULL;
1108 }
1109 }
1110
1111 md = GNUNET_CONTAINER_meta_data_create ();
1112 left = dataSize - ic * sizeof(struct MetaDataEntry);
1113 mdata = &cdata[ic * sizeof(struct MetaDataEntry)];
1114 for (i = 0; i < ic; i++)
1115 {
1116 GNUNET_memcpy (&ent, &cdata[i * sizeof(struct MetaDataEntry)],
1117 sizeof(struct MetaDataEntry));
1118 format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1119 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1120 (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1121 (EXTRACTOR_METAFORMAT_BINARY != format))
1122 {
1123 GNUNET_break_op (0);
1124 break;
1125 }
1126 dlen = ntohl (ent.data_size);
1127 plen = ntohl (ent.plugin_name_len);
1128 mlen = ntohl (ent.mime_type_len);
1129 if (dlen > left)
1130 {
1131 GNUNET_break_op (0);
1132 break;
1133 }
1134 left -= dlen;
1135 meta_data = &mdata[left];
1136 if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1137 (EXTRACTOR_METAFORMAT_C_STRING == format))
1138 {
1139 if (0 == dlen)
1140 {
1141 GNUNET_break_op (0);
1142 break;
1143 }
1144 if ('\0' != meta_data[dlen - 1])
1145 {
1146 GNUNET_break_op (0);
1147 break;
1148 }
1149 }
1150 if (plen > left)
1151 {
1152 GNUNET_break_op (0);
1153 break;
1154 }
1155 left -= plen;
1156 if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1157 {
1158 GNUNET_break_op (0);
1159 break;
1160 }
1161 if (0 == plen)
1162 plugin_name = NULL;
1163 else
1164 plugin_name = &mdata[left];
1165
1166 if (mlen > left)
1167 {
1168 GNUNET_break_op (0);
1169 break;
1170 }
1171 left -= mlen;
1172 if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1173 {
1174 GNUNET_break_op (0);
1175 break;
1176 }
1177 if (0 == mlen)
1178 mime_type = NULL;
1179 else
1180 mime_type = &mdata[left];
1181 GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1182 (enum EXTRACTOR_MetaType)
1183 ntohl (ent.type), format, mime_type,
1184 meta_data, dlen);
1185 }
1186 GNUNET_free (data);
1187 return md;
1188}
1189
1190
1191/* end of container_meta_data.c */