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