2004-10-20 15:50:55 +00:00
|
|
|
|
|
|
|
#include <asn_application.h>
|
|
|
|
#include <asn_internal.h>
|
|
|
|
#include <xer_support.h> /* XER/XML parsing support */
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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, void *buffer, size_t size) {
|
|
|
|
asn_codec_ctx_t s_codec_ctx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Satisfy the requirement that the codec context
|
|
|
|
* must be allocated on the stack.
|
|
|
|
*/
|
|
|
|
if(opt_codec_ctx && opt_codec_ctx->max_stack_size) {
|
|
|
|
s_codec_ctx = *opt_codec_ctx;
|
|
|
|
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;
|
|
|
|
void *chunk_buf;
|
|
|
|
int callback_not_invoked;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
xer__token_cb(pxml_chunk_type_e type, 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, void *buffer, size_t size,
|
|
|
|
pxer_chunk_type_e *ch_type) {
|
|
|
|
struct xer__cb_arg arg;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
arg.callback_not_invoked = 1;
|
|
|
|
ret = pxml_parse(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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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 "/" */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine the tag name.
|
|
|
|
*/
|
|
|
|
for(end = buf + size; buf < end; buf++, need_tag++) {
|
|
|
|
int b = *buf, 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 XCT_UNEXPECTED;
|
|
|
|
}
|
|
|
|
if(b == 0)
|
|
|
|
return XCT_BROKEN; /* Embedded 0 in buf?! */
|
|
|
|
}
|
|
|
|
if(*need_tag) return XCT_UNEXPECTED;
|
|
|
|
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#undef ADVANCE
|
|
|
|
#define ADVANCE(num_bytes) do { \
|
|
|
|
size_t num = (num_bytes); \
|
|
|
|
buf_ptr = ((char *)buf_ptr) + num; \
|
|
|
|
size -= num; \
|
|
|
|
consumed_myself += num; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#undef RETURN
|
|
|
|
#define RETURN(_code) do { \
|
|
|
|
rval.code = _code; \
|
|
|
|
rval.consumed = consumed_myself; \
|
|
|
|
return rval; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
#define XER_GOT_BODY(chunk_buf, chunk_size) do { \
|
|
|
|
ssize_t converted_size = body_receiver \
|
2004-10-21 11:22:12 +00:00
|
|
|
(struct_key, chunk_buf, chunk_size, \
|
2004-10-20 15:50:55 +00:00
|
|
|
(size_t)chunk_size < size); \
|
|
|
|
if(converted_size == -1) RETURN(RC_FAIL); \
|
|
|
|
chunk_size = converted_size; \
|
|
|
|
} while(0)
|
|
|
|
#define XER_GOT_EMPTY() do { \
|
|
|
|
ssize_t chunk_size = 0; \
|
|
|
|
XER_GOT_BODY(0, chunk_size); \
|
|
|
|
} 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 */
|
2004-10-21 11:22:12 +00:00
|
|
|
void *struct_key,
|
2004-10-20 15:50:55 +00:00
|
|
|
const char *xml_tag, /* Expected XML tag */
|
|
|
|
void *buf_ptr, size_t size,
|
|
|
|
int (*opt_unexpected_tag_decoder)
|
2004-10-21 11:22:12 +00:00
|
|
|
(void *struct_key, void *chunk_buf, size_t chunk_size),
|
2004-10-20 15:50:55 +00:00
|
|
|
ssize_t (*body_receiver)
|
2004-10-21 11:22:12 +00:00
|
|
|
(void *struct_key, void *chunk_buf, size_t chunk_size,
|
2004-10-20 15:50:55 +00:00
|
|
|
int have_more)
|
|
|
|
) {
|
|
|
|
|
|
|
|
asn_dec_rval_t rval;
|
|
|
|
ssize_t consumed_myself = 0;
|
|
|
|
pxer_chunk_type_e ch_type; /* XER chunk type */
|
|
|
|
int xer_state; /* XER low level parsing context */
|
|
|
|
|
|
|
|
(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(xer_state = ctx->step;;) {
|
|
|
|
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(&xer_state, buf_ptr, size, &ch_type);
|
|
|
|
switch(ch_size) {
|
|
|
|
case -1: RETURN(RC_FAIL);
|
|
|
|
case 0:
|
|
|
|
ctx->step = xer_state;
|
|
|
|
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) {
|
2004-10-21 05:44:11 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2004-10-20 15:50:55 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
if(ctx->phase == 0) {
|
|
|
|
/*
|
|
|
|
* Expecting the opening tag
|
|
|
|
* for the type being processed.
|
|
|
|
*/
|
|
|
|
switch(tcv) {
|
|
|
|
case XCT_BOTH:
|
|
|
|
/* Finished decoding of an empty element */
|
|
|
|
XER_GOT_EMPTY();
|
|
|
|
ADVANCE(ch_size);
|
|
|
|
ctx->phase = 2; /* Phase out */
|
|
|
|
RETURN(RC_OK);
|
|
|
|
case XCT_OPENING:
|
|
|
|
ADVANCE(ch_size);
|
|
|
|
ctx->phase = 1; /* Processing body phase */
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break; /* Unexpected tag */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Waiting for the closing XML tag.
|
|
|
|
*/
|
|
|
|
switch(tcv) {
|
|
|
|
case XCT_CLOSING:
|
|
|
|
ADVANCE(ch_size);
|
|
|
|
ctx->phase = 2; /* Phase out */
|
|
|
|
RETURN(RC_OK);
|
|
|
|
case XCT_UNEXPECTED:
|
|
|
|
/*
|
|
|
|
* Certain tags in the body may be expected.
|
|
|
|
*/
|
|
|
|
if(opt_unexpected_tag_decoder
|
2004-10-21 11:22:12 +00:00
|
|
|
&& opt_unexpected_tag_decoder(struct_key,
|
2004-10-20 15:50:55 +00:00
|
|
|
buf_ptr, ch_size) == 0) {
|
|
|
|
/* Tag's processed fine */
|
|
|
|
ADVANCE(ch_size);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Fall through */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ASN_DEBUG("Unexpected XML tag");
|
|
|
|
}
|
|
|
|
break; /* Dark and mysterious things have just happened */
|
|
|
|
}
|
|
|
|
|
|
|
|
RETURN(RC_FAIL);
|
|
|
|
}
|
|
|
|
|