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