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