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