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