rename NETWORK_socket API to NETWORK_connection (to make room for the new actual...
[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 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 #define EXTRA_CHECKS ALLOW_EXTRA_CHECKS
36
37 struct Item
38 {
39   EXTRACTOR_KeywordType type;
40   char *data;
41 };
42
43 /**
44  * Meta data to associate with a file, directory or namespace.
45  */
46 struct GNUNET_CONTAINER_MetaData
47 {
48   uint32_t itemCount;
49   struct Item *items;
50 };
51
52 /**
53  * Create a fresh struct CONTAINER_MetaData token.
54  */
55 struct GNUNET_CONTAINER_MetaData *
56 GNUNET_CONTAINER_meta_data_create ()
57 {
58   struct GNUNET_CONTAINER_MetaData *ret;
59   ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData));
60   ret->items = NULL;
61   ret->itemCount = 0;
62   return ret;
63 }
64
65 /**
66  * Free meta data.
67  */
68 void
69 GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
70 {
71   int i;
72
73   if (md == NULL)
74     return;
75   for (i = 0; i < md->itemCount; i++)
76     GNUNET_free (md->items[i].data);
77   GNUNET_array_grow (md->items, md->itemCount, 0);
78   GNUNET_free (md);
79 }
80
81 /**
82  * Add the current time as the publication date
83  * to the meta-data.
84  */
85 void
86 GNUNET_CONTAINER_meta_data_add_publication_date (struct
87                                                  GNUNET_CONTAINER_MetaData
88                                                  *md)
89 {
90   char *dat;
91   struct GNUNET_TIME_Absolute t;
92
93   t = GNUNET_TIME_absolute_get ();
94   GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_PUBLICATION_DATE, NULL);
95   dat = GNUNET_STRINGS_absolute_time_to_string (t);
96   GNUNET_CONTAINER_meta_data_insert (md, EXTRACTOR_PUBLICATION_DATE, dat);
97   GNUNET_free (dat);
98 }
99
100 /**
101  * Extend metadata.
102  * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
103  */
104 int
105 GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
106                                    EXTRACTOR_KeywordType type,
107                                    const char *data)
108 {
109   uint32_t idx;
110   char *p;
111
112   GNUNET_assert (data != NULL);
113   for (idx = 0; idx < md->itemCount; idx++)
114     {
115       if ((md->items[idx].type == type) &&
116           (0 == strcmp (md->items[idx].data, data)))
117         return GNUNET_SYSERR;
118     }
119   idx = md->itemCount;
120   GNUNET_array_grow (md->items, md->itemCount, md->itemCount + 1);
121   md->items[idx].type = type;
122   md->items[idx].data = p = GNUNET_strdup (data);
123
124   /* change OS native dir separators to unix '/' and others to '_' */
125   if (type == EXTRACTOR_FILENAME)
126     {
127       while (*p != '\0')
128         {
129           if (*p == DIR_SEPARATOR)
130             *p = '/';
131           else if (*p == '\\')
132             *p = '_';
133           p++;
134         }
135     }
136
137   return GNUNET_OK;
138 }
139
140 /**
141  * Remove an item.
142  * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md
143  */
144 int
145 GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md,
146                                    EXTRACTOR_KeywordType type,
147                                    const char *data)
148 {
149   uint32_t idx;
150   int ret = GNUNET_SYSERR;
151   for (idx = 0; idx < md->itemCount; idx++)
152     {
153       if ((md->items[idx].type == type) &&
154           ((data == NULL) || (0 == strcmp (md->items[idx].data, data))))
155         {
156           GNUNET_free (md->items[idx].data);
157           md->items[idx] = md->items[md->itemCount - 1];
158           GNUNET_array_grow (md->items, md->itemCount, md->itemCount - 1);
159           if (data == NULL)
160             {
161               ret = GNUNET_OK;
162               continue;
163             }
164           return GNUNET_OK;
165         }
166     }
167   return ret;
168 }
169
170 /**
171  * Iterate over MD entries, excluding thumbnails.
172  *
173  * @return number of entries
174  */
175 int
176 GNUNET_CONTAINER_meta_data_get_contents (const struct
177                                          GNUNET_CONTAINER_MetaData *md,
178                                          GNUNET_CONTAINER_MetaDataProcessor
179                                          iterator, void *closure)
180 {
181   uint32_t i;
182   uint32_t sub;
183
184   sub = 0;
185   for (i = 0; i < md->itemCount; i++)
186     {
187       if (!EXTRACTOR_isBinaryType (md->items[i].type))
188         {
189           if ((iterator != NULL) &&
190               (GNUNET_OK != iterator (closure,
191                                       md->items[i].type,
192                                       md->items[i].data)))
193             return GNUNET_SYSERR;
194         }
195       else
196         sub++;
197     }
198   return (int) (md->itemCount - sub);
199 }
200
201 /**
202  * Iterate over MD entries
203  *
204  * @return number of entries
205  */
206 char *
207 GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
208                                         *md, EXTRACTOR_KeywordType type)
209 {
210   uint32_t i;
211
212   for (i = 0; i < md->itemCount; i++)
213     if (type == md->items[i].type)
214       return GNUNET_strdup (md->items[i].data);
215   return NULL;
216 }
217
218 /**
219  * Iterate over MD entries
220  *
221  * @return number of entries
222  */
223 char *
224 GNUNET_CONTAINER_meta_data_get_first_by_types (const struct
225                                                GNUNET_CONTAINER_MetaData *md,
226                                                ...)
227 {
228   char *ret;
229   va_list args;
230   EXTRACTOR_KeywordType type;
231
232   ret = NULL;
233   va_start (args, md);
234   while (1)
235     {
236       type = va_arg (args, EXTRACTOR_KeywordType);
237       if (type == -1)
238         break;
239       ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type);
240       if (ret != NULL)
241         break;
242     }
243   va_end (args);
244   return ret;
245 }
246
247 /**
248  * Get a thumbnail from the meta-data (if present).
249  *
250  * @param thumb will be set to the thumbnail data.  Must be
251  *        freed by the caller!
252  * @return number of bytes in thumbnail, 0 if not available
253  */
254 size_t
255 GNUNET_CONTAINER_meta_data_get_thumbnail (const struct
256                                           GNUNET_CONTAINER_MetaData * md,
257                                           unsigned char **thumb)
258 {
259   char *encoded;
260   int ret;
261   size_t size;
262
263   encoded =
264     GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_THUMBNAIL_DATA);
265   if (encoded == NULL)
266     return 0;
267   if (strlen (encoded) == 0)
268     {
269       GNUNET_free (encoded);
270       return 0;                 /* invalid */
271     }
272   *thumb = NULL;
273   ret = EXTRACTOR_binaryDecode (encoded, thumb, &size);
274   GNUNET_free (encoded);
275   if (ret != 0)
276     return 0;
277   return size;
278 }
279
280 /**
281  * Duplicate struct GNUNET_CONTAINER_MetaData.
282  */
283 struct GNUNET_CONTAINER_MetaData *
284 GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
285                                       *md)
286 {
287   uint32_t i;
288   struct GNUNET_CONTAINER_MetaData *ret;
289
290   if (md == NULL)
291     return NULL;
292   ret = GNUNET_CONTAINER_meta_data_create ();
293   for (i = 0; i < md->itemCount; i++)
294     GNUNET_CONTAINER_meta_data_insert (ret, md->items[i].type,
295                                        md->items[i].data);
296   return ret;
297 }
298
299 /**
300  * Extract meta-data from a file.
301  *
302  * @return GNUNET_SYSERR on error, otherwise the number
303  *   of meta-data items obtained
304  */
305 int
306 GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData
307                                               *md, const char *filename,
308                                               EXTRACTOR_ExtractorList *
309                                               extractors)
310 {
311   EXTRACTOR_KeywordList *head;
312   EXTRACTOR_KeywordList *pos;
313   int ret;
314
315   if (filename == NULL)
316     return GNUNET_SYSERR;
317   if (extractors == NULL)
318     return 0;
319   head = EXTRACTOR_getKeywords (extractors, filename);
320   head = EXTRACTOR_removeDuplicateKeywords (head,
321                                             EXTRACTOR_DUPLICATES_REMOVE_UNKNOWN);
322   pos = head;
323   ret = 0;
324   while (pos != NULL)
325     {
326       if (GNUNET_OK ==
327           GNUNET_CONTAINER_meta_data_insert (md, pos->keywordType,
328                                              pos->keyword))
329         ret++;
330       pos = pos->next;
331     }
332   EXTRACTOR_freeKeywords (head);
333   return ret;
334 }
335
336 static unsigned int
337 tryCompression (char *data, unsigned int oldSize)
338 {
339   char *tmp;
340   uLongf dlen;
341
342 #ifdef compressBound
343   dlen = compressBound (oldSize);
344 #else
345   dlen = oldSize + (oldSize / 100) + 20;
346   /* documentation says 100.1% oldSize + 12 bytes, but we
347      should be able to overshoot by more to be safe */
348 #endif
349   tmp = GNUNET_malloc (dlen);
350   if (Z_OK == compress2 ((Bytef *) tmp,
351                          &dlen, (const Bytef *) data, oldSize, 9))
352     {
353       if (dlen < oldSize)
354         {
355           memcpy (data, tmp, dlen);
356           GNUNET_free (tmp);
357           return dlen;
358         }
359     }
360   GNUNET_free (tmp);
361   return oldSize;
362 }
363
364 /**
365  * Decompress input, return the decompressed data
366  * as output, set outputSize to the number of bytes
367  * that were found.
368  *
369  * @return NULL on error
370  */
371 static char *
372 decompress (const char *input,
373             unsigned int inputSize, unsigned int outputSize)
374 {
375   char *output;
376   uLongf olen;
377
378   olen = outputSize;
379   output = GNUNET_malloc (olen);
380   if (Z_OK == uncompress ((Bytef *) output,
381                           &olen, (const Bytef *) input, inputSize))
382     {
383       return output;
384     }
385   else
386     {
387       GNUNET_free (output);
388       return NULL;
389     }
390 }
391
392 /**
393  * Flag in 'version' that indicates compressed meta-data.
394  */
395 #define HEADER_COMPRESSED 0x80000000
396
397 /**
398  * Bits in 'version' that give the version number.
399  */
400 #define HEADER_VERSION_MASK 0x7FFFFFFF
401
402 struct MetaDataHeader
403 {
404   /**
405    * The version of the MD serialization.
406    * The highest bit is used to indicate
407    * compression.
408    *
409    * Version 0 is the current version;
410    * Version is 1 for a NULL pointer.
411    * Other version numbers are not yet defined.
412    */
413   uint32_t version;
414
415   /**
416    * How many MD entries are there?
417    */
418   uint32_t entries;
419
420   /**
421    * Size of the MD (decompressed)
422    */
423   uint32_t size;
424
425   /**
426    * This is followed by 'entries' values of type 'unsigned int' that
427    * correspond to EXTRACTOR_KeywordTypes.  After that, the meta-data
428    * keywords follow (0-terminated).  The MD block always ends with
429    * 0-termination, padding with 0 until a multiple of 8 bytes.
430    */
431
432 };
433
434 /**
435  * Serialize meta-data to target.
436  *
437  * @param size maximum number of bytes available
438  * @param part is it ok to just write SOME of the
439  *        meta-data to match the size constraint,
440  *        possibly discarding some data?
441  * @return number of bytes written on success,
442  *         GNUNET_SYSERR on error (typically: not enough
443  *         space)
444  */
445 int
446 GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
447                                       *md, char *target, unsigned int max,
448                                       enum
449                                       GNUNET_CONTAINER_MetaDataSerializationOptions
450                                       part)
451 {
452   struct MetaDataHeader *hdr;
453   size_t size;
454   size_t pos;
455   uint32_t i;
456   size_t len;
457   uint32_t ic;
458
459   if (max < sizeof (struct MetaDataHeader))
460     return GNUNET_SYSERR;       /* far too small */
461   ic = md ? md->itemCount : 0;
462   hdr = NULL;
463   while (1)
464     {
465       size = sizeof (struct MetaDataHeader);
466       size += sizeof (unsigned int) * ic;
467       for (i = 0; i < ic; i++)
468         size += 1 + strlen (md->items[i].data);
469       while (size % 8 != 0)
470         size++;
471       hdr = GNUNET_malloc (size);
472       hdr->version = htonl (md == NULL ? 1 : 0);
473       hdr->entries = htonl (ic);
474       for (i = 0; i < ic; i++)
475         ((unsigned int *) &hdr[1])[i] =
476           htonl ((unsigned int) md->items[i].type);
477       pos = sizeof (struct MetaDataHeader);
478       pos += sizeof (unsigned int) * ic;
479       for (i = 0; i < ic; i++)
480         {
481           len = strlen (md->items[i].data) + 1;
482           memcpy (&((char *) hdr)[pos], md->items[i].data, len);
483           pos += len;
484         }
485
486       hdr->size = htonl (size);
487       if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
488         {
489           pos = tryCompression ((char *) &hdr[1],
490                                 size - sizeof (struct MetaDataHeader));
491         }
492       else
493         {
494           pos = size - sizeof (struct MetaDataHeader);
495         }
496       if (pos < size - sizeof (struct MetaDataHeader))
497         {
498           hdr->version = htonl (HEADER_COMPRESSED);
499           size = pos + sizeof (struct MetaDataHeader);
500         }
501       if (size <= max)
502         break;
503       GNUNET_free (hdr);
504       hdr = NULL;
505
506       if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART) == 0)
507         {
508           return GNUNET_SYSERR; /* does not fit! */
509         }
510       /* partial serialization ok, try again with less meta-data */
511       if (size > 2 * max)
512         ic = ic * 2 / 3;        /* still far too big, make big reductions */
513       else
514         ic--;                   /* small steps, we're close */
515     }
516   GNUNET_assert (size <= max);
517   memcpy (target, hdr, size);
518   GNUNET_free (hdr);
519   /* extra check: deserialize! */
520 #if EXTRA_CHECKS
521   {
522     struct GNUNET_CONTAINER_MetaData *mdx;
523     mdx = GNUNET_CONTAINER_meta_data_deserialize (target, size);
524     GNUNET_assert (NULL != mdx);
525     GNUNET_CONTAINER_meta_data_destroy (mdx);
526   }
527 #endif
528   return size;
529 }
530
531 /**
532  * Estimate (!) the size of the meta-data in
533  * serialized form.  The estimate MAY be higher
534  * than what is strictly needed.
535  */
536 unsigned int
537 GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
538                                                 GNUNET_CONTAINER_MetaData *md,
539                                                 enum
540                                                 GNUNET_CONTAINER_MetaDataSerializationOptions
541                                                 part)
542 {
543   struct MetaDataHeader *hdr;
544   size_t size;
545   size_t pos;
546   uint32_t i;
547   size_t len;
548   uint32_t ic;
549
550   ic = md ? md->itemCount : 0;
551   size = sizeof (struct MetaDataHeader);
552   size += sizeof (unsigned int) * ic;
553   for (i = 0; i < ic; i++)
554     size += 1 + strlen (md->items[i].data);
555   while (size % 8 != 0)
556     size++;
557   hdr = GNUNET_malloc (size);
558   hdr->version = htonl (md == NULL ? 1 : 0);
559   hdr->entries = htonl (ic);
560   for (i = 0; i < ic; i++)
561     ((unsigned int *) &hdr[1])[i] = htonl ((unsigned int) md->items[i].type);
562   pos = sizeof (struct MetaDataHeader);
563   pos += sizeof (unsigned int) * ic;
564   for (i = 0; i < ic; i++)
565     {
566       len = strlen (md->items[i].data) + 1;
567       memcpy (&((char *) hdr)[pos], md->items[i].data, len);
568       pos += len;
569     }
570   if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0)
571     {
572       pos =
573         tryCompression ((char *) &hdr[1],
574                         size - sizeof (struct MetaDataHeader));
575     }
576   else
577     {
578       pos = size - sizeof (struct MetaDataHeader);
579     }
580   if (pos < size - sizeof (struct MetaDataHeader))
581     size = pos + sizeof (struct MetaDataHeader);
582   GNUNET_free (hdr);
583   return size;
584 }
585
586 /**
587  * Deserialize meta-data.  Initializes md.
588  * @param size number of bytes available
589  * @return MD on success, NULL on error (i.e.
590  *         bad format)
591  */
592 struct GNUNET_CONTAINER_MetaData *
593 GNUNET_CONTAINER_meta_data_deserialize (const char *input, unsigned int size)
594 {
595   struct GNUNET_CONTAINER_MetaData *md;
596   const struct MetaDataHeader *hdr;
597   uint32_t ic;
598   char *data;
599   const char *cdata;
600   uint32_t dataSize;
601   int compressed;
602   int i;
603   unsigned int pos;
604   int len;
605   uint32_t version;
606
607   if (size < sizeof (struct MetaDataHeader))
608     return NULL;
609   hdr = (const struct MetaDataHeader *) input;
610   version = ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_VERSION_MASK;
611   if (version == 1)
612     return NULL;                /* null pointer */
613   if (version != 0)
614     {
615       GNUNET_break_op (0);      /* unsupported version */
616       return NULL;
617     }
618   ic = ntohl (MAKE_UNALIGNED (hdr->entries));
619   compressed =
620     (ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_COMPRESSED) != 0;
621   if (compressed)
622     {
623       dataSize =
624         ntohl (MAKE_UNALIGNED (hdr->size)) - sizeof (struct MetaDataHeader);
625       if (dataSize > 2 * 1042 * 1024)
626         {
627           GNUNET_break (0);
628           return NULL;          /* only 2 MB allowed [to make sure we don't blow
629                                    our memory limit because of a mal-formed
630                                    message... ] */
631         }
632       data =
633         decompress ((const char *) &input[sizeof (struct MetaDataHeader)],
634                     size - sizeof (struct MetaDataHeader), dataSize);
635       if (data == NULL)
636         {
637           GNUNET_break_op (0);
638           return NULL;
639         }
640       cdata = data;
641     }
642   else
643     {
644       data = NULL;
645       cdata = (const char *) &hdr[1];
646       dataSize = size - sizeof (struct MetaDataHeader);
647       if (size != ntohl (MAKE_UNALIGNED (hdr->size)))
648         {
649           GNUNET_break (0);
650           return NULL;
651         }
652     }
653
654   if ((sizeof (unsigned int) * ic + ic) > dataSize)
655     {
656       GNUNET_break (0);
657       goto FAILURE;
658     }
659   if ((ic > 0) && (cdata[dataSize - 1] != '\0'))
660     {
661       GNUNET_break (0);
662       goto FAILURE;
663     }
664
665   md = GNUNET_CONTAINER_meta_data_create ();
666   GNUNET_array_grow (md->items, md->itemCount, ic);
667   i = 0;
668   pos = sizeof (unsigned int) * ic;
669   while ((pos < dataSize) && (i < ic))
670     {
671       len = strlen (&cdata[pos]) + 1;
672       md->items[i].type = (EXTRACTOR_KeywordType)
673         ntohl (MAKE_UNALIGNED (((const unsigned int *) cdata)[i]));
674       md->items[i].data = GNUNET_strdup (&cdata[pos]);
675       pos += len;
676       i++;
677     }
678   if (i < ic)
679     {                           /* oops */
680       GNUNET_CONTAINER_meta_data_destroy (md);
681       goto FAILURE;
682     }
683   GNUNET_free_non_null (data);
684   return md;
685 FAILURE:
686   GNUNET_free_non_null (data);
687   return NULL;                  /* size too small */
688 }
689
690 /**
691  * Test if two MDs are equal.
692  */
693 int
694 GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
695                                        *md1,
696                                        const struct GNUNET_CONTAINER_MetaData
697                                        *md2)
698 {
699   uint32_t i;
700   uint32_t j;
701   int found;
702
703   if (md1->itemCount != md2->itemCount)
704     return GNUNET_NO;
705   for (i = 0; i < md1->itemCount; i++)
706     {
707       found = GNUNET_NO;
708       for (j = 0; j < md2->itemCount; j++)
709         if ((md1->items[i].type == md2->items[j].type) &&
710             (0 == strcmp (md1->items[i].data, md2->items[j].data)))
711           {
712             found = GNUNET_YES;
713             break;
714           }
715       if (found == GNUNET_NO)
716         return GNUNET_NO;
717     }
718   return GNUNET_YES;
719 }
720
721
722 /* end of container_meta_data.c */