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