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