Try normal method (uncompressed) if Z_DATA_ERROR is returned.
[portableproplib.git] / src / prop_data.c
blobd4358104577bf10a858de0d9d909d21c9e32bca1
1 /* $NetBSD: prop_data.c,v 1.13 2008/08/03 04:00:12 thorpej Exp $ */
3 /*-
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <prop/prop_data.h>
33 #include "prop_object_impl.h"
35 #include <errno.h>
36 #include <limits.h>
37 #include <stdlib.h>
39 struct _prop_data {
40 struct _prop_object pd_obj;
41 union {
42 void * pdu_mutable;
43 const void * pdu_immutable;
44 } pd_un;
45 #define pd_mutable pd_un.pdu_mutable
46 #define pd_immutable pd_un.pdu_immutable
47 size_t pd_size;
48 int pd_flags;
51 #define PD_F_NOCOPY 0x01
53 _PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
55 _PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
56 "property data container object")
58 static _prop_object_free_rv_t
59 _prop_data_free(prop_stack_t, prop_object_t *);
60 static bool _prop_data_externalize(
61 struct _prop_object_externalize_context *,
62 void *);
63 static _prop_object_equals_rv_t
64 _prop_data_equals(prop_object_t, prop_object_t,
65 void **, void **,
66 prop_object_t *, prop_object_t *);
68 static const struct _prop_object_type _prop_object_type_data = {
69 .pot_type = PROP_TYPE_DATA,
70 .pot_free = _prop_data_free,
71 .pot_extern = _prop_data_externalize,
72 .pot_equals = _prop_data_equals,
75 #define prop_object_is_data(x) \
76 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
78 /* ARGSUSED */
79 static _prop_object_free_rv_t
80 _prop_data_free(prop_stack_t stack, prop_object_t *obj)
82 prop_data_t pd = *obj;
84 if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
85 _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
86 _PROP_POOL_PUT(_prop_data_pool, pd);
88 return (_PROP_OBJECT_FREE_DONE);
91 static const char _prop_data_base64[] =
92 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
93 static const char _prop_data_pad64 = '=';
95 static bool
96 _prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
98 prop_data_t pd = v;
99 size_t i, srclen;
100 const uint8_t *src;
101 uint8_t output[4];
102 uint8_t input[3];
104 if (pd->pd_size == 0)
105 return (_prop_object_externalize_empty_tag(ctx, "data"));
107 if (_prop_object_externalize_start_tag(ctx, "data") == false)
108 return (false);
110 for (src = pd->pd_immutable, srclen = pd->pd_size;
111 srclen > 2; srclen -= 3) {
112 input[0] = *src++;
113 input[1] = *src++;
114 input[2] = *src++;
116 output[0] = (uint32_t)input[0] >> 2;
117 output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
118 ((uint32_t)input[1] >> 4);
119 output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
120 ((uint32_t)input[2] >> 6);
121 output[3] = input[2] & 0x3f;
122 _PROP_ASSERT(output[0] < 64);
123 _PROP_ASSERT(output[1] < 64);
124 _PROP_ASSERT(output[2] < 64);
125 _PROP_ASSERT(output[3] < 64);
127 if (_prop_object_externalize_append_char(ctx,
128 _prop_data_base64[output[0]]) == false ||
129 _prop_object_externalize_append_char(ctx,
130 _prop_data_base64[output[1]]) == false ||
131 _prop_object_externalize_append_char(ctx,
132 _prop_data_base64[output[2]]) == false ||
133 _prop_object_externalize_append_char(ctx,
134 _prop_data_base64[output[3]]) == false)
135 return (false);
138 if (srclen != 0) {
139 input[0] = input[1] = input[2] = '\0';
140 for (i = 0; i < srclen; i++)
141 input[i] = *src++;
143 output[0] = (uint32_t)input[0] >> 2;
144 output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
145 ((uint32_t)input[1] >> 4);
146 output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
147 ((uint32_t)input[2] >> 6);
148 _PROP_ASSERT(output[0] < 64);
149 _PROP_ASSERT(output[1] < 64);
150 _PROP_ASSERT(output[2] < 64);
152 if (_prop_object_externalize_append_char(ctx,
153 _prop_data_base64[output[0]]) == false ||
154 _prop_object_externalize_append_char(ctx,
155 _prop_data_base64[output[1]]) == false ||
156 _prop_object_externalize_append_char(ctx,
157 srclen == 1 ? _prop_data_pad64
158 : _prop_data_base64[output[2]]) == false ||
159 _prop_object_externalize_append_char(ctx,
160 _prop_data_pad64) == false)
161 return (false);
164 if (_prop_object_externalize_end_tag(ctx, "data") == false)
165 return (false);
167 return (true);
170 /* ARGSUSED */
171 static _prop_object_equals_rv_t
172 _prop_data_equals(prop_object_t v1, prop_object_t v2,
173 void **stored_pointer1, void **stored_pointer2,
174 prop_object_t *next_obj1, prop_object_t *next_obj2)
176 prop_data_t pd1 = v1;
177 prop_data_t pd2 = v2;
179 if (pd1 == pd2)
180 return (_PROP_OBJECT_EQUALS_TRUE);
181 if (pd1->pd_size != pd2->pd_size)
182 return (_PROP_OBJECT_EQUALS_FALSE);
183 if (pd1->pd_size == 0) {
184 _PROP_ASSERT(pd1->pd_immutable == NULL);
185 _PROP_ASSERT(pd2->pd_immutable == NULL);
186 return (_PROP_OBJECT_EQUALS_TRUE);
188 if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
189 return _PROP_OBJECT_EQUALS_TRUE;
190 else
191 return _PROP_OBJECT_EQUALS_FALSE;
194 static prop_data_t
195 _prop_data_alloc(void)
197 prop_data_t pd;
199 pd = _PROP_POOL_GET(_prop_data_pool);
200 if (pd != NULL) {
201 _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
203 pd->pd_mutable = NULL;
204 pd->pd_size = 0;
205 pd->pd_flags = 0;
208 return (pd);
212 * prop_data_create_data --
213 * Create a data container that contains a copy of the data.
215 prop_data_t
216 prop_data_create_data(const void *v, size_t size)
218 prop_data_t pd;
219 void *nv;
221 pd = _prop_data_alloc();
222 if (pd != NULL && size != 0) {
223 nv = _PROP_MALLOC(size, M_PROP_DATA);
224 if (nv == NULL) {
225 prop_object_release(pd);
226 return (NULL);
228 memcpy(nv, v, size);
229 pd->pd_mutable = nv;
230 pd->pd_size = size;
232 return (pd);
236 * prop_data_create_data_nocopy --
237 * Create an immutable data container that contains a refrence to the
238 * provided external data.
240 prop_data_t
241 prop_data_create_data_nocopy(const void *v, size_t size)
243 prop_data_t pd;
245 pd = _prop_data_alloc();
246 if (pd != NULL) {
247 pd->pd_immutable = v;
248 pd->pd_size = size;
249 pd->pd_flags |= PD_F_NOCOPY;
251 return (pd);
255 * prop_data_copy --
256 * Copy a data container. If the original data is external, then
257 * the copy is also references the same external data.
259 prop_data_t
260 prop_data_copy(prop_data_t opd)
262 prop_data_t pd;
264 if (! prop_object_is_data(opd))
265 return (NULL);
267 pd = _prop_data_alloc();
268 if (pd != NULL) {
269 pd->pd_size = opd->pd_size;
270 pd->pd_flags = opd->pd_flags;
271 if (opd->pd_flags & PD_F_NOCOPY)
272 pd->pd_immutable = opd->pd_immutable;
273 else if (opd->pd_size != 0) {
274 void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
275 if (nv == NULL) {
276 prop_object_release(pd);
277 return (NULL);
279 memcpy(nv, opd->pd_immutable, opd->pd_size);
280 pd->pd_mutable = nv;
283 return (pd);
287 * prop_data_size --
288 * Return the size of the data.
290 size_t
291 prop_data_size(prop_data_t pd)
294 if (! prop_object_is_data(pd))
295 return (0);
297 return (pd->pd_size);
301 * prop_data_data --
302 * Return a copy of the contents of the data container.
303 * The data is allocated with the M_TEMP malloc type.
304 * If the data container is empty, NULL is returned.
306 void *
307 prop_data_data(prop_data_t pd)
309 void *v;
311 if (! prop_object_is_data(pd))
312 return (NULL);
314 if (pd->pd_size == 0) {
315 _PROP_ASSERT(pd->pd_immutable == NULL);
316 return (NULL);
319 _PROP_ASSERT(pd->pd_immutable != NULL);
321 v = _PROP_MALLOC(pd->pd_size, M_TEMP);
322 if (v != NULL)
323 memcpy(v, pd->pd_immutable, pd->pd_size);
325 return (v);
329 * prop_data_data_nocopy --
330 * Return an immutable reference to the contents of the data
331 * container.
333 const void *
334 prop_data_data_nocopy(prop_data_t pd)
337 if (! prop_object_is_data(pd))
338 return (NULL);
340 _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) ||
341 (pd->pd_size != 0 && pd->pd_immutable != NULL));
343 return (pd->pd_immutable);
347 * prop_data_equals --
348 * Return true if two strings are equivalent.
350 bool
351 prop_data_equals(prop_data_t pd1, prop_data_t pd2)
353 if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
354 return (false);
356 return (prop_object_equals(pd1, pd2));
360 * prop_data_equals_data --
361 * Return true if the contained data is equivalent to the specified
362 * external data.
364 bool
365 prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
368 if (! prop_object_is_data(pd))
369 return (false);
371 if (pd->pd_size != size)
372 return (false);
373 return (memcmp(pd->pd_immutable, v, size) == 0);
376 static bool
377 _prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
378 uint8_t *target, size_t targsize, size_t *sizep,
379 const char **cpp)
381 const char *src;
382 size_t tarindex;
383 int state, ch;
384 const char *pos;
386 state = 0;
387 tarindex = 0;
388 src = ctx->poic_cp;
390 for (;;) {
391 ch = (unsigned char) *src++;
392 if (_PROP_EOF(ch))
393 return (false);
394 if (_PROP_ISSPACE(ch))
395 continue;
396 if (ch == '<') {
397 src--;
398 break;
400 if (ch == _prop_data_pad64)
401 break;
403 pos = strchr(_prop_data_base64, ch);
404 if (pos == NULL)
405 return (false);
407 switch (state) {
408 case 0:
409 if (target) {
410 if (tarindex >= targsize)
411 return (false);
412 target[tarindex] =
413 (uint8_t)((pos - _prop_data_base64) << 2);
415 state = 1;
416 break;
418 case 1:
419 if (target) {
420 if (tarindex + 1 >= targsize)
421 return (false);
422 target[tarindex] |=
423 (uint32_t)(pos - _prop_data_base64) >> 4;
424 target[tarindex + 1] =
425 (uint8_t)(((pos - _prop_data_base64) & 0xf)
426 << 4);
428 tarindex++;
429 state = 2;
430 break;
432 case 2:
433 if (target) {
434 if (tarindex + 1 >= targsize)
435 return (false);
436 target[tarindex] |=
437 (uint32_t)(pos - _prop_data_base64) >> 2;
438 target[tarindex + 1] =
439 (uint8_t)(((pos - _prop_data_base64)
440 & 0x3) << 6);
442 tarindex++;
443 state = 3;
444 break;
446 case 3:
447 if (target) {
448 if (tarindex >= targsize)
449 return (false);
450 target[tarindex] |= (uint8_t)
451 (pos - _prop_data_base64);
453 tarindex++;
454 state = 0;
455 break;
457 default:
458 _PROP_ASSERT(/*CONSTCOND*/0);
463 * We are done decoding the Base64 characters. Let's see if we
464 * ended up on a byte boundary and/or with unrecognized trailing
465 * characters.
467 if (ch == _prop_data_pad64) {
468 ch = (unsigned char) *src; /* src already advanced */
469 if (_PROP_EOF(ch))
470 return (false);
471 switch (state) {
472 case 0: /* Invalid = in first position */
473 case 1: /* Invalid = in second position */
474 return (false);
476 case 2: /* Valid, one byte of info */
477 /* Skip whitespace */
478 for (ch = (unsigned char) *src++;
479 ch != '<'; ch = (unsigned char) *src++) {
480 if (_PROP_EOF(ch))
481 return (false);
482 if (!_PROP_ISSPACE(ch))
483 break;
485 /* Make sure there is another trailing = */
486 if (ch != _prop_data_pad64)
487 return (false);
488 ch = (unsigned char) *src;
489 /* FALLTHROUGH */
491 case 3: /* Valid, two bytes of info */
493 * We know this char is a =. Is there anything but
494 * whitespace after it?
496 for (ch = (unsigned char) *src++;
497 ch != '<'; ch = (unsigned char) *src++) {
498 if (_PROP_EOF(ch))
499 return (false);
500 if (!_PROP_ISSPACE(ch))
501 return (false);
503 /* back up to '<' */
504 src--;
506 } else {
508 * We ended by seeing the end of the Base64 string. Make
509 * sure there are no partial bytes lying around.
511 if (state != 0)
512 return (false);
515 _PROP_ASSERT(*src == '<');
516 if (sizep != NULL)
517 *sizep = tarindex;
518 if (cpp != NULL)
519 *cpp = src;
521 return (true);
525 * _prop_data_internalize --
526 * Parse a <data>...</data> and return the object created from the
527 * external representation.
530 /* strtoul is used for parsing, enforce. */
531 typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
533 /* ARGSUSED */
534 bool
535 _prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
536 struct _prop_object_internalize_context *ctx)
538 prop_data_t data;
539 uint8_t *buf;
540 size_t len, alen;
543 * We don't accept empty elements.
544 * This actually only checks for the node to be <data/>
545 * (Which actually causes another error if found.)
547 if (ctx->poic_is_empty_element)
548 return (true);
551 * If we got a "size" attribute, get the size of the data blob
552 * from that. Otherwise, we have to figure it out from the base64.
554 if (ctx->poic_tagattr != NULL) {
555 char *cp;
557 if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
558 ctx->poic_tagattrval_len == 0)
559 return (true);
561 errno = 0;
562 len = strtoul(ctx->poic_tagattrval, &cp, 0);
563 if (len == ULONG_MAX && errno == ERANGE)
564 return (true);
565 if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
566 return (true);
567 _PROP_ASSERT(*cp == '\"');
568 } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
569 NULL) == false)
570 return (true);
573 * Always allocate one extra in case we don't land on an even byte
574 * boundary during the decode.
576 buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
577 if (buf == NULL)
578 return (true);
580 if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
581 &ctx->poic_cp) == false) {
582 _PROP_FREE(buf, M_PROP_DATA);
583 return (true);
585 if (alen != len) {
586 _PROP_FREE(buf, M_PROP_DATA);
587 return (true);
590 if (_prop_object_internalize_find_tag(ctx, "data",
591 _PROP_TAG_TYPE_END) == false) {
592 _PROP_FREE(buf, M_PROP_DATA);
593 return (true);
596 data = _prop_data_alloc();
597 if (data == NULL) {
598 _PROP_FREE(buf, M_PROP_DATA);
599 return (true);
603 * Handle alternate type of empty node.
604 * XML document could contain open/close tags, yet still be empty.
606 if (alen == 0) {
607 _PROP_FREE(buf, M_PROP_DATA);
608 data->pd_mutable = NULL;
609 } else {
610 data->pd_mutable = buf;
612 data->pd_size = len;
614 *obj = data;
615 return (true);