blob: 9cbccf816c17f82f6257139d6efe7caf84c8eaca [file] [log] [blame]
Lev Walkindc06f6b2004-10-20 15:50:55 +00001
2#include <asn_application.h>
3#include <asn_internal.h>
4#include <xer_support.h> /* XER/XML parsing support */
5#include <assert.h>
6
7
8/*
9 * Decode the XER encoding of a given type.
10 */
11asn_dec_rval_t
12xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
13 void **struct_ptr, void *buffer, size_t size) {
14 asn_codec_ctx_t s_codec_ctx;
15
16 /*
17 * Satisfy the requirement that the codec context
18 * must be allocated on the stack.
19 */
20 if(opt_codec_ctx && opt_codec_ctx->max_stack_size) {
21 s_codec_ctx = *opt_codec_ctx;
22 opt_codec_ctx = &s_codec_ctx;
23 }
24
25 /*
26 * Invoke type-specific decoder.
27 */
28 return td->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
29}
30
31
32
33struct xer__cb_arg {
34 pxml_chunk_type_e chunk_type;
35 size_t chunk_size;
36 void *chunk_buf;
37 int callback_not_invoked;
38};
39
40static int
41xer__token_cb(pxml_chunk_type_e type, void *_chunk_data, size_t _chunk_size, void *key) {
42 struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
43 arg->chunk_type = type;
44 arg->chunk_size = _chunk_size;
45 arg->chunk_buf = _chunk_data;
46 arg->callback_not_invoked = 0;
47 return -1; /* Terminate the XML parsing */
48}
49
50/*
51 * Fetch the next token from the XER/XML stream.
52 */
53ssize_t
54xer_next_token(int *stateContext, void *buffer, size_t size,
55 pxer_chunk_type_e *ch_type) {
56 struct xer__cb_arg arg;
57 ssize_t ret;
58
59 arg.callback_not_invoked = 1;
60 ret = pxml_parse(stateContext, buffer, size, xer__token_cb, &arg);
61 if(ret < 0) return -1;
62 if(arg.callback_not_invoked) {
63 assert(ret == 0); /* No data was consumed */
64 return 0; /* Try again with more data */
65 } else {
66 assert(arg.chunk_size);
67 assert(arg.chunk_buf == buffer);
68 }
69
70 /*
71 * Translate the XML chunk types into more convenient ones.
72 */
73 switch(arg.chunk_type) {
74 case PXML_TEXT:
75 *ch_type = PXER_TEXT;
76 break;
77 case PXML_TAG: return 0; /* Want more */
78 case PXML_TAG_END:
79 *ch_type = PXER_TAG;
80 break;
81 case PXML_COMMENT:
82 case PXML_COMMENT_END:
83 *ch_type = PXER_COMMENT;
84 break;
85 }
86
87 return arg.chunk_size;
88}
89
90#define CSLASH 0x2f /* '/' */
91#define LANGLE 0x3c /* '<' */
92#define RANGLE 0x3e /* '>' */
93
94xer_check_tag_e
95xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
96 const char *buf = (const char *)buf_ptr;
97 const char *end;
98 xer_check_tag_e ct = XCT_OPENING;
99
100 if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
101 return XCT_BROKEN;
102 }
103
104 /*
105 * Determine the tag class.
106 */
107 if(buf[1] == CSLASH) {
108 buf += 2; /* advance past "</" */
109 size -= 3; /* strip "</" and ">" */
110 ct = XCT_CLOSING;
111 if(size > 0 && buf[size-1] == CSLASH)
112 return XCT_BROKEN; /* </abc/> */
113 } else {
114 buf++; /* advance past "<" */
115 size -= 2; /* strip "<" and ">" */
116 if(size > 0 && buf[size-1] == CSLASH) {
117 ct = XCT_BOTH;
118 size--; /* One more, for "/" */
119 }
120 }
121
122 /*
123 * Determine the tag name.
124 */
125 for(end = buf + size; buf < end; buf++, need_tag++) {
126 int b = *buf, n = *need_tag;
127 if(b != n) {
128 if(n == 0) {
129 switch(b) {
130 case 0x09: case 0x0a: case 0x0c: case 0x0d:
131 case 0x20:
132 /* "<abc def/>": whitespace is normal */
133 return ct;
134 }
135 }
136 return XCT_UNEXPECTED;
137 }
138 if(b == 0)
139 return XCT_BROKEN; /* Embedded 0 in buf?! */
140 }
141 if(*need_tag) return XCT_UNEXPECTED;
142
143 return ct;
144}
145
146
147#undef ADVANCE
148#define ADVANCE(num_bytes) do { \
149 size_t num = (num_bytes); \
150 buf_ptr = ((char *)buf_ptr) + num; \
151 size -= num; \
152 consumed_myself += num; \
153 } while(0)
154
155#undef RETURN
156#define RETURN(_code) do { \
157 rval.code = _code; \
158 rval.consumed = consumed_myself; \
159 return rval; \
160 } while(0)
161
162#define XER_GOT_BODY(chunk_buf, chunk_size) do { \
163 ssize_t converted_size = body_receiver \
Lev Walkin3256d6f2004-10-21 11:22:12 +0000164 (struct_key, chunk_buf, chunk_size, \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000165 (size_t)chunk_size < size); \
166 if(converted_size == -1) RETURN(RC_FAIL); \
167 chunk_size = converted_size; \
168 } while(0)
169#define XER_GOT_EMPTY() do { \
170 ssize_t chunk_size = 0; \
171 XER_GOT_BODY(0, chunk_size); \
172 } while(0)
173
174/*
175 * Generalized function for decoding the primitive values.
176 */
177asn_dec_rval_t
178xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
179 asn_struct_ctx_t *ctx, /* Type decoder context */
Lev Walkin3256d6f2004-10-21 11:22:12 +0000180 void *struct_key,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000181 const char *xml_tag, /* Expected XML tag */
182 void *buf_ptr, size_t size,
183 int (*opt_unexpected_tag_decoder)
Lev Walkin3256d6f2004-10-21 11:22:12 +0000184 (void *struct_key, void *chunk_buf, size_t chunk_size),
Lev Walkindc06f6b2004-10-20 15:50:55 +0000185 ssize_t (*body_receiver)
Lev Walkin3256d6f2004-10-21 11:22:12 +0000186 (void *struct_key, void *chunk_buf, size_t chunk_size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000187 int have_more)
188 ) {
189
190 asn_dec_rval_t rval;
191 ssize_t consumed_myself = 0;
192 pxer_chunk_type_e ch_type; /* XER chunk type */
193 int xer_state; /* XER low level parsing context */
194
195 (void)opt_codec_ctx;
196
197 /*
198 * Phases of XER/XML processing:
199 * Phase 0: Check that the opening tag matches our expectations.
200 * Phase 1: Processing body and reacting on closing tag.
201 */
202 if(ctx->phase > 1) RETURN(RC_FAIL);
203 for(xer_state = ctx->step;;) {
204 ssize_t ch_size; /* Chunk size */
205 xer_check_tag_e tcv; /* Tag check value */
206
207 /*
208 * Get the next part of the XML stream.
209 */
210 ch_size = xer_next_token(&xer_state, buf_ptr, size, &ch_type);
211 switch(ch_size) {
212 case -1: RETURN(RC_FAIL);
213 case 0:
214 ctx->step = xer_state;
215 RETURN(RC_WMORE);
216 default:
217 switch(ch_type) {
218 case PXER_COMMENT: /* Got XML comment */
219 ADVANCE(ch_size); /* Skip silently */
220 continue;
221 case PXER_TEXT:
222 if(ctx->phase == 0) {
Lev Walkincfeecfb2004-10-21 05:44:11 +0000223 /*
224 * We have to ignore whitespace here,
225 * but in order to be forward compatible
226 * with EXTENDED-XER (EMBED-VALUES, #25)
227 * any text is just ignored here.
228 */
229 } else {
230 XER_GOT_BODY(buf_ptr, ch_size);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000231 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000232 ADVANCE(ch_size);
233 continue;
234 case PXER_TAG:
235 break; /* Check the rest down there */
236 }
237 }
238
239 assert(ch_type == PXER_TAG && size);
240
241 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
242 if(ctx->phase == 0) {
243 /*
244 * Expecting the opening tag
245 * for the type being processed.
246 */
247 switch(tcv) {
248 case XCT_BOTH:
249 /* Finished decoding of an empty element */
250 XER_GOT_EMPTY();
251 ADVANCE(ch_size);
252 ctx->phase = 2; /* Phase out */
253 RETURN(RC_OK);
254 case XCT_OPENING:
255 ADVANCE(ch_size);
256 ctx->phase = 1; /* Processing body phase */
257 continue;
258 default:
259 break; /* Unexpected tag */
260 }
261 } else {
262 /*
263 * Waiting for the closing XML tag.
264 */
265 switch(tcv) {
266 case XCT_CLOSING:
267 ADVANCE(ch_size);
268 ctx->phase = 2; /* Phase out */
269 RETURN(RC_OK);
270 case XCT_UNEXPECTED:
271 /*
272 * Certain tags in the body may be expected.
273 */
274 if(opt_unexpected_tag_decoder
Lev Walkin3256d6f2004-10-21 11:22:12 +0000275 && opt_unexpected_tag_decoder(struct_key,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000276 buf_ptr, ch_size) == 0) {
277 /* Tag's processed fine */
278 ADVANCE(ch_size);
279 continue;
280 }
281 /* Fall through */
282 default:
283 break;
284 }
285 ASN_DEBUG("Unexpected XML tag");
286 }
287 break; /* Dark and mysterious things have just happened */
288 }
289
290 RETURN(RC_FAIL);
291}
292