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