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