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