MinGW
[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  * Test if two MDs are equal.  We consider them equal if
174  * the meta types, formats and content match (we do not
175  * include the mime types and plugins names in this
176  * consideration).
177  *
178  * @param md1 first value to check
179  * @param md2 other value to check
180  * @return GNUNET_YES if they are equal
181  */
182 int
183 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
184                                        *md1,
185                                        const struct GNUNET_CONTAINER_MetaData
186                                        *md2)
187 {
188   struct MetaItem *i;
189   struct MetaItem *j;
190   int found;
191
192   if (md1 == md2)
193     return GNUNET_YES;
194   if (md1->item_count != md2->item_count)
195     return GNUNET_NO;
196
197   i = md1->items;
198   while (NULL != i)
199     {
200       found = GNUNET_NO;
201       j = md2->items;
202       while (NULL != j)
203         {
204           if ( (i->type == j->type) &&
205                (i->format == j->format) &&
206                (i->data_size == j->data_size) &&
207                (0 == memcmp (i->data, 
208                              j->data,
209                              i->data_size)))
210             {
211               found = GNUNET_YES;
212               break;
213             }
214           j = j->next;
215         }
216       if (found == GNUNET_NO)
217         return GNUNET_NO;
218       i = i->next;
219     }
220   return GNUNET_YES;
221 }
222
223
224 /**
225  * Extend metadata.  Note that the list of meta data items is
226  * sorted by size (largest first).
227  *
228  * @param md metadata to extend
229  * @param plugin_name name of the plugin that produced this value;
230  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
231  *        used in the main libextractor library and yielding
232  *        meta data).
233  * @param type libextractor-type describing the meta data
234  * @param format basic format information about data 
235  * @param data_mime_type mime-type of data (not of the original file);
236  *        can be NULL (if mime-type is not known)
237  * @param data actual meta-data found
238  * @param data_len number of bytes in data
239  * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
240  *         data_mime_type and plugin_name are not considered for "exists" checks
241  */
242 int
243 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
244                                    const char *plugin_name,
245                                    enum EXTRACTOR_MetaType type,
246                                    enum EXTRACTOR_MetaFormat format,
247                                    const char *data_mime_type,
248                                    const char *data,
249                                    size_t data_len)
250 {
251   struct MetaItem *prev;
252   struct MetaItem *pos;
253   struct MetaItem *i;
254   char *p;
255
256   prev = NULL;
257   pos = md->items;
258   while (NULL != pos)
259     {
260       if (pos->data_size < data_len)
261         break;
262       if ( (pos->type == type) &&
263            (pos->format == format) &&
264            (pos->data_size == data_len) &&
265            (0 == memcmp (pos->data, 
266                          data,
267                          data_len)))
268         {
269           if ( (pos->mime_type == NULL) &&
270                (data_mime_type != NULL) )
271             {
272               pos->mime_type = GNUNET_strdup (data_mime_type);
273               invalidate_sbuf (md);  
274             }
275           return GNUNET_SYSERR;
276         }
277       prev = pos;
278       pos = pos->next;
279     }
280   md->item_count++;
281   i = GNUNET_malloc (sizeof (struct MetaItem));
282   i->type = type;
283   i->format = format;
284   i->data_size = data_len;
285   i->next = pos;
286   if (prev == NULL)
287     md->items = i;
288   else
289     prev->next = i;
290   i->mime_type = (data_mime_type == NULL) ? NULL : GNUNET_strdup (data_mime_type);
291   i->plugin_name = (plugin_name == NULL) ? NULL : GNUNET_strdup (plugin_name);
292   i->data = GNUNET_malloc (data_len);
293   memcpy (i->data, data, data_len);
294   /* change OS native dir separators to unix '/' and others to '_' */
295   if (type == EXTRACTOR_METATYPE_FILENAME)
296     {
297       p = i->data;
298       while ( (*p != '\0') &&
299               (p < i->data + data_len) )
300         {
301           if (*p == DIR_SEPARATOR)
302             *p = '/';
303           else if (*p == '\\')
304             *p = '_';
305           p++;
306         }
307     }
308   invalidate_sbuf (md);
309   return GNUNET_OK;
310 }
311
312
313 /**
314  * Remove an item.
315  *
316  * @param md metadata to manipulate
317  * @param type type of the item to remove
318  * @param data specific value to remove, NULL to remove all
319  *        entries of the given type
320  * @param data_len number of bytes in data
321  * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
322  */
323 int
324 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
325                                    enum EXTRACTOR_MetaType type,
326                                    const char *data,
327                                    size_t data_len)
328 {
329   struct MetaItem *pos;
330   struct MetaItem *prev;
331
332   prev = NULL;
333   pos = md->items;
334   while (NULL != pos)
335     {
336       if ( (pos->type == type) &&
337            ( (data == NULL) ||
338              ( (pos->data_size == data_len) &&
339                (0 == memcmp (pos->data, 
340                              data,
341                              data_len))) ) )
342         {
343           if (prev == NULL)
344             md->items = pos->next;
345           else
346             prev->next = pos->next;
347           meta_item_free (pos);
348           md->item_count--;
349           invalidate_sbuf (md);
350           return GNUNET_OK;
351         }
352       prev = pos;
353       pos = pos->next;
354     }
355   return GNUNET_SYSERR;
356 }
357
358
359 /**
360  * Add the current time as the publication date
361  * to the meta-data.
362  *
363  * @param md metadata to modify
364  */
365 void
366 GNUNET_CONTAINER_meta_data_add_publication_date (struct
367                                                  GNUNET_CONTAINER_MetaData
368                                                  *md)
369 {
370   char *dat;
371   struct GNUNET_TIME_Absolute t;
372
373   t = GNUNET_TIME_absolute_get ();
374   GNUNET_CONTAINER_meta_data_delete (md,
375                                      EXTRACTOR_METATYPE_PUBLICATION_DATE,
376                                      NULL,
377                                      0);
378   dat = GNUNET_STRINGS_absolute_time_to_string (t);
379   GNUNET_CONTAINER_meta_data_insert (md, 
380                                      "<gnunet>",
381                                      EXTRACTOR_METATYPE_PUBLICATION_DATE, 
382                                      EXTRACTOR_METAFORMAT_UTF8,
383                                      "text/plain",
384                                      dat,
385                                      strlen(dat)+1);
386   GNUNET_free (dat);
387 }
388
389
390 /**
391  * Iterate over MD entries.
392  *
393  * @param md metadata to inspect
394  * @param iter function to call on each entry
395  * @param iter_cls closure for iterator
396  * @return number of entries
397  */
398 int
399 GNUNET_CONTAINER_meta_data_iterate (const struct
400                                     GNUNET_CONTAINER_MetaData *md,
401                                     EXTRACTOR_MetaDataProcessor
402                                     iter, void *iter_cls)
403 {
404   struct MetaItem *pos;
405
406   if (iter == NULL)
407     return md->item_count;
408   pos = md->items;
409   while (NULL != pos)
410     {
411       if (0 != iter (iter_cls,
412                      pos->plugin_name,
413                      pos->type,
414                      pos->format,
415                      pos->mime_type,
416                      pos->data,
417                      pos->data_size))
418         return md->item_count;
419       pos = pos->next;
420     }
421   return md->item_count;
422 }
423
424
425 /**
426  * Get the first MD entry of the given type.  Caller
427  * is responsible for freeing the return value.
428  * Also, only meta data items that are strings (0-terminated)
429  * are returned by this function.
430  *
431  * @param md metadata to inspect
432  * @param type type to look for
433  * @return NULL if no entry was found
434  */
435 char *
436 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
437                                         *md, enum EXTRACTOR_MetaType type)
438 {
439   struct MetaItem *pos;
440
441   pos = md->items;
442   while (NULL != pos)
443     {
444       if ( (type == pos->type) &&
445            ( (pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
446              (pos->format == EXTRACTOR_METAFORMAT_C_STRING) ) )
447         return GNUNET_strdup (pos->data);
448       pos = pos->next;
449     }
450   return NULL;
451 }
452
453
454 /**
455  * Get the first matching MD entry of the given types. Caller is
456  * responsible for freeing the return value.  Also, only meta data
457  * items that are strings (0-terminated) are returned by this
458  * function.
459  *
460  * @param md metadata to inspect
461  * @param ... -1-terminated list of types
462  * @return NULL if we do not have any such entry,
463  *  otherwise client is responsible for freeing the value!
464  */
465 char *
466 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
467                                                GNUNET_CONTAINER_MetaData *md,
468                                                ...)
469 {
470   char *ret;
471   va_list args;
472   enum EXTRACTOR_MetaType type;
473
474   ret = NULL;
475   va_start (args, md);
476   while (1)
477     {
478       type = va_arg (args, enum EXTRACTOR_MetaType);
479       if (type == -1)
480         break;
481       ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type);
482       if (ret != NULL)
483         break;
484     }
485   va_end (args);
486   return ret;
487 }
488
489
490 /**
491  * Get a thumbnail from the meta-data (if present).
492  *
493  * @param md metadata to get the thumbnail from
494  * @param thumb will be set to the thumbnail data.  Must be
495  *        freed by the caller!
496  * @return number of bytes in thumbnail, 0 if not available
497  */
498 size_t
499 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
500                                           GNUNET_CONTAINER_MetaData * md,
501                                           unsigned char **thumb)
502 {
503   struct MetaItem *pos;
504   struct MetaItem *match;
505
506   match = NULL;
507   pos = md->items;
508   while (NULL != pos)
509     {
510       if ( (0 == strncasecmp ("image/", pos->mime_type,
511                               strlen("image/"))) &&
512            (pos->format == EXTRACTOR_METAFORMAT_BINARY) )
513         {
514           if (match == NULL)
515             match = pos;
516           else if ( (match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
517                     (pos->type == EXTRACTOR_METATYPE_THUMBNAIL) )
518             match = pos;
519         }
520       pos = pos->next;
521     }
522   if ( (match == NULL) ||
523        (match->data_size == 0) )
524     return 0;
525   *thumb = GNUNET_malloc (match->data_size);
526   memcpy (*thumb, match->data, match->data_size);
527   return match->data_size;
528 }
529
530
531 /**
532  * Duplicate struct GNUNET_CONTAINER_MetaData.
533  * 
534  * @param md what to duplicate
535  * @return duplicate meta-data container
536  */
537 struct GNUNET_CONTAINER_MetaData *
538 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
539                                       *md)
540 {
541   struct GNUNET_CONTAINER_MetaData *ret;
542   struct MetaItem *pos;
543
544   if (md == NULL)
545     return NULL;
546   ret = GNUNET_CONTAINER_meta_data_create ();
547   pos = md->items;
548   while (NULL != pos)
549     {
550       GNUNET_CONTAINER_meta_data_insert (ret, 
551                                          pos->plugin_name,
552                                          pos->type,
553                                          pos->format,
554                                          pos->mime_type,
555                                          pos->data,
556                                          pos->data_size);
557       pos = pos->next;
558     }
559   return ret;
560 }
561
562
563 /**
564  * Add meta data that libextractor finds to our meta data
565  * container.
566  *
567  * @param cls closure, our meta data container
568  * @param plugin_name name of the plugin that produced this value;
569  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
570  *        used in the main libextractor library and yielding
571  *        meta data).
572  * @param type libextractor-type describing the meta data
573  * @param format basic format information about data
574  * @param data_mime_type mime-type of data (not of the original file);
575  *        can be NULL (if mime-type is not known)
576  * @param data actual meta-data found
577  * @param data_len number of bytes in data
578  * @return always 0 to continue extracting
579  */
580 static int
581 add_to_md(void *cls,
582           const char *plugin_name,
583           enum EXTRACTOR_MetaType type,
584           enum EXTRACTOR_MetaFormat format,
585           const char *data_mime_type,
586           const char *data,
587           size_t data_len)
588 {
589   struct GNUNET_CONTAINER_MetaData *md = cls;
590   (void) GNUNET_CONTAINER_meta_data_insert (md,
591                                             plugin_name,
592                                             type,
593                                             format,
594                                             data_mime_type,
595                                             data,
596                                             data_len);
597   return 0;
598 }
599
600
601 /**
602  * Extract meta-data from a file.
603  *
604  * @return GNUNET_SYSERR on error, otherwise the number
605  *   of meta-data items obtained
606  */
607 int
608 GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData
609                                               *md, const char *filename,
610                                               struct EXTRACTOR_PluginList *
611                                               extractors)
612 {
613   unsigned int old;
614
615   if (filename == NULL)
616     return GNUNET_SYSERR;
617   if (extractors == NULL)
618     return 0;
619   old = md->item_count;
620   EXTRACTOR_extract (extractors, 
621                      filename,
622                      NULL, 0,
623                      &add_to_md,
624                      md);
625   return (int) (md->item_count - old);
626 }
627
628
629 /**
630  * Try to compress the given block of data.
631  *
632  * @param data block to compress; if compression
633  *        resulted in a smaller block, the first
634  *        bytes of data are updated to the compressed
635  *        data
636  * @param oldSize number of bytes in data
637  * @param result set to the compressed data
638  * @param newSize set to size of result
639  * @return GNUNET_YES if compression reduce the size,
640  *         GNUNET_NO if compression did not help
641  */
642 static int
643 try_compression (const char *data, 
644                  size_t oldSize,
645                  char **result,
646                  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 *hdr;  
777   struct MetaDataEntry *ent;
778   unsigned int i;
779   uint64_t msize;
780   size_t off;
781   char *mdata;
782   char *cdata;
783   size_t mlen;
784   size_t plen;
785   size_t size;
786   size_t left;
787   size_t clen;
788   int comp;
789
790   if (max < sizeof (struct MetaDataHeader))
791     return GNUNET_SYSERR;       /* far too small */
792   if (md == NULL)
793     return 0;
794
795   if (md->sbuf != NULL)
796     {
797       /* try to use serialization cache */
798       if (md->sbuf_size <= max)
799         {
800           if (NULL == *target)
801             *target = GNUNET_malloc (md->sbuf_size);
802           memcpy (*target,
803                   md->sbuf,
804                   md->sbuf_size);
805           return md->sbuf_size;
806         }
807       if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
808         return GNUNET_SYSERR; /* can say that this will fail */
809       /* need to compute a partial serialization, sbuf useless ... */
810     }
811
812
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   pos = md->items;
873   for (i=0;i<md->item_count;i++)
874     {           
875       comp = GNUNET_NO;
876       if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))        
877         comp = try_compression ((const char*) &ent[i],
878                                 left,
879                                 &cdata,
880                                 &clen);   
881
882       if ( (md->sbuf == NULL) &&
883            (i == 0) )
884         {
885           /* fill 'sbuf'; this "modifies" md, but since this is only
886              an internal cache we will cast away the 'const' instead
887              of making the API look strange. */
888           vmd = (struct GNUNET_CONTAINER_MetaData*) md;
889           hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
890           hdr->size = htonl (left);
891           hdr->entries = htonl (md->item_count);
892           if (GNUNET_YES == comp)
893             {
894               GNUNET_assert (clen < left);
895               hdr->version = htonl (2 | HEADER_COMPRESSED);
896               memcpy (&hdr[1],
897                       cdata, 
898                       clen);
899               vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
900             }
901           else
902             {
903               hdr->version = htonl (2);
904               memcpy (&hdr[1],
905                       &ent[0], 
906                       left);
907               vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
908             }
909           vmd->sbuf = (char*) hdr;
910         }
911
912       if ( ( (left + sizeof (struct MetaDataHeader)) <= max) ||
913            ( (comp == GNUNET_YES) &&
914              (clen <= max)) )
915         {
916           /* success, this now fits! */
917           if (GNUNET_YES == comp)
918             {
919               hdr = (struct MetaDataHeader*) *target;
920               if (hdr == NULL)
921                 {
922                   hdr = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
923                   *target = (char*) hdr;
924                 }
925               hdr->version = htonl (2 | HEADER_COMPRESSED);
926               hdr->size = htonl (left);
927               hdr->entries = htonl (md->item_count - i);
928               memcpy (&(*target)[sizeof(struct MetaDataHeader)],
929                       cdata, 
930                       clen);
931               GNUNET_free (cdata);
932               GNUNET_free (ent);
933               return clen + sizeof (struct MetaDataHeader);
934             }
935           else
936             {
937               hdr = (struct MetaDataHeader*) *target;
938               if (hdr == NULL)
939                 {
940                   hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
941                   *target = (char*) hdr;
942                 }
943               hdr->version = htonl (2);
944               hdr->entries = htonl (md->item_count - i);
945               hdr->size = htonl (left);
946               memcpy (&(*target)[sizeof(struct MetaDataHeader)],
947                       &ent[i], 
948                       left);
949               GNUNET_free (ent);
950               return left + sizeof (struct MetaDataHeader);           
951             }
952         }
953
954       if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
955         {
956           /* does not fit! */
957           GNUNET_free (ent);
958           return GNUNET_SYSERR;
959         }
960      
961       /* next iteration: ignore the corresponding meta data at the
962          end and try again without it */
963       left -= sizeof (struct MetaDataEntry);
964       left -= pos->data_size;
965       if (pos->plugin_name != NULL)
966         left -= strlen (pos->plugin_name) + 1;
967       if (pos->mime_type != NULL)
968         left -= strlen (pos->mime_type) + 1;
969       pos = pos->next;
970     }
971   GNUNET_free (ent);
972
973   /* nothing fit, only write header! */
974   hdr = (struct MetaDataHeader*) *target;
975   if (hdr == NULL)
976     {
977       hdr = GNUNET_malloc (sizeof (struct MetaDataHeader));
978       *target = (char*) hdr;
979     }
980   hdr->version = htonl (2);
981   hdr->entries = htonl (0);
982   hdr->size = htonl (0);
983   return sizeof (struct MetaDataHeader);
984 }
985
986
987 /**
988  * Get the size of the full meta-data in serialized form.
989  *
990  * @param md metadata to inspect
991  * @return number of bytes needed for serialization, -1 on error
992  */
993 ssize_t
994 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct GNUNET_CONTAINER_MetaData *md)
995 {
996   ssize_t ret;
997   char *ptr;
998   
999   if (md->sbuf != NULL)
1000     return md->sbuf_size;
1001   ptr = NULL;
1002   ret = GNUNET_CONTAINER_meta_data_serialize (md,
1003                                               &ptr,
1004                                               GNUNET_MAX_MALLOC_CHECKED,
1005                                               GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
1006   if (ret != -1)
1007     GNUNET_free (ptr);
1008   return ret;
1009 }
1010
1011
1012 /**
1013  * Decompress input, return the decompressed data
1014  * as output, set outputSize to the number of bytes
1015  * that were found.
1016  *
1017  * @param input compressed data
1018  * @param inputSize number of bytes in input
1019  * @param outputSize expected size of the output
1020  * @return NULL on error
1021  */
1022 static char *
1023 decompress (const char *input,
1024             size_t inputSize, 
1025             size_t outputSize)
1026 {
1027   char *output;
1028   uLongf olen;
1029
1030   olen = outputSize;
1031   output = GNUNET_malloc (olen);
1032   if (Z_OK == uncompress ((Bytef *) output,
1033                           &olen, (const Bytef *) input, inputSize))
1034     {
1035       return output;
1036     }
1037   else
1038     {
1039       GNUNET_free (output);
1040       return NULL;
1041     }
1042 }
1043
1044
1045 /**
1046  * Deserialize meta-data.  Initializes md.
1047  *
1048  * @param input buffer with the serialized metadata
1049  * @param size number of bytes available in input
1050  * @return MD on success, NULL on error (i.e.
1051  *         bad format)
1052  */
1053 struct GNUNET_CONTAINER_MetaData *
1054 GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
1055 {
1056   struct GNUNET_CONTAINER_MetaData *md;
1057   struct MetaDataHeader hdr;
1058   struct MetaDataEntry ent;
1059   uint32_t ic;
1060   uint32_t i;
1061   char *data;
1062   const char *cdata;
1063   uint32_t version;
1064   uint32_t dataSize;
1065   int compressed;
1066   size_t left;
1067   uint32_t mlen;
1068   uint32_t plen;
1069   uint32_t dlen;
1070   const char *mdata;
1071   const char *meta_data;
1072   const char *plugin_name;
1073   const char *mime_type;
1074   enum EXTRACTOR_MetaFormat format;
1075
1076   if (size < sizeof (struct MetaDataHeader))
1077     return NULL;
1078   memcpy (&hdr,
1079           input,
1080           sizeof (struct MetaDataHeader));
1081   version = ntohl (hdr.version) & HEADER_VERSION_MASK;
1082   compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
1083
1084   if (version == 1)
1085     return NULL;  /* null pointer */
1086   if (version != 2)
1087     {
1088       GNUNET_break_op (0);      /* unsupported version */
1089       return NULL;
1090     }
1091
1092   ic = ntohl (hdr.entries);
1093   dataSize = ntohl (hdr.size); 
1094   if ((sizeof (struct MetaDataEntry) * ic) > dataSize)
1095     {
1096       GNUNET_break_op (0);      
1097       return NULL; 
1098     }
1099
1100   if (compressed)
1101     {
1102       if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
1103         {
1104           /* make sure we don't blow our memory limit because of a mal-formed
1105              message... */
1106           GNUNET_break_op (0);
1107           return NULL;          
1108         }
1109       data =
1110         decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
1111                     size - sizeof (struct MetaDataHeader), dataSize);
1112       if (data == NULL)
1113         {
1114           GNUNET_break_op (0);
1115           return NULL;
1116         }
1117       cdata = data;
1118     }
1119   else
1120     {
1121       data = NULL;
1122       cdata = (const char *) &input[sizeof (struct MetaDataHeader)];
1123       if (dataSize != size - sizeof (struct MetaDataHeader))
1124         {
1125           GNUNET_break_op (0);
1126           return NULL;
1127         }
1128     }
1129
1130   md = GNUNET_CONTAINER_meta_data_create ();
1131   left = dataSize - ic * sizeof (struct MetaDataEntry);
1132   mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
1133   for (i=0;i<ic;i++)
1134     {
1135       memcpy (&ent,
1136               &cdata[i * sizeof(struct MetaDataEntry)],
1137               sizeof (struct MetaDataEntry));
1138       format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
1139       if ( (format != EXTRACTOR_METAFORMAT_UTF8) &&
1140            (format != EXTRACTOR_METAFORMAT_C_STRING) &&
1141            (format != EXTRACTOR_METAFORMAT_BINARY) )
1142         {
1143           GNUNET_break_op (0);
1144           break;
1145         }
1146       dlen = ntohl (ent.data_size);
1147       plen = ntohl (ent.plugin_name_len);
1148       mlen = ntohl (ent.mime_type_len);
1149       if (dlen > left)
1150         {
1151           GNUNET_break_op (0);
1152           break;
1153         }
1154       left -= dlen;
1155       meta_data = &mdata[left];
1156       if ( (format == EXTRACTOR_METAFORMAT_UTF8) ||
1157            (format == EXTRACTOR_METAFORMAT_C_STRING) )
1158         {
1159           if ( (dlen == 0) ||
1160                (mdata[left + dlen - 1] != '\0') )
1161             {
1162               GNUNET_break_op (0);
1163               break;
1164             }     
1165         }
1166       if (plen > left)
1167         {
1168           GNUNET_break_op (0);
1169           break;
1170         }
1171       left -= plen;
1172       if ( (plen > 0) &&
1173            (mdata[left + plen - 1] != '\0') )
1174         {
1175           GNUNET_break_op (0);
1176           break;
1177         }
1178       if (plen == 0)
1179         plugin_name = NULL;
1180       else
1181         plugin_name = &mdata[left];
1182
1183       if (mlen > left)
1184         {
1185           GNUNET_break_op (0);
1186           break;
1187         }
1188       left -= mlen;
1189       if ( (mlen > 0) &&
1190            (mdata[left + mlen - 1] != '\0') )
1191         {
1192           GNUNET_break_op (0);
1193           break;
1194         }
1195       if (mlen == 0)
1196         mime_type = NULL;
1197       else
1198         mime_type = &mdata[left];      
1199       GNUNET_CONTAINER_meta_data_insert (md,
1200                                          plugin_name,
1201                                          (enum EXTRACTOR_MetaType) ntohl (ent.type),
1202                                          format,
1203                                          mime_type,
1204                                          meta_data,
1205                                          dlen);
1206     }
1207   GNUNET_free_non_null (data);  
1208   return md;
1209 }
1210
1211
1212 /* end of container_meta_data.c */