1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-06-24 22:13:15 +00:00
gnss-sdr/src/core/libs/supl/asn-supl/xer_decoder.c
2021-10-18 18:18:57 +02:00

437 lines
14 KiB
C

/*
* SPDX-FileCopyrightText: (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
* SPDX-License-Identifier: BSD-1-Clause
*/
#include <asn_application.h>
#include <asn_internal.h>
#include <xer_support.h> /* XER/XML parsing support */
/*
* Decode the XER encoding of a given type.
*/
asn_dec_rval_t xer_decode(asn_codec_ctx_t *opt_codec_ctx,
asn_TYPE_descriptor_t *td, void **struct_ptr,
const void *buffer, size_t size)
{
asn_codec_ctx_t s_codec_ctx;
/*
* Stack checker requires that the codec context
* must be allocated on the stack.
*/
if (opt_codec_ctx)
{
if (opt_codec_ctx->max_stack_size)
{
s_codec_ctx = *opt_codec_ctx;
opt_codec_ctx = &s_codec_ctx;
}
}
else
{
/* If context is not given, be security-conscious anyway */
memset(&s_codec_ctx, 0, sizeof(s_codec_ctx));
s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX;
opt_codec_ctx = &s_codec_ctx;
}
/*
* Invoke type-specific decoder.
*/
return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
}
struct xer__cb_arg
{
pxml_chunk_type_e chunk_type;
size_t chunk_size;
const void *chunk_buf;
int callback_not_invoked;
};
static int xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data,
size_t _chunk_size, void *key)
{
struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
arg->chunk_type = type;
arg->chunk_size = _chunk_size;
arg->chunk_buf = _chunk_data;
arg->callback_not_invoked = 0;
return -1; /* Terminate the XML parsing */
}
/*
* Fetch the next token from the XER/XML stream.
*/
ssize_t xer_next_token(int *stateContext, const void *buffer, size_t size,
pxer_chunk_type_e *ch_type)
{
struct xer__cb_arg arg;
int new_stateContext = *stateContext;
ssize_t ret;
arg.callback_not_invoked = 1;
ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
if (ret < 0)
{
return -1;
}
if (arg.callback_not_invoked)
{
assert(ret == 0); /* No data was consumed */
return 0; /* Try again with more data */
}
else
{
assert(arg.chunk_size);
assert(arg.chunk_buf == buffer);
}
/*
* Translate the XML chunk types into more convenient ones.
*/
switch (arg.chunk_type)
{
case PXML_TEXT:
*ch_type = PXER_TEXT;
break;
case PXML_TAG:
return 0; /* Want more */
case PXML_TAG_END:
*ch_type = PXER_TAG;
break;
case PXML_COMMENT:
case PXML_COMMENT_END:
*ch_type = PXER_COMMENT;
break;
}
*stateContext = new_stateContext;
return arg.chunk_size;
}
#define CSLASH 0x2f /* '/' */
#define LANGLE 0x3c /* '<' */
#define RANGLE 0x3e /* '>' */
xer_check_tag_e xer_check_tag(const void *buf_ptr, int size,
const char *need_tag)
{
const char *buf = (const char *)buf_ptr;
const char *end;
xer_check_tag_e ct = XCT_OPENING;
if (size < 2 || buf[0] != LANGLE || buf[size - 1] != RANGLE)
{
if (size >= 2)
{
ASN_DEBUG("Broken XML tag: \"%c...%c\"", buf[0], buf[size - 1]);
}
return XCT_BROKEN;
}
/*
* Determine the tag class.
*/
if (buf[1] == CSLASH)
{
buf += 2; /* advance past "</" */
size -= 3; /* strip "</" and ">" */
ct = XCT_CLOSING;
if (size > 0 && buf[size - 1] == CSLASH)
{
return XCT_BROKEN; /* </abc/> */
}
}
else
{
buf++; /* advance past "<" */
size -= 2; /* strip "<" and ">" */
if (size > 0 && buf[size - 1] == CSLASH)
{
ct = XCT_BOTH;
size--; /* One more, for "/" */
}
}
/* Sometimes we don't care about the tag */
if (!need_tag || !*need_tag)
{
return (xer_check_tag_e)(XCT__UNK__MASK | ct);
}
/*
* Determine the tag name.
*/
for (end = buf + size; buf < end; buf++, need_tag++)
{
int b = *buf;
int n = *need_tag;
if (b != n)
{
if (n == 0)
{
switch (b)
{
case 0x09:
case 0x0a:
case 0x0c:
case 0x0d:
case 0x20:
/* "<abc def/>": whitespace is normal */
return ct;
}
}
return (xer_check_tag_e)(XCT__UNK__MASK | ct);
}
if (b == 0)
{
return XCT_BROKEN; /* Embedded 0 in buf?! */
}
}
if (*need_tag)
{
return (xer_check_tag_e)(XCT__UNK__MASK | ct);
}
return ct;
}
#undef ADVANCE
#define ADVANCE(num_bytes) \
do \
{ \
size_t num = (num_bytes); \
buf_ptr = ((const char *)buf_ptr) + num; \
size -= num; \
consumed_myself += num; \
} \
while (0)
#undef RETURN
#define RETURN(_code) \
do \
{ \
rval.code = _code; \
rval.consumed = consumed_myself; \
if (rval.code != RC_OK) ASN_DEBUG("Failed with %d", rval.code); \
return rval; \
} \
while (0)
#define XER_GOT_BODY(chunk_buf, chunk_size, size) \
do \
{ \
ssize_t converted_size = \
body_receiver(struct_key, chunk_buf, chunk_size, \
(size_t)(chunk_size) < (size)); \
if (converted_size == -1) RETURN(RC_FAIL); \
if (converted_size == 0 && (size) == (size_t)(chunk_size)) \
RETURN(RC_WMORE); \
(chunk_size) = converted_size; \
} \
while (0)
#define XER_GOT_EMPTY() \
do \
{ \
if (body_receiver(struct_key, 0, 0, size > 0) == -1) \
RETURN(RC_FAIL); \
} \
while (0)
/*
* Generalized function for decoding the primitive values.
*/
asn_dec_rval_t xer_decode_general(
asn_codec_ctx_t *opt_codec_ctx,
asn_struct_ctx_t *ctx, /* Type decoder context */
void *struct_key, const char *xml_tag, /* Expected XML tag */
const void *buf_ptr, size_t size,
int (*opt_unexpected_tag_decoder)(void *struct_key, const void *chunk_buf,
size_t chunk_size),
ssize_t (*body_receiver)(void *struct_key, const void *chunk_buf,
size_t chunk_size, int have_more))
{
asn_dec_rval_t rval;
ssize_t consumed_myself = 0;
(void)opt_codec_ctx;
/*
* Phases of XER/XML processing:
* Phase 0: Check that the opening tag matches our expectations.
* Phase 1: Processing body and reacting on closing tag.
*/
if (ctx->phase > 1)
{
RETURN(RC_FAIL);
}
for (;;)
{
pxer_chunk_type_e ch_type; /* XER chunk type */
ssize_t ch_size; /* Chunk size */
xer_check_tag_e tcv; /* Tag check value */
/*
* Get the next part of the XML stream.
*/
ch_size = xer_next_token(&ctx->context, buf_ptr, size, &ch_type);
switch (ch_size)
{
case -1:
RETURN(RC_FAIL);
case 0:
RETURN(RC_WMORE);
default:
switch (ch_type)
{
case PXER_COMMENT: /* Got XML comment */
ADVANCE(ch_size); /* Skip silently */
continue;
case PXER_TEXT:
if (ctx->phase == 0)
{
/*
* We have to ignore whitespace
* here, but in order to be forward
* compatible with EXTENDED-XER
* (EMBED-VALUES, #25) any text is
* just ignored here.
*/
}
else
{
XER_GOT_BODY(buf_ptr, ch_size,
size);
}
ADVANCE(ch_size);
continue;
case PXER_TAG:
break; /* Check the rest down there */
}
}
assert(ch_type == PXER_TAG && size);
tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
/*
* Phase 0:
* Expecting the opening tag
* for the type being processed.
* Phase 1:
* Waiting for the closing XML tag.
*/
switch (tcv)
{
case XCT_BOTH:
if (ctx->phase)
{
break;
}
/* Finished decoding of an empty element */
XER_GOT_EMPTY();
ADVANCE(ch_size);
ctx->phase = 2; /* Phase out */
RETURN(RC_OK);
case XCT_OPENING:
if (ctx->phase)
{
break;
}
ADVANCE(ch_size);
ctx->phase = 1; /* Processing body phase */
continue;
case XCT_CLOSING:
if (!ctx->phase)
{
break;
}
ADVANCE(ch_size);
ctx->phase = 2; /* Phase out */
RETURN(RC_OK);
case XCT_UNKNOWN_BO:
/*
* Certain tags in the body may be expected.
*/
if (opt_unexpected_tag_decoder &&
opt_unexpected_tag_decoder(struct_key, buf_ptr,
ch_size) >= 0)
{
/* Tag's processed fine */
ADVANCE(ch_size);
if (!ctx->phase)
{
/* We are not expecting
* the closing tag anymore. */
ctx->phase = 2; /* Phase out */
RETURN(RC_OK);
}
continue;
}
/* Fall through */
default:
break; /* Unexpected tag */
}
ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
break; /* Dark and mysterious things have just happened */
}
RETURN(RC_FAIL);
}
int xer_is_whitespace(const void *chunk_buf, size_t chunk_size)
{
const char *p = (const char *)chunk_buf;
const char *pend = p + chunk_size;
for (; p < pend; p++)
{
switch (*p)
{
/* X.693, #8.1.4
* HORISONTAL TAB (9)
* LINE FEED (10)
* CARRIAGE RETURN (13)
* SPACE (32)
*/
case 0x09:
case 0x0a:
case 0x0d:
case 0x20:
break;
default:
return 0;
}
}
return 1; /* All whitespace */
}
/*
* This is a vastly simplified, non-validating XML tree skipper.
*/
int xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth)
{
assert(*depth > 0);
switch (tcv)
{
case XCT_BOTH:
case XCT_UNKNOWN_BO:
/* These negate each other. */
return 0;
case XCT_OPENING:
case XCT_UNKNOWN_OP:
++(*depth);
return 0;
case XCT_CLOSING:
case XCT_UNKNOWN_CL:
if (--(*depth) == 0)
{
return (tcv == XCT_CLOSING) ? 2 : 1;
}
return 0;
default:
return -1;
}
}