2 * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
\r
3 * Redistribution and modifications are permitted subject to BSD license.
\r
5 #include <asn_application.h>
\r
6 #include <asn_internal.h>
\r
7 #include <xer_support.h> /* XER/XML parsing support */
\r
11 * Decode the XER encoding of a given type.
\r
14 xer_decode(const asn_codec_ctx_t *opt_codec_ctx,
\r
15 const asn_TYPE_descriptor_t *td, void **struct_ptr,
\r
16 const void *buffer, size_t size) {
\r
17 asn_codec_ctx_t s_codec_ctx;
\r
20 * Stack checker requires that the codec context
\r
21 * must be allocated on the stack.
\r
24 if(opt_codec_ctx->max_stack_size) {
\r
25 s_codec_ctx = *opt_codec_ctx;
\r
26 opt_codec_ctx = &s_codec_ctx;
\r
29 /* If context is not given, be security-conscious anyway */
\r
30 memset(&s_codec_ctx, 0, sizeof(s_codec_ctx));
\r
31 s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX;
\r
32 opt_codec_ctx = &s_codec_ctx;
\r
36 * Invoke type-specific decoder.
\r
38 return td->op->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
\r
43 struct xer__cb_arg {
\r
44 pxml_chunk_type_e chunk_type;
\r
46 const void *chunk_buf;
\r
47 int callback_not_invoked;
\r
51 xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
\r
52 struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
\r
53 arg->chunk_type = type;
\r
54 arg->chunk_size = _chunk_size;
\r
55 arg->chunk_buf = _chunk_data;
\r
56 arg->callback_not_invoked = 0;
\r
57 return -1; /* Terminate the XML parsing */
\r
61 * Fetch the next token from the XER/XML stream.
\r
64 xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
\r
65 struct xer__cb_arg arg;
\r
66 int new_stateContext = *stateContext;
\r
69 arg.callback_not_invoked = 1;
\r
70 ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
\r
71 if(ret < 0) return -1;
\r
72 if(arg.callback_not_invoked) {
\r
73 assert(ret == 0); /* No data was consumed */
\r
74 *ch_type = PXER_WMORE;
\r
75 return 0; /* Try again with more data */
\r
77 assert(arg.chunk_size);
\r
78 assert(arg.chunk_buf == buffer);
\r
82 * Translate the XML chunk types into more convenient ones.
\r
84 switch(arg.chunk_type) {
\r
86 *ch_type = PXER_TEXT;
\r
89 *ch_type = PXER_WMORE;
\r
90 return 0; /* Want more */
\r
92 *ch_type = PXER_TAG;
\r
95 case PXML_COMMENT_END:
\r
96 *ch_type = PXER_COMMENT;
\r
100 *stateContext = new_stateContext;
\r
101 return arg.chunk_size;
\r
104 #define CSLASH 0x2f /* '/' */
\r
105 #define LANGLE 0x3c /* '<' */
\r
106 #define RANGLE 0x3e /* '>' */
\r
109 xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
\r
110 const char *buf = (const char *)buf_ptr;
\r
112 xer_check_tag_e ct = XCT_OPENING;
\r
114 if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
\r
116 ASN_DEBUG("Broken XML tag: \"%c...%c\"",
\r
117 buf[0], buf[size - 1]);
\r
122 * Determine the tag class.
\r
124 if(buf[1] == CSLASH) {
\r
125 buf += 2; /* advance past "</" */
\r
126 size -= 3; /* strip "</" and ">" */
\r
128 if(size > 0 && buf[size-1] == CSLASH)
\r
129 return XCT_BROKEN; /* </abc/> */
\r
131 buf++; /* advance past "<" */
\r
132 size -= 2; /* strip "<" and ">" */
\r
133 if(size > 0 && buf[size-1] == CSLASH) {
\r
135 size--; /* One more, for "/" */
\r
139 /* Sometimes we don't care about the tag */
\r
140 if(!need_tag || !*need_tag)
\r
141 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
\r
144 * Determine the tag name.
\r
146 for(end = buf + size; buf < end; buf++, need_tag++) {
\r
147 int b = *buf, n = *need_tag;
\r
151 case 0x09: case 0x0a: case 0x0c: case 0x0d:
\r
153 /* "<abc def/>": whitespace is normal */
\r
157 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
\r
160 return XCT_BROKEN; /* Embedded 0 in buf?! */
\r
163 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
\r
170 #define ADVANCE(num_bytes) do { \
\r
171 size_t num = (num_bytes); \
\r
172 buf_ptr = ((const char *)buf_ptr) + num; \
\r
174 consumed_myself += num; \
\r
178 #define RETURN(_code) do { \
\r
179 rval.code = _code; \
\r
180 rval.consumed = consumed_myself; \
\r
181 if(rval.code != RC_OK) \
\r
182 ASN_DEBUG("Failed with %d", rval.code); \
\r
186 #define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \
\r
187 ssize_t converted_size = body_receiver \
\r
188 (struct_key, chunk_buf, chunk_size, \
\r
189 (size_t)chunk_size < size); \
\r
190 if(converted_size == -1) RETURN(RC_FAIL); \
\r
191 if(converted_size == 0 \
\r
192 && size == (size_t)chunk_size) \
\r
193 RETURN(RC_WMORE); \
\r
194 chunk_size = converted_size; \
\r
196 #define XER_GOT_EMPTY() do { \
\r
197 if(body_receiver(struct_key, 0, 0, size > 0) == -1) \
\r
202 * Generalized function for decoding the primitive values.
\r
205 xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx,
\r
206 asn_struct_ctx_t *ctx, /* Type decoder context */
\r
208 const char *xml_tag, /* Expected XML tag */
\r
209 const void *buf_ptr, size_t size,
\r
210 int (*opt_unexpected_tag_decoder)
\r
211 (void *struct_key, const void *chunk_buf, size_t chunk_size),
\r
212 ssize_t (*body_receiver)
\r
213 (void *struct_key, const void *chunk_buf, size_t chunk_size,
\r
217 asn_dec_rval_t rval;
\r
218 ssize_t consumed_myself = 0;
\r
220 (void)opt_codec_ctx;
\r
223 * Phases of XER/XML processing:
\r
224 * Phase 0: Check that the opening tag matches our expectations.
\r
225 * Phase 1: Processing body and reacting on closing tag.
\r
227 if(ctx->phase > 1) RETURN(RC_FAIL);
\r
229 pxer_chunk_type_e ch_type; /* XER chunk type */
\r
230 ssize_t ch_size; /* Chunk size */
\r
231 xer_check_tag_e tcv; /* Tag check value */
\r
234 * Get the next part of the XML stream.
\r
236 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
\r
238 if(ch_size == -1) {
\r
244 case PXER_COMMENT: /* Got XML comment */
\r
245 ADVANCE(ch_size); /* Skip silently */
\r
248 if(ctx->phase == 0) {
\r
250 * We have to ignore whitespace here,
\r
251 * but in order to be forward compatible
\r
252 * with EXTENDED-XER (EMBED-VALUES, #25)
\r
253 * any text is just ignored here.
\r
256 XER_GOT_BODY(buf_ptr, ch_size, size);
\r
261 break; /* Check the rest down there */
\r
265 assert(ch_type == PXER_TAG && size);
\r
267 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
\r
270 * Expecting the opening tag
\r
271 * for the type being processed.
\r
273 * Waiting for the closing XML tag.
\r
277 if(ctx->phase) break;
\r
278 /* Finished decoding of an empty element */
\r
281 ctx->phase = 2; /* Phase out */
\r
284 if(ctx->phase) break;
\r
286 ctx->phase = 1; /* Processing body phase */
\r
289 if(!ctx->phase) break;
\r
291 ctx->phase = 2; /* Phase out */
\r
293 case XCT_UNKNOWN_BO:
\r
295 * Certain tags in the body may be expected.
\r
297 if(opt_unexpected_tag_decoder
\r
298 && opt_unexpected_tag_decoder(struct_key,
\r
299 buf_ptr, ch_size) >= 0) {
\r
300 /* Tag's processed fine */
\r
303 /* We are not expecting
\r
304 * the closing tag anymore. */
\r
305 ctx->phase = 2; /* Phase out */
\r
312 break; /* Unexpected tag */
\r
315 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
\r
316 break; /* Dark and mysterious things have just happened */
\r
324 xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
\r
325 const char *p = (const char *)chunk_buf;
\r
326 const char *pend = p + chunk_size;
\r
328 for(; p < pend; p++) {
\r
331 * HORISONTAL TAB (9)
\r
333 * CARRIAGE RETURN (13)
\r
336 case 0x09: case 0x0a: case 0x0d: case 0x20:
\r
343 return (p - (const char *)chunk_buf);
\r
347 * This is a vastly simplified, non-validating XML tree skipper.
\r
350 xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
\r
351 assert(*depth > 0);
\r
354 case XCT_UNKNOWN_BO:
\r
355 /* These negate each other. */
\r
358 case XCT_UNKNOWN_OP:
\r
362 case XCT_UNKNOWN_CL:
\r
363 if(--(*depth) == 0)
\r
364 return (tcv == XCT_CLOSING) ? 2 : 1;
\r