-cosmetics, trying to narrow down deserialization error
[oweals/gnunet.git] / src / util / container_meta_data.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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_common.h"
29 #include "gnunet_container_lib.h"
30 #include "gnunet_strings_lib.h"
31 #include "gnunet_time_lib.h"
32 #include <extractor.h>
33 #include <zlib.h>
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
36
37 /**
38  * Meta data item.
39  */
40 struct MetaItem
41 {
42   /**
43    * This is a doubly linked list.
44    */
45   struct MetaItem *next;
46
47   /**
48    * This is a doubly linked list.
49    */
50   struct MetaItem *prev;
51
52   /**
53    * Name of the extracting plugin.
54    */
55   char *plugin_name;
56
57   /**
58    * Mime-type of data.
59    */
60   char *mime_type;
61
62   /**
63    * The actual meta data.
64    */
65   char *data;
66
67   /**
68    * Number of bytes in 'data'.
69    */
70   size_t data_size;
71
72   /**
73    * Type of the meta data.
74    */
75   enum EXTRACTOR_MetaType type;
76
77   /**
78    * Format of the meta data.
79    */
80   enum EXTRACTOR_MetaFormat format;
81
82 };
83
84 /**
85  * Meta data to associate with a file, directory or namespace.
86  */
87 struct GNUNET_CONTAINER_MetaData
88 {
89   /**
90    * Head of linked list of the meta data items.
91    */
92   struct MetaItem *items_head;
93
94   /**
95    * Tail of linked list of the meta data items.
96    */
97   struct MetaItem *items_tail;
98
99   /**
100    * Complete serialized and compressed buffer of the items.
101    * NULL if we have not computed that buffer yet.
102    */
103   char *sbuf;
104
105   /**
106    * Number of bytes in 'sbuf'. 0 if the buffer is stale.
107    */
108   size_t sbuf_size;
109
110   /**
111    * Number of items in the linked list.
112    */
113   unsigned int item_count;
114
115 };
116
117
118 /**
119  * Create a fresh struct CONTAINER_MetaData token.
120  *
121  * @return empty meta-data container
122  */
123 struct GNUNET_CONTAINER_MetaData *
124 GNUNET_CONTAINER_meta_data_create ()
125 {
126   return GNUNET_new (struct GNUNET_CONTAINER_MetaData);
127 }
128
129
130 /**
131  * Free meta data item.
132  *
133  * @param mi item to free
134  */
135 static void
136 meta_item_free (struct MetaItem *mi)
137 {
138   GNUNET_free_non_null (mi->plugin_name);
139   GNUNET_free_non_null (mi->mime_type);
140   GNUNET_free_non_null (mi->data);
141   GNUNET_free (mi);
142 }
143
144
145 /**
146  * The meta data has changed, invalidate its serialization
147  * buffer.
148  *
149  * @param md meta data that changed
150  */
151 static void
152 invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
153 {
154   if (NULL == md->sbuf)
155     return;
156   GNUNET_free (md->sbuf);
157   md->sbuf = NULL;
158   md->sbuf_size = 0;
159 }
160
161
162 /**
163  * Free meta data.
164  *
165  * @param md what to free
166  */
167 void
168 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
169 {
170   struct MetaItem *pos;
171
172   if (NULL == md)
173     return;
174   while (NULL != (pos = md->items_head))
175   {
176     GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
177     meta_item_free (pos);
178   }
179   GNUNET_free_non_null (md->sbuf);
180   GNUNET_free (md);
181 }
182
183
184 /**
185  * Remove all items in the container.
186  *
187  * @param md metadata to manipulate
188  */
189 void
190 GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
191 {
192   struct MetaItem *mi;
193
194   if (NULL == md)
195     return;
196   while (NULL != (mi = md->items_head))
197   {
198     GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, mi);
199     meta_item_free (mi);
200   }
201   GNUNET_free_non_null (md->sbuf);
202   memset (md, 0, sizeof (struct GNUNET_CONTAINER_MetaData));
203 }
204
205
206 /**
207  * Test if two MDs are equal.  We consider them equal if
208  * the meta types, formats and content match (we do not
209  * include the mime types and plugins names in this
210  * consideration).
211  *
212  * @param md1 first value to check
213  * @param md2 other value to check
214  * @return GNUNET_YES if they are equal
215  */
216 int
217 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
218                                        *md1,
219                                        const struct GNUNET_CONTAINER_MetaData
220                                        *md2)
221 {
222   struct MetaItem *i;
223   struct MetaItem *j;
224   int found;
225
226   if (md1 == md2)
227     return GNUNET_YES;
228   if (md1->item_count != md2->item_count)
229     return GNUNET_NO;
230   for (i = md1->items_head; NULL != i; i = i->next)
231   {
232     found = GNUNET_NO;
233     for (j = md2->items_head; NULL != j; j = j->next)
234     {
235       if ((i->type == j->type) && (i->format == j->format) &&
236           (i->data_size == j->data_size) &&
237           (0 == memcmp (i->data, j->data, i->data_size)))
238       {
239         found = GNUNET_YES;
240         break;
241       }
242       if (j->data_size < i->data_size)
243         break; /* elements are sorted by (decreasing) size... */
244     }
245     if (GNUNET_NO == found)
246       return GNUNET_NO;
247   }
248   return GNUNET_YES;
249 }
250
251
252 /**
253  * Extend metadata.  Note that the list of meta data items is
254  * sorted by size (largest first).
255  *
256  * @param md metadata to extend
257  * @param plugin_name name of the plugin that produced this value;
258  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
259  *        used in the main libextractor library and yielding
260  *        meta data).
261  * @param type libextractor-type describing the meta data
262  * @param format basic format information about data
263  * @param data_mime_type mime-type of data (not of the original file);
264  *        can be NULL (if mime-type is not known)
265  * @param data actual meta-data found
266  * @param data_size number of bytes in @a data
267  * @return #GNUNET_OK on success, #GNUNET_SYSERR if this entry already exists
268  *         data_mime_type and plugin_name are not considered for "exists" checks
269  */
270 int
271 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
272                                    const char *plugin_name,
273                                    enum EXTRACTOR_MetaType type,
274                                    enum EXTRACTOR_MetaFormat format,
275                                    const char *data_mime_type, const char *data,
276                                    size_t data_size)
277 {
278   struct MetaItem *pos;
279   struct MetaItem *mi;
280   char *p;
281
282   if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
283       (EXTRACTOR_METAFORMAT_C_STRING == format))
284     GNUNET_break ('\0' == data[data_size - 1]);
285
286   for (pos = md->items_head; NULL != pos; pos = pos->next)
287   {
288     if (pos->data_size < data_size)
289       break; /* elements are sorted by size in the list */
290     if ((pos->type == type) && (pos->data_size == data_size) &&
291         (0 == memcmp (pos->data, data, data_size)))
292     {
293       if ((NULL == pos->mime_type) && (NULL != data_mime_type))
294       {
295         pos->mime_type = GNUNET_strdup (data_mime_type);
296         invalidate_sbuf (md);
297       }
298       if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
299           (EXTRACTOR_METAFORMAT_UTF8 == format))
300       {
301         pos->format = EXTRACTOR_METAFORMAT_UTF8;
302         invalidate_sbuf (md);
303       }
304       return GNUNET_SYSERR;
305     }
306   }
307   md->item_count++;
308   mi = GNUNET_new (struct MetaItem);
309   mi->type = type;
310   mi->format = format;
311   mi->data_size = data_size;
312   if (NULL == pos)
313     GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
314                                       md->items_tail,
315                                       mi);
316   else
317     GNUNET_CONTAINER_DLL_insert_after (md->items_head,
318                                        md->items_tail,
319                                        pos->prev,
320                                        mi);
321   mi->mime_type =
322       (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
323   mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
324   mi->data = GNUNET_malloc (data_size);
325   memcpy (mi->data, data, data_size);
326   /* change all dir separators to POSIX style ('/') */
327   if ( (EXTRACTOR_METATYPE_FILENAME == type) ||
328        (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) )
329   {
330     p = mi->data;
331     while (('\0' != *p) && (p < mi->data + data_size))
332     {
333       if ('\\' == *p)
334         *p = '/';
335       p++;
336     }
337   }
338   invalidate_sbuf (md);
339   return GNUNET_OK;
340 }
341
342
343 /**
344  * Merge given meta data.
345  *
346  * @param cls the 'struct GNUNET_CONTAINER_MetaData' to merge into
347  * @param plugin_name name of the plugin that produced this value;
348  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
349  *        used in the main libextractor library and yielding
350  *        meta data).
351  * @param type libextractor-type describing the meta data
352  * @param format basic format information about data
353  * @param data_mime_type mime-type of data (not of the original file);
354  *        can be NULL (if mime-type is not known)
355  * @param data actual meta-data found
356  * @param data_size number of bytes in @a data
357  * @return 0 (to continue)
358  */
359 static int
360 merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
361               enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
362               const char *data, size_t data_size)
363 {
364   struct GNUNET_CONTAINER_MetaData *md = cls;
365
366   (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
367                                             data_mime_type, data, data_size);
368   return 0;
369 }
370
371
372 /**
373  * Extend metadata.  Merges the meta data from the second argument
374  * into the first, discarding duplicate key-value pairs.
375  *
376  * @param md metadata to extend
377  * @param in metadata to merge
378  */
379 void
380 GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
381                                   const struct GNUNET_CONTAINER_MetaData *in)
382 {
383   GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
384 }
385
386
387 /**
388  * Remove an item.
389  *
390  * @param md metadata to manipulate
391  * @param type type of the item to remove
392  * @param data specific value to remove, NULL to remove all
393  *        entries of the given type
394  * @param data_size number of bytes in @a data
395  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the item does not exist in md
396  */
397 int
398 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
399                                    enum EXTRACTOR_MetaType type,
400                                    const char *data, size_t data_size)
401 {
402   struct MetaItem *pos;
403
404   for (pos = md->items_head; NULL != pos; pos = pos->next)
405   {
406     if (pos->data_size < data_size)
407       break; /* items are sorted by (decreasing) size */
408     if ((pos->type == type) &&
409         ((NULL == data) ||
410          ((pos->data_size == data_size) &&
411           (0 == memcmp (pos->data, data, data_size)))))
412     {
413       GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
414       meta_item_free (pos);
415       md->item_count--;
416       invalidate_sbuf (md);
417       return GNUNET_OK;
418     }
419   }
420   return GNUNET_SYSERR;
421 }
422
423
424 /**
425  * Add the current time as the publication date
426  * to the meta-data.
427  *
428  * @param md metadata to modify
429  */
430 void
431 GNUNET_CONTAINER_meta_data_add_publication_date (struct
432                                                  GNUNET_CONTAINER_MetaData *md)
433 {
434   const char *dat;
435   struct GNUNET_TIME_Absolute t;
436
437   t = GNUNET_TIME_absolute_get ();
438   GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_METATYPE_PUBLICATION_DATE,
439                                      NULL, 0);
440   dat = GNUNET_STRINGS_absolute_time_to_string (t);
441   GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
442                                      EXTRACTOR_METATYPE_PUBLICATION_DATE,
443                                      EXTRACTOR_METAFORMAT_UTF8, "text/plain",
444                                      dat, strlen (dat) + 1);
445 }
446
447
448 /**
449  * Iterate over MD entries.
450  *
451  * @param md metadata to inspect
452  * @param iter function to call on each entry
453  * @param iter_cls closure for iterator
454  * @return number of entries
455  */
456 int
457 GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
458                                     EXTRACTOR_MetaDataProcessor iter,
459                                     void *iter_cls)
460 {
461   struct MetaItem *pos;
462
463   if (NULL == md)
464     return 0;
465   if (NULL == iter)
466     return md->item_count;
467   for (pos = md->items_head; NULL != pos; pos = pos->next)
468     if (0 !=
469         iter (iter_cls, pos->plugin_name, pos->type, pos->format,
470               pos->mime_type, pos->data, pos->data_size))
471       return md->item_count;
472   return md->item_count;
473 }
474
475
476 /**
477  * Get the first MD entry of the given type.  Caller
478  * is responsible for freeing the return value.
479  * Also, only meta data items that are strings (0-terminated)
480  * are returned by this function.
481  *
482  * @param md metadata to inspect
483  * @param type type to look for
484  * @return NULL if no entry was found
485  */
486 char *
487 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
488                                         *md, enum EXTRACTOR_MetaType type)
489 {
490   struct MetaItem *pos;
491
492   if (NULL == md)
493     return NULL;
494   for (pos = md->items_head; NULL != pos; pos = pos->next)
495     if ((type == pos->type) &&
496         ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
497          (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
498       return GNUNET_strdup (pos->data);
499   return NULL;
500 }
501
502
503 /**
504  * Get the first matching MD entry of the given types. Caller is
505  * responsible for freeing the return value.  Also, only meta data
506  * items that are strings (0-terminated) are returned by this
507  * function.
508  *
509  * @param md metadata to inspect
510  * @param ... -1-terminated list of types
511  * @return NULL if we do not have any such entry,
512  *  otherwise client is responsible for freeing the value!
513  */
514 char *
515 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
516                                                GNUNET_CONTAINER_MetaData *md,
517                                                ...)
518 {
519   char *ret;
520   va_list args;
521   enum EXTRACTOR_MetaType type;
522
523   if (NULL == md)
524     return NULL;
525   ret = NULL;
526   va_start (args, md);
527   while (1)
528   {
529     type = va_arg (args, enum EXTRACTOR_MetaType);
530     if (-1 == type)
531       break;
532     if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
533       break;
534   }
535   va_end (args);
536   return ret;
537 }
538
539
540 /**
541  * Get a thumbnail from the meta-data (if present).
542  *
543  * @param md metadata to get the thumbnail from
544  * @param thumb will be set to the thumbnail data.  Must be
545  *        freed by the caller!
546  * @return number of bytes in thumbnail, 0 if not available
547  */
548 size_t
549 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
550                                           * md, unsigned char **thumb)
551 {
552   struct MetaItem *pos;
553   struct MetaItem *match;
554
555   if (NULL == md)
556     return 0;
557   match = NULL;
558   for (pos = md->items_head; NULL != pos; pos = pos->next)
559   {
560     if ((NULL != pos->mime_type) &&
561         (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
562         (EXTRACTOR_METAFORMAT_BINARY == pos->format))
563     {
564       if (NULL == match)
565         match = pos;
566       else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
567                (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
568         match = pos;
569     }
570   }
571   if ((NULL == match) || (0 == match->data_size))
572     return 0;
573   *thumb = GNUNET_malloc (match->data_size);
574   memcpy (*thumb, match->data, match->data_size);
575   return match->data_size;
576 }
577
578
579 /**
580  * Duplicate a `struct GNUNET_CONTAINER_MetaData`.
581  *
582  * @param md what to duplicate
583  * @return duplicate meta-data container
584  */
585 struct GNUNET_CONTAINER_MetaData *
586 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
587                                       *md)
588 {
589   struct GNUNET_CONTAINER_MetaData *ret;
590   struct MetaItem *pos;
591
592   if (NULL == md)
593     return NULL;
594   ret = GNUNET_CONTAINER_meta_data_create ();
595   for (pos = md->items_tail; NULL != pos; pos = pos->prev)
596     GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
597                                        pos->format, pos->mime_type, pos->data,
598                                        pos->data_size);
599   return ret;
600 }
601
602
603
604 /**
605  * Try to compress the given block of data.
606  *
607  * @param data block to compress; if compression
608  *        resulted in a smaller block, the first
609  *        bytes of data are updated to the compressed
610  *        data
611  * @param oldSize number of bytes in data
612  * @param result set to the compressed data
613  * @param newSize set to size of result
614  * @return #GNUNET_YES if compression reduce the size,
615  *         #GNUNET_NO if compression did not help
616  */
617 static int
618 try_compression (const char *data, size_t oldSize, char **result,
619                  size_t * newSize)
620 {
621   char *tmp;
622   uLongf dlen;
623
624 #ifdef compressBound
625   dlen = compressBound (oldSize);
626 #else
627   dlen = oldSize + (oldSize / 100) + 20;
628   /* documentation says 100.1% oldSize + 12 bytes, but we
629    * should be able to overshoot by more to be safe */
630 #endif
631   tmp = GNUNET_malloc (dlen);
632   if (Z_OK ==
633       compress2 ((Bytef *) tmp, &dlen, (const Bytef *) data, oldSize, 9))
634   {
635     if (dlen < oldSize)
636     {
637       *result = tmp;
638       *newSize = dlen;
639       return GNUNET_YES;
640     }
641   }
642   GNUNET_free (tmp);
643   return GNUNET_NO;
644 }
645
646
647 /**
648  * Flag in 'version' that indicates compressed meta-data.
649  */
650 #define HEADER_COMPRESSED 0x80000000
651
652
653 /**
654  * Bits in 'version' that give the version number.
655  */
656 #define HEADER_VERSION_MASK 0x7FFFFFFF
657
658
659 /**
660  * Header for serialized meta data.
661  */
662 struct MetaDataHeader
663 {
664   /**
665    * The version of the MD serialization.  The highest bit is used to
666    * indicate compression.
667    *
668    * Version 0 is traditional (pre-0.9) meta data (unsupported)
669    * Version is 1 for a NULL pointer
670    * Version 2 is for 0.9.x (and possibly higher)
671    * Other version numbers are not yet defined.
672    */
673   uint32_t version;
674
675   /**
676    * How many MD entries are there?
677    */
678   uint32_t entries;
679
680   /**
681    * Size of the decompressed meta data.
682    */
683   uint32_t size;
684
685   /**
686    * This is followed by 'entries' values of type 'struct MetaDataEntry'
687    * and then by 'entry' plugin names, mime-types and data blocks
688    * as specified in those meta data entries.
689    */
690 };
691
692
693 /**
694  * Entry of serialized meta data.
695  */
696 struct MetaDataEntry
697 {
698   /**
699    * Meta data type.  Corresponds to an 'enum EXTRACTOR_MetaType'
700    */
701   uint32_t type;
702
703   /**
704    * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
705    */
706   uint32_t format;
707
708   /**
709    * Number of bytes of meta data.
710    */
711   uint32_t data_size;
712
713   /**
714    * Number of bytes in the plugin name including 0-terminator.  0 for NULL.
715    */
716   uint32_t plugin_name_len;
717
718   /**
719    * Number of bytes in the mime type including 0-terminator.  0 for NULL.
720    */
721   uint32_t mime_type_len;
722
723 };
724
725
726 /**
727  * Serialize meta-data to target.
728  *
729  * @param md metadata to serialize
730  * @param target where to write the serialized metadata;
731  *         *target can be NULL, in which case memory is allocated
732  * @param max maximum number of bytes available in target
733  * @param opt is it ok to just write SOME of the
734  *        meta-data to match the size constraint,
735  *        possibly discarding some data?
736  * @return number of bytes written on success,
737  *         #GNUNET_SYSERR on error (typically: not enough
738  *         space)
739  */
740 ssize_t
741 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
742                                       *md, char **target, size_t max,
743                                       enum
744                                       GNUNET_CONTAINER_MetaDataSerializationOptions
745                                       opt)
746 {
747   struct GNUNET_CONTAINER_MetaData *vmd;
748   struct MetaItem *pos;
749   struct MetaDataHeader ihdr;
750   struct MetaDataHeader *hdr;
751   struct MetaDataEntry *ent;
752   char *dst;
753   unsigned int i;
754   uint64_t msize;
755   size_t off;
756   char *mdata;
757   char *cdata;
758   size_t mlen;
759   size_t plen;
760   size_t size;
761   size_t left;
762   size_t clen;
763   size_t rlen;
764   int comp;
765
766   if (max < sizeof (struct MetaDataHeader))
767     return GNUNET_SYSERR;       /* far too small */
768   if (NULL == md)
769     return 0;
770
771   if (NULL != md->sbuf)
772   {
773     /* try to use serialization cache */
774     if (md->sbuf_size <= max)
775     {
776       if (NULL == *target)
777         *target = GNUNET_malloc (md->sbuf_size);
778       memcpy (*target, md->sbuf, md->sbuf_size);
779       return md->sbuf_size;
780     }
781     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
782       return GNUNET_SYSERR;     /* can say that this will fail */
783     /* need to compute a partial serialization, sbuf useless ... */
784   }
785   dst = NULL;
786   msize = 0;
787   for (pos = md->items_tail; NULL != pos; pos = pos->prev)
788   {
789     msize += sizeof (struct MetaDataEntry);
790     msize += pos->data_size;
791     if (NULL != pos->plugin_name)
792       msize += strlen (pos->plugin_name) + 1;
793     if (NULL != pos->mime_type)
794       msize += strlen (pos->mime_type) + 1;
795   }
796   size = (size_t) msize;
797   if (size != msize)
798   {
799     GNUNET_break (0);           /* integer overflow */
800     return GNUNET_SYSERR;
801   }
802   if (size >= GNUNET_MAX_MALLOC_CHECKED)
803   {
804     /* too large to be processed */
805     return GNUNET_SYSERR;
806   }
807   ent = GNUNET_malloc (size);
808   mdata = (char *) &ent[md->item_count];
809   off = size - (md->item_count * sizeof (struct MetaDataEntry));
810   i = 0;
811   for (pos = md->items_head; NULL != pos; pos = pos->next)
812   {
813     ent[i].type = htonl ((uint32_t) pos->type);
814     ent[i].format = htonl ((uint32_t) pos->format);
815     ent[i].data_size = htonl ((uint32_t) pos->data_size);
816     if (NULL == pos->plugin_name)
817       plen = 0;
818     else
819       plen = strlen (pos->plugin_name) + 1;
820     ent[i].plugin_name_len = htonl ((uint32_t) plen);
821     if (NULL == pos->mime_type)
822       mlen = 0;
823     else
824       mlen = strlen (pos->mime_type) + 1;
825     ent[i].mime_type_len = htonl ((uint32_t) mlen);
826     off -= pos->data_size;
827     if ((EXTRACTOR_METAFORMAT_UTF8 == pos->format) ||
828         (EXTRACTOR_METAFORMAT_C_STRING == pos->format))
829       GNUNET_break ('\0' == pos->data[pos->data_size - 1]);
830     memcpy (&mdata[off], pos->data, pos->data_size);
831     off -= plen;
832     if (NULL != pos->plugin_name)
833       memcpy (&mdata[off], pos->plugin_name, plen);
834     off -= mlen;
835     if (NULL != pos->mime_type)
836       memcpy (&mdata[off], pos->mime_type, mlen);
837     i++;
838   }
839   GNUNET_assert (0 == off);
840
841   clen = 0;
842   cdata = NULL;
843   left = size;
844   i = 0;
845   for (pos = md->items_head; NULL != pos; pos = pos->next)
846   {
847     comp = GNUNET_NO;
848     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
849       comp = try_compression ((const char *) &ent[i], left, &cdata, &clen);
850
851     if ((NULL == md->sbuf) && (0 == i))
852     {
853       /* fill 'sbuf'; this "modifies" md, but since this is only
854        * an internal cache we will cast away the 'const' instead
855        * of making the API look strange. */
856       vmd = (struct GNUNET_CONTAINER_MetaData *) md;
857       hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
858       hdr->size = htonl (left);
859       hdr->entries = htonl (md->item_count);
860       if (GNUNET_YES == comp)
861       {
862         GNUNET_assert (clen < left);
863         hdr->version = htonl (2 | HEADER_COMPRESSED);
864         memcpy (&hdr[1], cdata, clen);
865         vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
866       }
867       else
868       {
869         hdr->version = htonl (2);
870         memcpy (&hdr[1], &ent[0], left);
871         vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
872       }
873       vmd->sbuf = (char *) hdr;
874     }
875
876     if (((left + sizeof (struct MetaDataHeader)) <= max) ||
877         ((GNUNET_YES == comp) && (clen <= max)))
878     {
879       /* success, this now fits! */
880       if (GNUNET_YES == comp)
881       {
882         if (NULL == dst)
883           dst = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
884         hdr = (struct MetaDataHeader *) dst;
885         hdr->version = htonl (2 | HEADER_COMPRESSED);
886         hdr->size = htonl (left);
887         hdr->entries = htonl (md->item_count - i);
888         memcpy (&dst[sizeof (struct MetaDataHeader)], cdata, clen);
889         GNUNET_free (cdata);
890         cdata = NULL;
891         GNUNET_free (ent);
892         rlen = clen + sizeof (struct MetaDataHeader);
893       }
894       else
895       {
896         if (NULL == dst)
897           dst = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
898         hdr = (struct MetaDataHeader *) dst;
899         hdr->version = htonl (2);
900         hdr->entries = htonl (md->item_count - i);
901         hdr->size = htonl (left);
902         memcpy (&dst[sizeof (struct MetaDataHeader)], &ent[i], left);
903         GNUNET_free (ent);
904         rlen = left + sizeof (struct MetaDataHeader);
905       }
906       if (NULL != *target)
907       {
908         if (GNUNET_YES == comp)
909           memcpy (*target, dst, clen + sizeof (struct MetaDataHeader));
910         else
911           memcpy (*target, dst, left + sizeof (struct MetaDataHeader));
912         GNUNET_free (dst);
913       }
914       else
915       {
916         *target = dst;
917       }
918       return rlen;
919     }
920
921     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
922     {
923       /* does not fit! */
924       GNUNET_free (ent);
925       return GNUNET_SYSERR;
926     }
927
928     /* next iteration: ignore the corresponding meta data at the
929      * end and try again without it */
930     left -= sizeof (struct MetaDataEntry);
931     left -= pos->data_size;
932     if (NULL != pos->plugin_name)
933       left -= strlen (pos->plugin_name) + 1;
934     if (NULL != pos->mime_type)
935       left -= strlen (pos->mime_type) + 1;
936
937     GNUNET_free_non_null (cdata);
938     cdata = NULL;
939
940     i++;
941   }
942   GNUNET_free (ent);
943
944   /* nothing fit, only write header! */
945   ihdr.version = htonl (2);
946   ihdr.entries = htonl (0);
947   ihdr.size = htonl (0);
948   if (NULL == *target)
949     *target = (char *) GNUNET_new (struct MetaDataHeader);
950   memcpy (*target, &ihdr, sizeof (struct MetaDataHeader));
951   return sizeof (struct MetaDataHeader);
952 }
953
954
955 /**
956  * Get the size of the full meta-data in serialized form.
957  *
958  * @param md metadata to inspect
959  * @return number of bytes needed for serialization, -1 on error
960  */
961 ssize_t
962 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
963                                                 GNUNET_CONTAINER_MetaData *md)
964 {
965   ssize_t ret;
966   char *ptr;
967
968   if (NULL != md->sbuf)
969     return md->sbuf_size;
970   ptr = NULL;
971   ret =
972       GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
973                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
974   if (-1 != ret)
975     GNUNET_free (ptr);
976   return ret;
977 }
978
979
980 /**
981  * Decompress input, return the decompressed data
982  * as output, set outputSize to the number of bytes
983  * that were found.
984  *
985  * @param input compressed data
986  * @param inputSize number of bytes in input
987  * @param outputSize expected size of the output
988  * @return NULL on error
989  */
990 static char *
991 decompress (const char *input, size_t inputSize, size_t outputSize)
992 {
993   char *output;
994   uLongf olen;
995
996   olen = outputSize;
997   output = GNUNET_malloc (olen);
998   if (Z_OK ==
999       uncompress ((Bytef *) output, &olen, (const Bytef *) input, inputSize))
1000     return output;
1001   GNUNET_free (output);
1002   return NULL;  
1003 }
1004
1005
1006 /**
1007  * Deserialize meta-data.  Initializes md.
1008  *
1009  * @param input buffer with the serialized metadata
1010  * @param size number of bytes available in input
1011  * @return MD on success, NULL on error (i.e.
1012  *         bad format)
1013  */
1014 struct GNUNET_CONTAINER_MetaData *
1015 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1016 {
1017   struct GNUNET_CONTAINER_MetaData *md;
1018   struct MetaDataHeader hdr;
1019   struct MetaDataEntry ent;
1020   uint32_t ic;
1021   uint32_t i;
1022   char *data;
1023   const char *cdata;
1024   uint32_t version;
1025   uint32_t dataSize;
1026   int compressed;
1027   size_t left;
1028   uint32_t mlen;
1029   uint32_t plen;
1030   uint32_t dlen;
1031   const char *mdata;
1032   const char *meta_data;
1033   const char *plugin_name;
1034   const char *mime_type;
1035   enum EXTRACTOR_MetaFormat format;
1036
1037   if (size < sizeof (struct MetaDataHeader))
1038     return NULL;
1039   memcpy (&hdr, input, sizeof (struct MetaDataHeader));
1040   version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1041   compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1042
1043   if (1 == version)
1044     return NULL;                /* null pointer */
1045   if (2 != version)
1046   {
1047     GNUNET_break_op (0);        /* unsupported version */
1048     return NULL;
1049   }
1050
1051   ic = ntohl (hdr.entries);
1052   dataSize = ntohl (hdr.size);
1053   if ((sizeof (struct MetaDataEntry) * ic) > dataSize)
1054   {
1055     GNUNET_break_op (0);
1056     return NULL;
1057   }
1058
1059   if (compressed)
1060   {
1061     if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1062     {
1063       /* make sure we don't blow our memory limit because of a mal-formed
1064        * message... */
1065       GNUNET_break_op (0);
1066       return NULL;
1067     }
1068     data =
1069         decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
1070                     size - sizeof (struct MetaDataHeader), dataSize);
1071     if (NULL == data)
1072     {
1073       GNUNET_break_op (0);
1074       return NULL;
1075     }
1076     cdata = data;
1077   }
1078   else
1079   {
1080     data = NULL;
1081     cdata = (const char *) &input[sizeof (struct MetaDataHeader)];
1082     if (dataSize != size - sizeof (struct MetaDataHeader))
1083     {
1084       GNUNET_break_op (0);
1085       return NULL;
1086     }
1087   }
1088
1089   md = GNUNET_CONTAINER_meta_data_create ();
1090   left = dataSize - ic * sizeof (struct MetaDataEntry);
1091   mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
1092   for (i = 0; i < ic; i++)
1093   {
1094     memcpy (&ent, &cdata[i * sizeof (struct MetaDataEntry)],
1095             sizeof (struct MetaDataEntry));
1096     format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1097     if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1098         (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1099         (EXTRACTOR_METAFORMAT_BINARY != format))
1100     {
1101       GNUNET_break_op (0);
1102       break;
1103     }
1104     dlen = ntohl (ent.data_size);
1105     plen = ntohl (ent.plugin_name_len);
1106     mlen = ntohl (ent.mime_type_len);
1107     if (dlen > left)
1108     {
1109       GNUNET_break_op (0);
1110       break;
1111     }
1112     left -= dlen;
1113     meta_data = &mdata[left];
1114     if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1115         (EXTRACTOR_METAFORMAT_C_STRING == format))
1116     {
1117       if (0 == dlen)
1118       {
1119         GNUNET_break_op (0);
1120         break;
1121       }
1122       if ('\0' != meta_data[dlen - 1])
1123       {
1124         GNUNET_break_op (0);
1125         break;
1126       }
1127     }
1128     if (plen > left)
1129     {
1130       GNUNET_break_op (0);
1131       break;
1132     }
1133     left -= plen;
1134     if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1135     {
1136       GNUNET_break_op (0);
1137       break;
1138     }
1139     if (0 == plen)
1140       plugin_name = NULL;
1141     else
1142       plugin_name = &mdata[left];
1143
1144     if (mlen > left)
1145     {
1146       GNUNET_break_op (0);
1147       break;
1148     }
1149     left -= mlen;
1150     if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1151     {
1152       GNUNET_break_op (0);
1153       break;
1154     }
1155     if (0 == mlen)
1156       mime_type = NULL;
1157     else
1158       mime_type = &mdata[left];
1159     GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1160                                        (enum EXTRACTOR_MetaType)
1161                                        ntohl (ent.type), format, mime_type,
1162                                        meta_data, dlen);
1163   }
1164   GNUNET_free_non_null (data);
1165   return md;
1166 }
1167
1168
1169 /* end of container_meta_data.c */