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