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