#include #include #include /* XER/XML parsing support */ #include /* * 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 "" */ ct = XCT_CLOSING; if(size > 0 && buf[size-1] == CSLASH) return XCT_BROKEN; /* */ } 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: /* "": 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 \ (struct_ptr, chunk_buf, chunk_size, \ (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 */ void *struct_ptr, /* The structure must be already allocated */ const char *xml_tag, /* Expected XML tag */ void *buf_ptr, size_t size, int (*opt_unexpected_tag_decoder) (void *struct_ptr, void *chunk_buf, size_t chunk_size), ssize_t (*body_receiver) (void *struct_ptr, void *chunk_buf, size_t chunk_size, 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) { /* * 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); } 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 && opt_unexpected_tag_decoder(struct_ptr, 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); }