blob: 2b3f16a6e786c64a4a3889052ee159f53f0f7a7e [file] [log] [blame]
Lev Walkinfc776432005-03-29 17:21:14 +00001/*
2 * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Redistribution and modifications are permitted subject to BSD license.
4 */
Lev Walkindc06f6b2004-10-20 15:50:55 +00005#include <asn_application.h>
6#include <asn_internal.h>
7#include <xer_support.h> /* XER/XML parsing support */
Lev Walkindc06f6b2004-10-20 15:50:55 +00008
9
10/*
11 * Decode the XER encoding of a given type.
12 */
13asn_dec_rval_t
14xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
Lev Walkin8c3b8542005-03-10 18:52:02 +000015 void **struct_ptr, const void *buffer, size_t size) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000016 asn_codec_ctx_t s_codec_ctx;
17
18 /*
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000019 * Stack checker requires that the codec context
Lev Walkindc06f6b2004-10-20 15:50:55 +000020 * must be allocated on the stack.
21 */
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000022 if(opt_codec_ctx) {
23 if(opt_codec_ctx->max_stack_size) {
24 s_codec_ctx = *opt_codec_ctx;
25 opt_codec_ctx = &s_codec_ctx;
26 }
27 } else {
28 /* If context is not given, be security-conscious anyway */
29 memset(&s_codec_ctx, 0, sizeof(s_codec_ctx));
30 s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX;
Lev Walkindc06f6b2004-10-20 15:50:55 +000031 opt_codec_ctx = &s_codec_ctx;
32 }
33
34 /*
35 * Invoke type-specific decoder.
36 */
37 return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
38}
39
40
41
42struct xer__cb_arg {
43 pxml_chunk_type_e chunk_type;
44 size_t chunk_size;
Lev Walkin0fab1a62005-03-09 22:19:25 +000045 const void *chunk_buf;
Lev Walkindc06f6b2004-10-20 15:50:55 +000046 int callback_not_invoked;
47};
48
49static int
Lev Walkin0fab1a62005-03-09 22:19:25 +000050xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000051 struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
52 arg->chunk_type = type;
53 arg->chunk_size = _chunk_size;
54 arg->chunk_buf = _chunk_data;
55 arg->callback_not_invoked = 0;
56 return -1; /* Terminate the XML parsing */
57}
58
59/*
60 * Fetch the next token from the XER/XML stream.
61 */
62ssize_t
Lev Walkin0fab1a62005-03-09 22:19:25 +000063xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000064 struct xer__cb_arg arg;
Lev Walkin1e443962005-02-18 18:06:36 +000065 int new_stateContext = *stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +000066 ssize_t ret;
67
68 arg.callback_not_invoked = 1;
Lev Walkin1e443962005-02-18 18:06:36 +000069 ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
Lev Walkindc06f6b2004-10-20 15:50:55 +000070 if(ret < 0) return -1;
71 if(arg.callback_not_invoked) {
72 assert(ret == 0); /* No data was consumed */
73 return 0; /* Try again with more data */
74 } else {
75 assert(arg.chunk_size);
76 assert(arg.chunk_buf == buffer);
77 }
78
79 /*
80 * Translate the XML chunk types into more convenient ones.
81 */
82 switch(arg.chunk_type) {
83 case PXML_TEXT:
84 *ch_type = PXER_TEXT;
85 break;
86 case PXML_TAG: return 0; /* Want more */
87 case PXML_TAG_END:
88 *ch_type = PXER_TAG;
89 break;
90 case PXML_COMMENT:
91 case PXML_COMMENT_END:
92 *ch_type = PXER_COMMENT;
93 break;
94 }
95
Lev Walkin1e443962005-02-18 18:06:36 +000096 *stateContext = new_stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +000097 return arg.chunk_size;
98}
99
100#define CSLASH 0x2f /* '/' */
101#define LANGLE 0x3c /* '<' */
102#define RANGLE 0x3e /* '>' */
103
104xer_check_tag_e
105xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
106 const char *buf = (const char *)buf_ptr;
107 const char *end;
108 xer_check_tag_e ct = XCT_OPENING;
109
110 if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
Lev Walkinc61f3862005-02-14 17:21:22 +0000111 if(size >= 2)
112 ASN_DEBUG("Broken XML tag: \"%c...%c\"", buf[0], buf[size - 1]);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000113 return XCT_BROKEN;
114 }
115
116 /*
117 * Determine the tag class.
118 */
119 if(buf[1] == CSLASH) {
120 buf += 2; /* advance past "</" */
121 size -= 3; /* strip "</" and ">" */
122 ct = XCT_CLOSING;
123 if(size > 0 && buf[size-1] == CSLASH)
124 return XCT_BROKEN; /* </abc/> */
125 } else {
126 buf++; /* advance past "<" */
127 size -= 2; /* strip "<" and ">" */
128 if(size > 0 && buf[size-1] == CSLASH) {
129 ct = XCT_BOTH;
130 size--; /* One more, for "/" */
131 }
132 }
133
Lev Walkind1bfea62005-11-08 03:06:16 +0000134 /* Sometimes we don't care about the tag */
135 if(!need_tag || !*need_tag)
136 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
137
Lev Walkindc06f6b2004-10-20 15:50:55 +0000138 /*
139 * Determine the tag name.
140 */
141 for(end = buf + size; buf < end; buf++, need_tag++) {
142 int b = *buf, n = *need_tag;
143 if(b != n) {
144 if(n == 0) {
145 switch(b) {
146 case 0x09: case 0x0a: case 0x0c: case 0x0d:
147 case 0x20:
148 /* "<abc def/>": whitespace is normal */
149 return ct;
150 }
151 }
Lev Walkin1e443962005-02-18 18:06:36 +0000152 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000153 }
154 if(b == 0)
155 return XCT_BROKEN; /* Embedded 0 in buf?! */
156 }
Lev Walkin904e65b2005-02-18 14:23:48 +0000157 if(*need_tag)
Lev Walkin1e443962005-02-18 18:06:36 +0000158 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000159
160 return ct;
161}
162
163
164#undef ADVANCE
165#define ADVANCE(num_bytes) do { \
166 size_t num = (num_bytes); \
Lev Walkin0fab1a62005-03-09 22:19:25 +0000167 buf_ptr = ((const char *)buf_ptr) + num; \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000168 size -= num; \
169 consumed_myself += num; \
170 } while(0)
171
172#undef RETURN
173#define RETURN(_code) do { \
174 rval.code = _code; \
175 rval.consumed = consumed_myself; \
176 return rval; \
177 } while(0)
178
Lev Walkin9357ec12005-02-18 12:26:20 +0000179#define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000180 ssize_t converted_size = body_receiver \
Lev Walkin3256d6f2004-10-21 11:22:12 +0000181 (struct_key, chunk_buf, chunk_size, \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000182 (size_t)chunk_size < size); \
183 if(converted_size == -1) RETURN(RC_FAIL); \
Lev Walkin1e443962005-02-18 18:06:36 +0000184 if(converted_size == 0 \
185 && size == (size_t)chunk_size) \
Lev Walkin9357ec12005-02-18 12:26:20 +0000186 RETURN(RC_WMORE); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000187 chunk_size = converted_size; \
188 } while(0)
189#define XER_GOT_EMPTY() do { \
Lev Walkin9357ec12005-02-18 12:26:20 +0000190 if(body_receiver(struct_key, 0, 0, size > 0) == -1) \
191 RETURN(RC_FAIL); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000192 } while(0)
193
194/*
195 * Generalized function for decoding the primitive values.
196 */
197asn_dec_rval_t
198xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
199 asn_struct_ctx_t *ctx, /* Type decoder context */
Lev Walkin3256d6f2004-10-21 11:22:12 +0000200 void *struct_key,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000201 const char *xml_tag, /* Expected XML tag */
Lev Walkin0fab1a62005-03-09 22:19:25 +0000202 const void *buf_ptr, size_t size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000203 int (*opt_unexpected_tag_decoder)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000204 (void *struct_key, const void *chunk_buf, size_t chunk_size),
Lev Walkindc06f6b2004-10-20 15:50:55 +0000205 ssize_t (*body_receiver)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000206 (void *struct_key, const void *chunk_buf, size_t chunk_size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000207 int have_more)
208 ) {
209
210 asn_dec_rval_t rval;
211 ssize_t consumed_myself = 0;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000212
213 (void)opt_codec_ctx;
214
215 /*
216 * Phases of XER/XML processing:
217 * Phase 0: Check that the opening tag matches our expectations.
218 * Phase 1: Processing body and reacting on closing tag.
219 */
220 if(ctx->phase > 1) RETURN(RC_FAIL);
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000221 for(;;) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000222 pxer_chunk_type_e ch_type; /* XER chunk type */
223 ssize_t ch_size; /* Chunk size */
224 xer_check_tag_e tcv; /* Tag check value */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000225
226 /*
227 * Get the next part of the XML stream.
228 */
Lev Walkin1e443962005-02-18 18:06:36 +0000229 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
230 &ch_type);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000231 switch(ch_size) {
232 case -1: RETURN(RC_FAIL);
233 case 0:
Lev Walkindc06f6b2004-10-20 15:50:55 +0000234 RETURN(RC_WMORE);
235 default:
236 switch(ch_type) {
237 case PXER_COMMENT: /* Got XML comment */
238 ADVANCE(ch_size); /* Skip silently */
239 continue;
240 case PXER_TEXT:
241 if(ctx->phase == 0) {
Lev Walkincfeecfb2004-10-21 05:44:11 +0000242 /*
243 * We have to ignore whitespace here,
244 * but in order to be forward compatible
245 * with EXTENDED-XER (EMBED-VALUES, #25)
246 * any text is just ignored here.
247 */
248 } else {
Lev Walkin9357ec12005-02-18 12:26:20 +0000249 XER_GOT_BODY(buf_ptr, ch_size, size);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000250 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000251 ADVANCE(ch_size);
252 continue;
253 case PXER_TAG:
254 break; /* Check the rest down there */
255 }
256 }
257
258 assert(ch_type == PXER_TAG && size);
259
260 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
Lev Walkind2de48a2004-10-23 13:27:03 +0000261 /*
262 * Phase 0:
263 * Expecting the opening tag
264 * for the type being processed.
265 * Phase 1:
266 * Waiting for the closing XML tag.
267 */
268 switch(tcv) {
269 case XCT_BOTH:
270 if(ctx->phase) break;
271 /* Finished decoding of an empty element */
272 XER_GOT_EMPTY();
273 ADVANCE(ch_size);
274 ctx->phase = 2; /* Phase out */
275 RETURN(RC_OK);
276 case XCT_OPENING:
277 if(ctx->phase) break;
278 ADVANCE(ch_size);
279 ctx->phase = 1; /* Processing body phase */
280 continue;
281 case XCT_CLOSING:
282 if(!ctx->phase) break;
283 ADVANCE(ch_size);
284 ctx->phase = 2; /* Phase out */
285 RETURN(RC_OK);
Lev Walkin904e65b2005-02-18 14:23:48 +0000286 case XCT_UNKNOWN_BO:
Lev Walkindc06f6b2004-10-20 15:50:55 +0000287 /*
Lev Walkind2de48a2004-10-23 13:27:03 +0000288 * Certain tags in the body may be expected.
Lev Walkindc06f6b2004-10-20 15:50:55 +0000289 */
Lev Walkind2de48a2004-10-23 13:27:03 +0000290 if(opt_unexpected_tag_decoder
291 && opt_unexpected_tag_decoder(struct_key,
Lev Walkin4efbfb72005-02-25 14:20:30 +0000292 buf_ptr, ch_size) >= 0) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000293 /* Tag's processed fine */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000294 ADVANCE(ch_size);
Lev Walkine0b56e02005-02-25 12:10:27 +0000295 if(!ctx->phase) {
296 /* We are not expecting
297 * the closing tag anymore. */
298 ctx->phase = 2; /* Phase out */
299 RETURN(RC_OK);
300 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000301 continue;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000302 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000303 /* Fall through */
304 default:
305 break; /* Unexpected tag */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000306 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000307
Lev Walkin642962a2005-02-24 22:37:07 +0000308 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000309 break; /* Dark and mysterious things have just happened */
310 }
311
312 RETURN(RC_FAIL);
313}
314
Lev Walkindde25b32004-10-22 08:17:16 +0000315
316int
Lev Walkin0fab1a62005-03-09 22:19:25 +0000317xer_is_whitespace(const void *chunk_buf, size_t chunk_size) {
318 const char *p = (const char *)chunk_buf;
319 const char *pend = p + chunk_size;
Lev Walkindde25b32004-10-22 08:17:16 +0000320
321 for(; p < pend; p++) {
322 switch(*p) {
323 /* X.693, #8.1.4
324 * HORISONTAL TAB (9)
325 * LINE FEED (10)
326 * CARRIAGE RETURN (13)
327 * SPACE (32)
328 */
329 case 0x09: case 0x0a: case 0x0d: case 0x20:
330 break;
331 default:
332 return 0;
333 }
334 }
335 return 1; /* All whitespace */
336}
337
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000338/*
339 * This is a vastly simplified, non-validating XML tree skipper.
340 */
341int
342xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
343 assert(*depth > 0);
344 switch(tcv) {
345 case XCT_BOTH:
346 case XCT_UNKNOWN_BO:
347 /* These negate each other. */
348 return 0;
349 case XCT_OPENING:
350 case XCT_UNKNOWN_OP:
351 ++(*depth);
352 return 0;
353 case XCT_CLOSING:
354 case XCT_UNKNOWN_CL:
355 if(--(*depth) == 0)
356 return (tcv == XCT_CLOSING) ? 2 : 1;
357 return 0;
358 default:
359 return -1;
360 }
361}