blob: b112a72d1afa090af94d1c217850c1fa90c49631 [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 /*
19 * Satisfy the requirement that the codec context
20 * must be allocated on the stack.
21 */
22 if(opt_codec_ctx && opt_codec_ctx->max_stack_size) {
23 s_codec_ctx = *opt_codec_ctx;
24 opt_codec_ctx = &s_codec_ctx;
25 }
26
27 /*
28 * Invoke type-specific decoder.
29 */
30 return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
31}
32
33
34
35struct xer__cb_arg {
36 pxml_chunk_type_e chunk_type;
37 size_t chunk_size;
Lev Walkin0fab1a62005-03-09 22:19:25 +000038 const void *chunk_buf;
Lev Walkindc06f6b2004-10-20 15:50:55 +000039 int callback_not_invoked;
40};
41
42static int
Lev Walkin0fab1a62005-03-09 22:19:25 +000043xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000044 struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
45 arg->chunk_type = type;
46 arg->chunk_size = _chunk_size;
47 arg->chunk_buf = _chunk_data;
48 arg->callback_not_invoked = 0;
49 return -1; /* Terminate the XML parsing */
50}
51
52/*
53 * Fetch the next token from the XER/XML stream.
54 */
55ssize_t
Lev Walkin0fab1a62005-03-09 22:19:25 +000056xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000057 struct xer__cb_arg arg;
Lev Walkin1e443962005-02-18 18:06:36 +000058 int new_stateContext = *stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +000059 ssize_t ret;
60
61 arg.callback_not_invoked = 1;
Lev Walkin1e443962005-02-18 18:06:36 +000062 ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
Lev Walkindc06f6b2004-10-20 15:50:55 +000063 if(ret < 0) return -1;
64 if(arg.callback_not_invoked) {
65 assert(ret == 0); /* No data was consumed */
66 return 0; /* Try again with more data */
67 } else {
68 assert(arg.chunk_size);
69 assert(arg.chunk_buf == buffer);
70 }
71
72 /*
73 * Translate the XML chunk types into more convenient ones.
74 */
75 switch(arg.chunk_type) {
76 case PXML_TEXT:
77 *ch_type = PXER_TEXT;
78 break;
79 case PXML_TAG: return 0; /* Want more */
80 case PXML_TAG_END:
81 *ch_type = PXER_TAG;
82 break;
83 case PXML_COMMENT:
84 case PXML_COMMENT_END:
85 *ch_type = PXER_COMMENT;
86 break;
87 }
88
Lev Walkin1e443962005-02-18 18:06:36 +000089 *stateContext = new_stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +000090 return arg.chunk_size;
91}
92
93#define CSLASH 0x2f /* '/' */
94#define LANGLE 0x3c /* '<' */
95#define RANGLE 0x3e /* '>' */
96
97xer_check_tag_e
98xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
99 const char *buf = (const char *)buf_ptr;
100 const char *end;
101 xer_check_tag_e ct = XCT_OPENING;
102
103 if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
Lev Walkinc61f3862005-02-14 17:21:22 +0000104 if(size >= 2)
105 ASN_DEBUG("Broken XML tag: \"%c...%c\"", buf[0], buf[size - 1]);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000106 return XCT_BROKEN;
107 }
108
109 /*
110 * Determine the tag class.
111 */
112 if(buf[1] == CSLASH) {
113 buf += 2; /* advance past "</" */
114 size -= 3; /* strip "</" and ">" */
115 ct = XCT_CLOSING;
116 if(size > 0 && buf[size-1] == CSLASH)
117 return XCT_BROKEN; /* </abc/> */
118 } else {
119 buf++; /* advance past "<" */
120 size -= 2; /* strip "<" and ">" */
121 if(size > 0 && buf[size-1] == CSLASH) {
122 ct = XCT_BOTH;
123 size--; /* One more, for "/" */
124 }
125 }
126
127 /*
128 * Determine the tag name.
129 */
130 for(end = buf + size; buf < end; buf++, need_tag++) {
131 int b = *buf, n = *need_tag;
132 if(b != n) {
133 if(n == 0) {
134 switch(b) {
135 case 0x09: case 0x0a: case 0x0c: case 0x0d:
136 case 0x20:
137 /* "<abc def/>": whitespace is normal */
138 return ct;
139 }
140 }
Lev Walkin1e443962005-02-18 18:06:36 +0000141 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000142 }
143 if(b == 0)
144 return XCT_BROKEN; /* Embedded 0 in buf?! */
145 }
Lev Walkin904e65b2005-02-18 14:23:48 +0000146 if(*need_tag)
Lev Walkin1e443962005-02-18 18:06:36 +0000147 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000148
149 return ct;
150}
151
152
153#undef ADVANCE
154#define ADVANCE(num_bytes) do { \
155 size_t num = (num_bytes); \
Lev Walkin0fab1a62005-03-09 22:19:25 +0000156 buf_ptr = ((const char *)buf_ptr) + num; \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000157 size -= num; \
158 consumed_myself += num; \
159 } while(0)
160
161#undef RETURN
162#define RETURN(_code) do { \
163 rval.code = _code; \
164 rval.consumed = consumed_myself; \
165 return rval; \
166 } while(0)
167
Lev Walkin9357ec12005-02-18 12:26:20 +0000168#define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000169 ssize_t converted_size = body_receiver \
Lev Walkin3256d6f2004-10-21 11:22:12 +0000170 (struct_key, chunk_buf, chunk_size, \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000171 (size_t)chunk_size < size); \
172 if(converted_size == -1) RETURN(RC_FAIL); \
Lev Walkin1e443962005-02-18 18:06:36 +0000173 if(converted_size == 0 \
174 && size == (size_t)chunk_size) \
Lev Walkin9357ec12005-02-18 12:26:20 +0000175 RETURN(RC_WMORE); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000176 chunk_size = converted_size; \
177 } while(0)
178#define XER_GOT_EMPTY() do { \
Lev Walkin9357ec12005-02-18 12:26:20 +0000179 if(body_receiver(struct_key, 0, 0, size > 0) == -1) \
180 RETURN(RC_FAIL); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000181 } while(0)
182
183/*
184 * Generalized function for decoding the primitive values.
185 */
186asn_dec_rval_t
187xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
188 asn_struct_ctx_t *ctx, /* Type decoder context */
Lev Walkin3256d6f2004-10-21 11:22:12 +0000189 void *struct_key,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000190 const char *xml_tag, /* Expected XML tag */
Lev Walkin0fab1a62005-03-09 22:19:25 +0000191 const void *buf_ptr, size_t size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000192 int (*opt_unexpected_tag_decoder)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000193 (void *struct_key, const void *chunk_buf, size_t chunk_size),
Lev Walkindc06f6b2004-10-20 15:50:55 +0000194 ssize_t (*body_receiver)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000195 (void *struct_key, const void *chunk_buf, size_t chunk_size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000196 int have_more)
197 ) {
198
199 asn_dec_rval_t rval;
200 ssize_t consumed_myself = 0;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000201
202 (void)opt_codec_ctx;
203
204 /*
205 * Phases of XER/XML processing:
206 * Phase 0: Check that the opening tag matches our expectations.
207 * Phase 1: Processing body and reacting on closing tag.
208 */
209 if(ctx->phase > 1) RETURN(RC_FAIL);
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000210 for(;;) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000211 pxer_chunk_type_e ch_type; /* XER chunk type */
212 ssize_t ch_size; /* Chunk size */
213 xer_check_tag_e tcv; /* Tag check value */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000214
215 /*
216 * Get the next part of the XML stream.
217 */
Lev Walkin1e443962005-02-18 18:06:36 +0000218 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
219 &ch_type);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000220 switch(ch_size) {
221 case -1: RETURN(RC_FAIL);
222 case 0:
Lev Walkindc06f6b2004-10-20 15:50:55 +0000223 RETURN(RC_WMORE);
224 default:
225 switch(ch_type) {
226 case PXER_COMMENT: /* Got XML comment */
227 ADVANCE(ch_size); /* Skip silently */
228 continue;
229 case PXER_TEXT:
230 if(ctx->phase == 0) {
Lev Walkincfeecfb2004-10-21 05:44:11 +0000231 /*
232 * We have to ignore whitespace here,
233 * but in order to be forward compatible
234 * with EXTENDED-XER (EMBED-VALUES, #25)
235 * any text is just ignored here.
236 */
237 } else {
Lev Walkin9357ec12005-02-18 12:26:20 +0000238 XER_GOT_BODY(buf_ptr, ch_size, size);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000239 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000240 ADVANCE(ch_size);
241 continue;
242 case PXER_TAG:
243 break; /* Check the rest down there */
244 }
245 }
246
247 assert(ch_type == PXER_TAG && size);
248
249 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
Lev Walkind2de48a2004-10-23 13:27:03 +0000250 /*
251 * Phase 0:
252 * Expecting the opening tag
253 * for the type being processed.
254 * Phase 1:
255 * Waiting for the closing XML tag.
256 */
257 switch(tcv) {
258 case XCT_BOTH:
259 if(ctx->phase) break;
260 /* Finished decoding of an empty element */
261 XER_GOT_EMPTY();
262 ADVANCE(ch_size);
263 ctx->phase = 2; /* Phase out */
264 RETURN(RC_OK);
265 case XCT_OPENING:
266 if(ctx->phase) break;
267 ADVANCE(ch_size);
268 ctx->phase = 1; /* Processing body phase */
269 continue;
270 case XCT_CLOSING:
271 if(!ctx->phase) break;
272 ADVANCE(ch_size);
273 ctx->phase = 2; /* Phase out */
274 RETURN(RC_OK);
Lev Walkin904e65b2005-02-18 14:23:48 +0000275 case XCT_UNKNOWN_BO:
Lev Walkindc06f6b2004-10-20 15:50:55 +0000276 /*
Lev Walkind2de48a2004-10-23 13:27:03 +0000277 * Certain tags in the body may be expected.
Lev Walkindc06f6b2004-10-20 15:50:55 +0000278 */
Lev Walkind2de48a2004-10-23 13:27:03 +0000279 if(opt_unexpected_tag_decoder
280 && opt_unexpected_tag_decoder(struct_key,
Lev Walkin4efbfb72005-02-25 14:20:30 +0000281 buf_ptr, ch_size) >= 0) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000282 /* Tag's processed fine */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000283 ADVANCE(ch_size);
Lev Walkine0b56e02005-02-25 12:10:27 +0000284 if(!ctx->phase) {
285 /* We are not expecting
286 * the closing tag anymore. */
287 ctx->phase = 2; /* Phase out */
288 RETURN(RC_OK);
289 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000290 continue;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000291 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000292 /* Fall through */
293 default:
294 break; /* Unexpected tag */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000295 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000296
Lev Walkin642962a2005-02-24 22:37:07 +0000297 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000298 break; /* Dark and mysterious things have just happened */
299 }
300
301 RETURN(RC_FAIL);
302}
303
Lev Walkindde25b32004-10-22 08:17:16 +0000304
305int
Lev Walkin0fab1a62005-03-09 22:19:25 +0000306xer_is_whitespace(const void *chunk_buf, size_t chunk_size) {
307 const char *p = (const char *)chunk_buf;
308 const char *pend = p + chunk_size;
Lev Walkindde25b32004-10-22 08:17:16 +0000309
310 for(; p < pend; p++) {
311 switch(*p) {
312 /* X.693, #8.1.4
313 * HORISONTAL TAB (9)
314 * LINE FEED (10)
315 * CARRIAGE RETURN (13)
316 * SPACE (32)
317 */
318 case 0x09: case 0x0a: case 0x0d: case 0x20:
319 break;
320 default:
321 return 0;
322 }
323 }
324 return 1; /* All whitespace */
325}
326
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000327/*
328 * This is a vastly simplified, non-validating XML tree skipper.
329 */
330int
331xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
332 assert(*depth > 0);
333 switch(tcv) {
334 case XCT_BOTH:
335 case XCT_UNKNOWN_BO:
336 /* These negate each other. */
337 return 0;
338 case XCT_OPENING:
339 case XCT_UNKNOWN_OP:
340 ++(*depth);
341 return 0;
342 case XCT_CLOSING:
343 case XCT_UNKNOWN_CL:
344 if(--(*depth) == 0)
345 return (tcv == XCT_CLOSING) ? 2 : 1;
346 return 0;
347 default:
348 return -1;
349 }
350}