-remove async ecc key generation, not needed
[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 2, 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_malloc (sizeof (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 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   for (pos = md->items_head; NULL != pos; pos = pos->next)
283   {
284     if (pos->data_size < data_size)
285       break; /* elements are sorted by size in the list */
286     if ((pos->type == type) && (pos->data_size == data_size) &&
287         (0 == memcmp (pos->data, data, data_size)))
288     {
289       if ((NULL == pos->mime_type) && (NULL != data_mime_type))
290       {
291         pos->mime_type = GNUNET_strdup (data_mime_type);
292         invalidate_sbuf (md);
293       }
294       if ((EXTRACTOR_METAFORMAT_C_STRING == pos->format) &&
295           (EXTRACTOR_METAFORMAT_UTF8 == format))
296       {
297         pos->format = EXTRACTOR_METAFORMAT_UTF8;
298         invalidate_sbuf (md);
299       }
300       return GNUNET_SYSERR;
301     }
302   }
303   md->item_count++;
304   mi = GNUNET_malloc (sizeof (struct MetaItem));
305   mi->type = type;
306   mi->format = format;
307   mi->data_size = data_size;
308   if (NULL == pos)
309     GNUNET_CONTAINER_DLL_insert_tail (md->items_head,
310                                       md->items_tail,
311                                       mi);
312   else
313     GNUNET_CONTAINER_DLL_insert_after (md->items_head,
314                                        md->items_tail,
315                                        pos->prev,
316                                        mi);
317   mi->mime_type =
318       (NULL == data_mime_type) ? NULL : GNUNET_strdup (data_mime_type);
319   mi->plugin_name = (NULL == plugin_name) ? NULL : GNUNET_strdup (plugin_name);
320   mi->data = GNUNET_malloc (data_size);
321   memcpy (mi->data, data, data_size);
322   /* change all dir separators to POSIX style ('/') */
323   if ( (EXTRACTOR_METATYPE_FILENAME == type) ||
324        (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type) )
325   {
326     p = mi->data;
327     while (('\0' != *p) && (p < mi->data + data_size))
328     {
329       if ('\\' == *p)
330         *p = '/';
331       p++;
332     }
333   }
334   invalidate_sbuf (md);
335   return GNUNET_OK;
336 }
337
338
339 /**
340  * Merge given meta data.
341  *
342  * @param cls the 'struct GNUNET_CONTAINER_MetaData' to merge into
343  * @param plugin_name name of the plugin that produced this value;
344  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
345  *        used in the main libextractor library and yielding
346  *        meta data).
347  * @param type libextractor-type describing the meta data
348  * @param format basic format information about data
349  * @param data_mime_type mime-type of data (not of the original file);
350  *        can be NULL (if mime-type is not known)
351  * @param data actual meta-data found
352  * @param data_size number of bytes in data
353  * @return 0 (to continue)
354  */
355 static int
356 merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
357               enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
358               const char *data, size_t data_size)
359 {
360   struct GNUNET_CONTAINER_MetaData *md = cls;
361
362   (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
363                                             data_mime_type, data, data_size);
364   return 0;
365 }
366
367
368 /**
369  * Extend metadata.  Merges the meta data from the second argument
370  * into the first, discarding duplicate key-value pairs.
371  *
372  * @param md metadata to extend
373  * @param in metadata to merge
374  */
375 void
376 GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
377                                   const struct GNUNET_CONTAINER_MetaData *in)
378 {
379   GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
380 }
381
382
383 /**
384  * Remove an item.
385  *
386  * @param md metadata to manipulate
387  * @param type type of the item to remove
388  * @param data specific value to remove, NULL to remove all
389  *        entries of the given type
390  * @param data_size number of bytes in data
391  * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
392  */
393 int
394 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
395                                    enum EXTRACTOR_MetaType type,
396                                    const char *data, size_t data_size)
397 {
398   struct MetaItem *pos;
399
400   for (pos = md->items_head; NULL != pos; pos = pos->next)
401   {
402     if (pos->data_size < data_size)
403       break; /* items are sorted by (decreasing) size */
404     if ((pos->type == type) &&
405         ((NULL == data) ||
406          ((pos->data_size == data_size) &&
407           (0 == memcmp (pos->data, data, data_size)))))
408     {
409       GNUNET_CONTAINER_DLL_remove (md->items_head, md->items_tail, pos);
410       meta_item_free (pos);
411       md->item_count--;
412       invalidate_sbuf (md);
413       return GNUNET_OK;
414     }
415   }
416   return GNUNET_SYSERR;
417 }
418
419
420 /**
421  * Add the current time as the publication date
422  * to the meta-data.
423  *
424  * @param md metadata to modify
425  */
426 void
427 GNUNET_CONTAINER_meta_data_add_publication_date (struct
428                                                  GNUNET_CONTAINER_MetaData *md)
429 {
430   const char *dat;
431   struct GNUNET_TIME_Absolute t;
432
433   t = GNUNET_TIME_absolute_get ();
434   GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_METATYPE_PUBLICATION_DATE,
435                                      NULL, 0);
436   dat = GNUNET_STRINGS_absolute_time_to_string (t);
437   GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
438                                      EXTRACTOR_METATYPE_PUBLICATION_DATE,
439                                      EXTRACTOR_METAFORMAT_UTF8, "text/plain",
440                                      dat, strlen (dat) + 1);
441 }
442
443
444 /**
445  * Iterate over MD entries.
446  *
447  * @param md metadata to inspect
448  * @param iter function to call on each entry
449  * @param iter_cls closure for iterator
450  * @return number of entries
451  */
452 int
453 GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
454                                     EXTRACTOR_MetaDataProcessor iter,
455                                     void *iter_cls)
456 {
457   struct MetaItem *pos;
458
459   if (NULL == md)
460     return 0;
461   if (NULL == iter)
462     return md->item_count;
463   for (pos = md->items_head; NULL != pos; pos = pos->next)
464     if (0 !=
465         iter (iter_cls, pos->plugin_name, pos->type, pos->format,
466               pos->mime_type, pos->data, pos->data_size))
467       return md->item_count;
468   return md->item_count;
469 }
470
471
472 /**
473  * Get the first MD entry of the given type.  Caller
474  * is responsible for freeing the return value.
475  * Also, only meta data items that are strings (0-terminated)
476  * are returned by this function.
477  *
478  * @param md metadata to inspect
479  * @param type type to look for
480  * @return NULL if no entry was found
481  */
482 char *
483 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
484                                         *md, enum EXTRACTOR_MetaType type)
485 {
486   struct MetaItem *pos;
487
488   if (NULL == md)
489     return NULL;
490   for (pos = md->items_head; NULL != pos; pos = pos->next)
491     if ((type == pos->type) &&
492         ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
493          (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
494       return GNUNET_strdup (pos->data);
495   return NULL;
496 }
497
498
499 /**
500  * Get the first matching MD entry of the given types. Caller is
501  * responsible for freeing the return value.  Also, only meta data
502  * items that are strings (0-terminated) are returned by this
503  * function.
504  *
505  * @param md metadata to inspect
506  * @param ... -1-terminated list of types
507  * @return NULL if we do not have any such entry,
508  *  otherwise client is responsible for freeing the value!
509  */
510 char *
511 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
512                                                GNUNET_CONTAINER_MetaData *md,
513                                                ...)
514 {
515   char *ret;
516   va_list args;
517   enum EXTRACTOR_MetaType type;
518
519   if (NULL == md)
520     return NULL;
521   ret = NULL;
522   va_start (args, md);
523   while (1)
524   {
525     type = va_arg (args, enum EXTRACTOR_MetaType);
526     if (-1 == type)
527       break;
528     if (NULL != (ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type)))
529       break;
530   }
531   va_end (args);
532   return ret;
533 }
534
535
536 /**
537  * Get a thumbnail from the meta-data (if present).
538  *
539  * @param md metadata to get the thumbnail from
540  * @param thumb will be set to the thumbnail data.  Must be
541  *        freed by the caller!
542  * @return number of bytes in thumbnail, 0 if not available
543  */
544 size_t
545 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct GNUNET_CONTAINER_MetaData
546                                           * md, unsigned char **thumb)
547 {
548   struct MetaItem *pos;
549   struct MetaItem *match;
550
551   if (NULL == md)
552     return 0;
553   match = NULL;
554   for (pos = md->items_head; NULL != pos; pos = pos->next)
555   {
556     if ((NULL != pos->mime_type) &&
557         (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
558         (EXTRACTOR_METAFORMAT_BINARY == pos->format))
559     {
560       if (NULL == match)
561         match = pos;
562       else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
563                (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
564         match = pos;
565     }
566   }
567   if ((NULL == match) || (0 == match->data_size))
568     return 0;
569   *thumb = GNUNET_malloc (match->data_size);
570   memcpy (*thumb, match->data, match->data_size);
571   return match->data_size;
572 }
573
574
575 /**
576  * Duplicate struct GNUNET_CONTAINER_MetaData.
577  *
578  * @param md what to duplicate
579  * @return duplicate meta-data container
580  */
581 struct GNUNET_CONTAINER_MetaData *
582 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
583                                       *md)
584 {
585   struct GNUNET_CONTAINER_MetaData *ret;
586   struct MetaItem *pos;
587
588   if (NULL == md)
589     return NULL;
590   ret = GNUNET_CONTAINER_meta_data_create ();
591   for (pos = md->items_tail; NULL != pos; pos = pos->prev)
592     GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
593                                        pos->format, pos->mime_type, pos->data,
594                                        pos->data_size);
595   return ret;
596 }
597
598
599
600 /**
601  * Try to compress the given block of data.
602  *
603  * @param data block to compress; if compression
604  *        resulted in a smaller block, the first
605  *        bytes of data are updated to the compressed
606  *        data
607  * @param oldSize number of bytes in data
608  * @param result set to the compressed data
609  * @param newSize set to size of result
610  * @return GNUNET_YES if compression reduce the size,
611  *         GNUNET_NO if compression did not help
612  */
613 static int
614 try_compression (const char *data, size_t oldSize, char **result,
615                  size_t * newSize)
616 {
617   char *tmp;
618   uLongf dlen;
619
620 #ifdef compressBound
621   dlen = compressBound (oldSize);
622 #else
623   dlen = oldSize + (oldSize / 100) + 20;
624   /* documentation says 100.1% oldSize + 12 bytes, but we
625    * should be able to overshoot by more to be safe */
626 #endif
627   tmp = GNUNET_malloc (dlen);
628   if (Z_OK ==
629       compress2 ((Bytef *) tmp, &dlen, (const Bytef *) data, oldSize, 9))
630   {
631     if (dlen < oldSize)
632     {
633       *result = tmp;
634       *newSize = dlen;
635       return GNUNET_YES;
636     }
637   }
638   GNUNET_free (tmp);
639   return GNUNET_NO;
640 }
641
642
643 /**
644  * Flag in 'version' that indicates compressed meta-data.
645  */
646 #define HEADER_COMPRESSED 0x80000000
647
648
649 /**
650  * Bits in 'version' that give the version number.
651  */
652 #define HEADER_VERSION_MASK 0x7FFFFFFF
653
654
655 /**
656  * Header for serialized meta data.
657  */
658 struct MetaDataHeader
659 {
660   /**
661    * The version of the MD serialization.  The highest bit is used to
662    * indicate compression.
663    *
664    * Version 0 is traditional (pre-0.9) meta data (unsupported)
665    * Version is 1 for a NULL pointer
666    * Version 2 is for 0.9.x (and possibly higher)
667    * Other version numbers are not yet defined.
668    */
669   uint32_t version;
670
671   /**
672    * How many MD entries are there?
673    */
674   uint32_t entries;
675
676   /**
677    * Size of the decompressed meta data.
678    */
679   uint32_t size;
680
681   /**
682    * This is followed by 'entries' values of type 'struct MetaDataEntry'
683    * and then by 'entry' plugin names, mime-types and data blocks
684    * as specified in those meta data entries.
685    */
686 };
687
688
689 /**
690  * Entry of serialized meta data.
691  */
692 struct MetaDataEntry
693 {
694   /**
695    * Meta data type.  Corresponds to an 'enum EXTRACTOR_MetaType'
696    */
697   uint32_t type;
698
699   /**
700    * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
701    */
702   uint32_t format;
703
704   /**
705    * Number of bytes of meta data.
706    */
707   uint32_t data_size;
708
709   /**
710    * Number of bytes in the plugin name including 0-terminator.  0 for NULL.
711    */
712   uint32_t plugin_name_len;
713
714   /**
715    * Number of bytes in the mime type including 0-terminator.  0 for NULL.
716    */
717   uint32_t mime_type_len;
718
719 };
720
721
722 /**
723  * Serialize meta-data to target.
724  *
725  * @param md metadata to serialize
726  * @param target where to write the serialized metadata;
727  *         *target can be NULL, in which case memory is allocated
728  * @param max maximum number of bytes available in target
729  * @param opt is it ok to just write SOME of the
730  *        meta-data to match the size constraint,
731  *        possibly discarding some data?
732  * @return number of bytes written on success,
733  *         GNUNET_SYSERR on error (typically: not enough
734  *         space)
735  */
736 ssize_t
737 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
738                                       *md, char **target, size_t max,
739                                       enum
740                                       GNUNET_CONTAINER_MetaDataSerializationOptions
741                                       opt)
742 {
743   struct GNUNET_CONTAINER_MetaData *vmd;
744   struct MetaItem *pos;
745   struct MetaDataHeader ihdr;
746   struct MetaDataHeader *hdr;
747   struct MetaDataEntry *ent;
748   char *dst;
749   unsigned int i;
750   uint64_t msize;
751   size_t off;
752   char *mdata;
753   char *cdata;
754   size_t mlen;
755   size_t plen;
756   size_t size;
757   size_t left;
758   size_t clen;
759   size_t rlen;
760   int comp;
761
762   if (max < sizeof (struct MetaDataHeader))
763     return GNUNET_SYSERR;       /* far too small */
764   if (NULL == md)
765     return 0;
766
767   if (NULL != md->sbuf)
768   {
769     /* try to use serialization cache */
770     if (md->sbuf_size <= max)
771     {
772       if (NULL == *target)
773         *target = GNUNET_malloc (md->sbuf_size);
774       memcpy (*target, md->sbuf, md->sbuf_size);
775       return md->sbuf_size;
776     }
777     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
778       return GNUNET_SYSERR;     /* can say that this will fail */
779     /* need to compute a partial serialization, sbuf useless ... */
780   }
781   dst = NULL;
782   msize = 0;
783   for (pos = md->items_tail; NULL != pos; pos = pos->prev)
784   {
785     msize += sizeof (struct MetaDataEntry);
786     msize += pos->data_size;
787     if (NULL != pos->plugin_name)
788       msize += strlen (pos->plugin_name) + 1;
789     if (NULL != pos->mime_type)
790       msize += strlen (pos->mime_type) + 1;
791   }
792   size = (size_t) msize;
793   if (size != msize)
794   {
795     GNUNET_break (0);           /* integer overflow */
796     return GNUNET_SYSERR;
797   }
798   if (size >= GNUNET_MAX_MALLOC_CHECKED)
799   {
800     /* too large to be processed */
801     return GNUNET_SYSERR;
802   }
803   ent = GNUNET_malloc (size);
804   mdata = (char *) &ent[md->item_count];
805   off = size - (md->item_count * sizeof (struct MetaDataEntry));
806   i = 0;
807   for (pos = md->items_head; NULL != pos; pos = pos->next)
808   {
809     ent[i].type = htonl ((uint32_t) pos->type);
810     ent[i].format = htonl ((uint32_t) pos->format);
811     ent[i].data_size = htonl ((uint32_t) pos->data_size);
812     if (pos->plugin_name == NULL)
813       plen = 0;
814     else
815       plen = strlen (pos->plugin_name) + 1;
816     ent[i].plugin_name_len = htonl ((uint32_t) plen);
817     if (pos->mime_type == NULL)
818       mlen = 0;
819     else
820       mlen = strlen (pos->mime_type) + 1;
821     ent[i].mime_type_len = htonl ((uint32_t) mlen);
822     off -= pos->data_size;
823     memcpy (&mdata[off], pos->data, pos->data_size);
824     off -= plen;
825     if (pos->plugin_name != NULL)
826       memcpy (&mdata[off], pos->plugin_name, plen);
827     off -= mlen;
828     if (pos->mime_type != NULL)
829       memcpy (&mdata[off], pos->mime_type, mlen);
830     i++;
831   }
832   GNUNET_assert (0 == off);
833
834   clen = 0;
835   cdata = NULL;
836   left = size;
837   i = 0;
838   for (pos = md->items_head; NULL != pos; pos = pos->next)
839   {
840     comp = GNUNET_NO;
841     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
842       comp = try_compression ((const char *) &ent[i], left, &cdata, &clen);
843
844     if ((NULL == md->sbuf) && (0 == i))
845     {
846       /* fill 'sbuf'; this "modifies" md, but since this is only
847        * an internal cache we will cast away the 'const' instead
848        * of making the API look strange. */
849       vmd = (struct GNUNET_CONTAINER_MetaData *) md;
850       hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
851       hdr->size = htonl (left);
852       hdr->entries = htonl (md->item_count);
853       if (GNUNET_YES == comp)
854       {
855         GNUNET_assert (clen < left);
856         hdr->version = htonl (2 | HEADER_COMPRESSED);
857         memcpy (&hdr[1], cdata, clen);
858         vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
859       }
860       else
861       {
862         hdr->version = htonl (2);
863         memcpy (&hdr[1], &ent[0], left);
864         vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
865       }
866       vmd->sbuf = (char *) hdr;
867     }
868
869     if (((left + sizeof (struct MetaDataHeader)) <= max) ||
870         ((GNUNET_YES == comp) && (clen <= max)))
871     {
872       /* success, this now fits! */
873       if (GNUNET_YES == comp)
874       {
875         if (NULL == dst)
876           dst = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
877         hdr = (struct MetaDataHeader *) dst;
878         hdr->version = htonl (2 | HEADER_COMPRESSED);
879         hdr->size = htonl (left);
880         hdr->entries = htonl (md->item_count - i);
881         memcpy (&dst[sizeof (struct MetaDataHeader)], cdata, clen);
882         GNUNET_free (cdata);
883         cdata = NULL;
884         GNUNET_free (ent);
885         rlen = clen + sizeof (struct MetaDataHeader);
886       }
887       else
888       {
889         if (NULL == dst)
890           dst = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
891         hdr = (struct MetaDataHeader *) dst;
892         hdr->version = htonl (2);
893         hdr->entries = htonl (md->item_count - i);
894         hdr->size = htonl (left);
895         memcpy (&dst[sizeof (struct MetaDataHeader)], &ent[i], left);
896         GNUNET_free (ent);
897         rlen = left + sizeof (struct MetaDataHeader);
898       }
899       if (NULL != *target)
900       {
901         if (GNUNET_YES == comp)
902           memcpy (*target, dst, clen + sizeof (struct MetaDataHeader));
903         else
904           memcpy (*target, dst, left + sizeof (struct MetaDataHeader));
905         GNUNET_free (dst);
906       }
907       else
908       {
909         *target = dst;
910       }
911       return rlen;
912     }
913
914     if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
915     {
916       /* does not fit! */
917       GNUNET_free (ent);
918       return GNUNET_SYSERR;
919     }
920
921     /* next iteration: ignore the corresponding meta data at the
922      * end and try again without it */
923     left -= sizeof (struct MetaDataEntry);
924     left -= pos->data_size;
925     if (NULL != pos->plugin_name)
926       left -= strlen (pos->plugin_name) + 1;
927     if (NULL != pos->mime_type)
928       left -= strlen (pos->mime_type) + 1;
929
930     GNUNET_free_non_null (cdata);
931     cdata = NULL;
932
933     i++;
934   }
935   GNUNET_free (ent);
936
937   /* nothing fit, only write header! */
938   ihdr.version = htonl (2);
939   ihdr.entries = htonl (0);
940   ihdr.size = htonl (0);
941   if (NULL == *target)
942     *target = GNUNET_malloc (sizeof (struct MetaDataHeader));
943   memcpy (*target, &ihdr, sizeof (struct MetaDataHeader));
944   return sizeof (struct MetaDataHeader);
945 }
946
947
948 /**
949  * Get the size of the full meta-data in serialized form.
950  *
951  * @param md metadata to inspect
952  * @return number of bytes needed for serialization, -1 on error
953  */
954 ssize_t
955 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
956                                                 GNUNET_CONTAINER_MetaData *md)
957 {
958   ssize_t ret;
959   char *ptr;
960
961   if (NULL != md->sbuf)
962     return md->sbuf_size;
963   ptr = NULL;
964   ret =
965       GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
966                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
967   if (-1 != ret)
968     GNUNET_free (ptr);
969   return ret;
970 }
971
972
973 /**
974  * Decompress input, return the decompressed data
975  * as output, set outputSize to the number of bytes
976  * that were found.
977  *
978  * @param input compressed data
979  * @param inputSize number of bytes in input
980  * @param outputSize expected size of the output
981  * @return NULL on error
982  */
983 static char *
984 decompress (const char *input, size_t inputSize, size_t outputSize)
985 {
986   char *output;
987   uLongf olen;
988
989   olen = outputSize;
990   output = GNUNET_malloc (olen);
991   if (Z_OK ==
992       uncompress ((Bytef *) output, &olen, (const Bytef *) input, inputSize))
993     return output;
994   GNUNET_free (output);
995   return NULL;  
996 }
997
998
999 /**
1000  * Deserialize meta-data.  Initializes md.
1001  *
1002  * @param input buffer with the serialized metadata
1003  * @param size number of bytes available in input
1004  * @return MD on success, NULL on error (i.e.
1005  *         bad format)
1006  */
1007 struct GNUNET_CONTAINER_MetaData *
1008 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1009 {
1010   struct GNUNET_CONTAINER_MetaData *md;
1011   struct MetaDataHeader hdr;
1012   struct MetaDataEntry ent;
1013   uint32_t ic;
1014   uint32_t i;
1015   char *data;
1016   const char *cdata;
1017   uint32_t version;
1018   uint32_t dataSize;
1019   int compressed;
1020   size_t left;
1021   uint32_t mlen;
1022   uint32_t plen;
1023   uint32_t dlen;
1024   const char *mdata;
1025   const char *meta_data;
1026   const char *plugin_name;
1027   const char *mime_type;
1028   enum EXTRACTOR_MetaFormat format;
1029
1030   if (size < sizeof (struct MetaDataHeader))
1031     return NULL;
1032   memcpy (&hdr, input, sizeof (struct MetaDataHeader));
1033   version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1034   compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1035
1036   if (1 == version)
1037     return NULL;                /* null pointer */
1038   if (2 != version)
1039   {
1040     GNUNET_break_op (0);        /* unsupported version */
1041     return NULL;
1042   }
1043
1044   ic = ntohl (hdr.entries);
1045   dataSize = ntohl (hdr.size);
1046   if ((sizeof (struct MetaDataEntry) * ic) > dataSize)
1047   {
1048     GNUNET_break_op (0);
1049     return NULL;
1050   }
1051
1052   if (compressed)
1053   {
1054     if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1055     {
1056       /* make sure we don't blow our memory limit because of a mal-formed
1057        * message... */
1058       GNUNET_break_op (0);
1059       return NULL;
1060     }
1061     data =
1062         decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
1063                     size - sizeof (struct MetaDataHeader), dataSize);
1064     if (NULL == data)
1065     {
1066       GNUNET_break_op (0);
1067       return NULL;
1068     }
1069     cdata = data;
1070   }
1071   else
1072   {
1073     data = NULL;
1074     cdata = (const char *) &input[sizeof (struct MetaDataHeader)];
1075     if (dataSize != size - sizeof (struct MetaDataHeader))
1076     {
1077       GNUNET_break_op (0);
1078       return NULL;
1079     }
1080   }
1081
1082   md = GNUNET_CONTAINER_meta_data_create ();
1083   left = dataSize - ic * sizeof (struct MetaDataEntry);
1084   mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
1085   for (i = 0; i < ic; i++)
1086   {
1087     memcpy (&ent, &cdata[i * sizeof (struct MetaDataEntry)],
1088             sizeof (struct MetaDataEntry));
1089     format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1090     if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
1091         (EXTRACTOR_METAFORMAT_C_STRING != format) &&
1092         (EXTRACTOR_METAFORMAT_BINARY != format))
1093     {
1094       GNUNET_break_op (0);
1095       break;
1096     }
1097     dlen = ntohl (ent.data_size);
1098     plen = ntohl (ent.plugin_name_len);
1099     mlen = ntohl (ent.mime_type_len);
1100     if (dlen > left)
1101     {
1102       GNUNET_break_op (0);
1103       break;
1104     }
1105     left -= dlen;
1106     meta_data = &mdata[left];
1107     if ((EXTRACTOR_METAFORMAT_UTF8 == format) ||
1108         (EXTRACTOR_METAFORMAT_C_STRING == format))
1109     {
1110       if ((0 == dlen) || ('\0' != mdata[left + dlen - 1]))
1111       {
1112         GNUNET_break_op (0);
1113         break;
1114       }
1115     }
1116     if (plen > left)
1117     {
1118       GNUNET_break_op (0);
1119       break;
1120     }
1121     left -= plen;
1122     if ((plen > 0) && ('\0' != mdata[left + plen - 1]))
1123     {
1124       GNUNET_break_op (0);
1125       break;
1126     }
1127     if (0 == plen)
1128       plugin_name = NULL;
1129     else
1130       plugin_name = &mdata[left];
1131
1132     if (mlen > left)
1133     {
1134       GNUNET_break_op (0);
1135       break;
1136     }
1137     left -= mlen;
1138     if ((mlen > 0) && ('\0' != mdata[left + mlen - 1]))
1139     {
1140       GNUNET_break_op (0);
1141       break;
1142     }
1143     if (0 == mlen)
1144       mime_type = NULL;
1145     else
1146       mime_type = &mdata[left];
1147     GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
1148                                        (enum EXTRACTOR_MetaType)
1149                                        ntohl (ent.type), format, mime_type,
1150                                        meta_data, dlen);
1151   }
1152   GNUNET_free_non_null (data);
1153   return md;
1154 }
1155
1156
1157 /* end of container_meta_data.c */