]> git.stg.codes - stg.git/blob - libs/smux/xer_decoder.c
Finalize transition to Boost.UnitTest.
[stg.git] / libs / smux / xer_decoder.c
1 /*
2  * Copyright (c) 2004, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #include <asn_application.h>
6 #include <asn_internal.h>
7 #include <xer_support.h>                /* XER/XML parsing support */
8
9
10 /*
11  * Decode the XER encoding of a given type.
12  */
13 asn_dec_rval_t
14 xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
15                 void **struct_ptr, const void *buffer, size_t size) {
16         asn_codec_ctx_t s_codec_ctx;
17
18         /*
19          * Stack checker requires that the codec context
20          * must be allocated on the stack.
21          */
22         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;
31                 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
42 struct xer__cb_arg {
43         pxml_chunk_type_e       chunk_type;
44         size_t                  chunk_size;
45         const void              *chunk_buf;
46         int callback_not_invoked;
47 };
48
49 static int
50 xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
51         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  */
62 ssize_t
63 xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
64         struct xer__cb_arg arg;
65         int new_stateContext = *stateContext;
66         ssize_t ret;
67
68         arg.callback_not_invoked = 1;
69         ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
70         if(ret < 0) return -1;
71         if(arg.callback_not_invoked) {
72                 assert(ret == 0);       /* No data was consumed */
73         *ch_type = PXER_WMORE;
74                 return 0;               /* Try again with more data */
75         } else {
76                 assert(arg.chunk_size);
77                 assert(arg.chunk_buf == buffer);
78         }
79
80         /*
81          * Translate the XML chunk types into more convenient ones.
82          */
83         switch(arg.chunk_type) {
84         case PXML_TEXT:
85                 *ch_type = PXER_TEXT;
86                 break;
87         case PXML_TAG:
88         *ch_type = PXER_WMORE;
89         return 0;       /* Want more */
90         case PXML_TAG_END:
91                 *ch_type = PXER_TAG;
92                 break;
93         case PXML_COMMENT:
94         case PXML_COMMENT_END:
95                 *ch_type = PXER_COMMENT;
96                 break;
97         }
98
99         *stateContext = new_stateContext;
100         return arg.chunk_size;
101 }
102
103 #define CSLASH  0x2f    /* '/' */
104 #define LANGLE  0x3c    /* '<' */
105 #define RANGLE  0x3e    /* '>' */
106
107 xer_check_tag_e
108 xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
109         const char *buf = (const char *)buf_ptr;
110         const char *end;
111         xer_check_tag_e ct = XCT_OPENING;
112
113         if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
114                 if(size >= 2)
115                         ASN_DEBUG("Broken XML tag: \"%c...%c\"",
116                         buf[0], buf[size - 1]);
117                 return XCT_BROKEN;
118         }
119
120         /*
121          * Determine the tag class.
122          */
123         if(buf[1] == CSLASH) {
124                 buf += 2;       /* advance past "</" */
125                 size -= 3;      /* strip "</" and ">" */
126                 ct = XCT_CLOSING;
127                 if(size > 0 && buf[size-1] == CSLASH)
128                         return XCT_BROKEN;      /* </abc/> */
129         } else {
130                 buf++;          /* advance past "<" */
131                 size -= 2;      /* strip "<" and ">" */
132                 if(size > 0 && buf[size-1] == CSLASH) {
133                         ct = XCT_BOTH;
134                         size--; /* One more, for "/" */
135                 }
136         }
137
138         /* Sometimes we don't care about the tag */
139         if(!need_tag || !*need_tag)
140                 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
141
142         /*
143          * Determine the tag name.
144          */
145         for(end = buf + size; buf < end; buf++, need_tag++) {
146                 int b = *buf, n = *need_tag;
147                 if(b != n) {
148                         if(n == 0) {
149                                 switch(b) {
150                                 case 0x09: case 0x0a: case 0x0c: case 0x0d:
151                                 case 0x20:
152                                         /* "<abc def/>": whitespace is normal */
153                                         return ct;
154                                 }
155                         }
156                         return (xer_check_tag_e)(XCT__UNK__MASK | ct);
157                 }
158                 if(b == 0)
159                         return XCT_BROKEN;      /* Embedded 0 in buf?! */
160         }
161         if(*need_tag)
162                 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
163
164         return ct;
165 }
166
167
168 #undef  ADVANCE
169 #define ADVANCE(num_bytes)      do {                            \
170                 size_t num = (num_bytes);                       \
171                 buf_ptr = ((const char *)buf_ptr) + num;        \
172                 size -= num;                                    \
173                 consumed_myself += num;                         \
174         } while(0)
175
176 #undef  RETURN
177 #define RETURN(_code)   do {                                    \
178                 rval.code = _code;                              \
179                 rval.consumed = consumed_myself;                \
180                 if(rval.code != RC_OK)                          \
181                         ASN_DEBUG("Failed with %d", rval.code); \
182                 return rval;                                    \
183         } while(0)
184
185 #define XER_GOT_BODY(chunk_buf, chunk_size, size)       do {    \
186                 ssize_t converted_size = body_receiver          \
187                         (struct_key, chunk_buf, chunk_size,     \
188                                 (size_t)chunk_size < size);     \
189                 if(converted_size == -1) RETURN(RC_FAIL);       \
190                 if(converted_size == 0                          \
191                         && size == (size_t)chunk_size)          \
192                         RETURN(RC_WMORE);                       \
193                 chunk_size = converted_size;                    \
194         } while(0)
195 #define XER_GOT_EMPTY() do {                                    \
196         if(body_receiver(struct_key, 0, 0, size > 0) == -1)     \
197                         RETURN(RC_FAIL);                        \
198         } while(0)
199
200 /*
201  * Generalized function for decoding the primitive values.
202  */
203 asn_dec_rval_t
204 xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
205         asn_struct_ctx_t *ctx,  /* Type decoder context */
206         void *struct_key,
207         const char *xml_tag,    /* Expected XML tag */
208         const void *buf_ptr, size_t size,
209         int (*opt_unexpected_tag_decoder)
210                 (void *struct_key, const void *chunk_buf, size_t chunk_size),
211         ssize_t (*body_receiver)
212                 (void *struct_key, const void *chunk_buf, size_t chunk_size,
213                         int have_more)
214         ) {
215
216         asn_dec_rval_t rval;
217         ssize_t consumed_myself = 0;
218
219         (void)opt_codec_ctx;
220
221         /*
222          * Phases of XER/XML processing:
223          * Phase 0: Check that the opening tag matches our expectations.
224          * Phase 1: Processing body and reacting on closing tag.
225          */
226         if(ctx->phase > 1) RETURN(RC_FAIL);
227         for(;;) {
228                 pxer_chunk_type_e ch_type;      /* XER chunk type */
229                 ssize_t ch_size;                /* Chunk size */
230                 xer_check_tag_e tcv;            /* Tag check value */
231
232                 /*
233                  * Get the next part of the XML stream.
234                  */
235                 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
236                         &ch_type);
237                 if(ch_size == -1) {
238             RETURN(RC_FAIL);
239         } else {
240                         switch(ch_type) {
241                         case PXER_WMORE:
242                 RETURN(RC_WMORE);
243                         case PXER_COMMENT:              /* Got XML comment */
244                                 ADVANCE(ch_size);       /* Skip silently */
245                                 continue;
246                         case PXER_TEXT:
247                                 if(ctx->phase == 0) {
248                                         /*
249                                          * We have to ignore whitespace here,
250                                          * but in order to be forward compatible
251                                          * with EXTENDED-XER (EMBED-VALUES, #25)
252                                          * any text is just ignored here.
253                                          */
254                                 } else {
255                                         XER_GOT_BODY(buf_ptr, ch_size, size);
256                                 }
257                                 ADVANCE(ch_size);
258                                 continue;
259                         case PXER_TAG:
260                                 break;  /* Check the rest down there */
261                         }
262                 }
263
264                 assert(ch_type == PXER_TAG && size);
265
266                 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
267                 /*
268                  * Phase 0:
269                  *      Expecting the opening tag
270                  *      for the type being processed.
271                  * Phase 1:
272                  *      Waiting for the closing XML tag.
273                  */
274                 switch(tcv) {
275                 case XCT_BOTH:
276                         if(ctx->phase) break;
277                         /* Finished decoding of an empty element */
278                         XER_GOT_EMPTY();
279                         ADVANCE(ch_size);
280                         ctx->phase = 2; /* Phase out */
281                         RETURN(RC_OK);
282                 case XCT_OPENING:
283                         if(ctx->phase) break;
284                         ADVANCE(ch_size);
285                         ctx->phase = 1; /* Processing body phase */
286                         continue;
287                 case XCT_CLOSING:
288                         if(!ctx->phase) break;
289                         ADVANCE(ch_size);
290                         ctx->phase = 2; /* Phase out */
291                         RETURN(RC_OK);
292                 case XCT_UNKNOWN_BO:
293                         /*
294                          * Certain tags in the body may be expected.
295                          */
296                         if(opt_unexpected_tag_decoder
297                         && opt_unexpected_tag_decoder(struct_key,
298                                         buf_ptr, ch_size) >= 0) {
299                                 /* Tag's processed fine */
300                                 ADVANCE(ch_size);
301                                 if(!ctx->phase) {
302                                         /* We are not expecting
303                                          * the closing tag anymore. */
304                                         ctx->phase = 2; /* Phase out */
305                                         RETURN(RC_OK);
306                                 }
307                                 continue;
308                         }
309                         /* Fall through */
310                 default:
311                         break;          /* Unexpected tag */
312                 }
313
314                 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
315                 break;  /* Dark and mysterious things have just happened */
316         }
317
318         RETURN(RC_FAIL);
319 }
320
321
322 size_t
323 xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
324         const char *p = (const char *)chunk_buf;
325         const char *pend = p + chunk_size;
326
327         for(; p < pend; p++) {
328                 switch(*p) {
329                 /* X.693, #8.1.4
330                  * HORISONTAL TAB (9)
331                  * LINE FEED (10) 
332                  * CARRIAGE RETURN (13) 
333                  * SPACE (32)
334                  */
335                 case 0x09: case 0x0a: case 0x0d: case 0x20:
336                         continue;
337                 default:
338                         break;
339                 }
340                 break;
341         }
342         return (p - (const char *)chunk_buf);
343 }
344
345 /*
346  * This is a vastly simplified, non-validating XML tree skipper.
347  */
348 int
349 xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
350         assert(*depth > 0);
351         switch(tcv) {
352         case XCT_BOTH:
353         case XCT_UNKNOWN_BO:
354                 /* These negate each other. */
355                 return 0;
356         case XCT_OPENING:
357         case XCT_UNKNOWN_OP:
358                 ++(*depth);
359                 return 0;
360         case XCT_CLOSING:
361         case XCT_UNKNOWN_CL:
362                 if(--(*depth) == 0)
363                         return (tcv == XCT_CLOSING) ? 2 : 1;
364                 return 0;
365         default:
366                 return -1;
367         }
368 }