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