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