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