1 /*
2  * libwbxml, the WBXML Library.
3  * Copyright (C) 2002-2008 Aymerick Jehanne <aymerick@jehanne.org>
4  * Copyright (C) 2008-2011 Michael Bell <michael.bell@opensync.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * LGPL v2.1: http://www.gnu.org/copyleft/lesser.txt
21  *
22  * Contact: aymerick@jehanne.org
23  * Home: http://libwbxml.aymerick.com
24  */
25 
26 /**
27  * @file wbxml_tree_clb_xml.c
28  * @ingroup wbxml_tree
29  *
30  * @author Aymerick Jehanne <aymerick@jehanne.org>
31  * @date 03/03/11
32  *
33  * @brief WBXML Tree Callbacks for XML Parser (Expat)
34  */
35 
36 #include "wbxml_config_internals.h"
37 
38 #if defined( HAVE_EXPAT )
39 
40 #include "wbxml_tree_clb_xml.h"
41 #include "wbxml_tree.h"
42 #include "wbxml_log.h"
43 #include "wbxml_charset.h"
44 #include "wbxml_base64.h"
45 #include <assert.h>
46 
47 /************************************
48  *  Public Functions
49  */
50 
wbxml_tree_clb_xml_decl(void * ctx,const XML_Char * version,const XML_Char * encoding,int standalone)51 void wbxml_tree_clb_xml_decl(void           *ctx,
52                              const XML_Char *version,
53                              const XML_Char *encoding,
54                              int             standalone)
55 {
56     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
57 
58     (void) standalone; /* avoid warning about unused parameter */
59 
60     if (tree_ctx->expat_utf16) {
61         /** @todo Convert from UTF-16 to UTF-8 */
62     }
63 
64     /* This handler is called for XML declarations and also for text declarations discovered
65      * in external entities. The way to distinguish is that the version parameter will
66      * be NULL for text declarations.
67      */
68     if (version != NULL) {
69         if (encoding != NULL) {
70             /* Get encoding */
71             if (!wbxml_charset_get_mib((const WB_TINY*)encoding, &(tree_ctx->tree->orig_charset))) {
72                 WBXML_WARNING((WBXML_CONV, "Charset Encoding not supported: %s", encoding));
73             }
74         }
75     }
76 }
77 
78 
wbxml_tree_clb_xml_doctype_decl(void * ctx,const XML_Char * doctypeName,const XML_Char * sysid,const XML_Char * pubid,int has_internal_subset)79 void wbxml_tree_clb_xml_doctype_decl(void           *ctx,
80                                      const XML_Char *doctypeName,
81                                      const XML_Char *sysid,
82                                      const XML_Char *pubid,
83                                      int             has_internal_subset)
84 {
85     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
86     const WBXMLLangEntry *lang_table = NULL;
87 
88     (void) doctypeName;         /* avoid warning about unused parameter */
89     (void) has_internal_subset; /* avoid warning about unused parameter */
90 
91     if (tree_ctx->expat_utf16) {
92         /** @todo Convert from UTF-16 to UTF-8 */
93     }
94 
95     /* Search for Language Table, given the XML Public ID and System ID */
96     lang_table = wbxml_tables_search_table(wbxml_tables_get_main(),
97                                            (const WB_UTINY *) pubid,
98                                            (const WB_UTINY *) sysid,
99                                            NULL);
100 
101     if (lang_table != NULL) {
102         /* Ho Yeah ! We got it ! */
103         tree_ctx->tree->lang = lang_table;
104     }
105     else {
106         /* We will try to find the Language Table, given the Root Element */
107         WBXML_WARNING((WBXML_CONV, "Language Table NOT found, given the XML Public ID and System ID"));
108     }
109 }
110 
111 
wbxml_tree_clb_xml_start_element(void * ctx,const XML_Char * localName,const XML_Char ** attrs)112 void wbxml_tree_clb_xml_start_element(void           *ctx,
113                                       const XML_Char *localName,
114                                       const XML_Char **attrs)
115 {
116     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
117     const WBXMLLangEntry *lang_table = NULL;
118 
119     WBXML_DEBUG((WBXML_PARSER, "Expat element start callback ('%s')", localName));
120 
121     if (tree_ctx->expat_utf16) {
122         /** @todo Convert from UTF-16 to UTF-8 */
123     }
124 
125     /* Check for Error */
126     if (tree_ctx->error != WBXML_OK)
127         return;
128 
129     /* Are we skipping a whole node ? */
130     if (tree_ctx->skip_lvl > 0) {
131         tree_ctx->skip_lvl++;
132         return;
133     }
134 
135     if (tree_ctx->current == NULL) {
136         /* This is the Root Element */
137         if (tree_ctx->tree->lang == NULL) {
138             /* Language Table not already found: Search again */
139             lang_table = wbxml_tables_search_table(wbxml_tables_get_main(),
140                                                    NULL,
141                                                    NULL,
142                                                    (const WB_UTINY *) localName);
143 
144             if (lang_table == NULL) {
145                 /* Damn, this is an unknown language for us... */
146                 tree_ctx->error = WBXML_ERROR_UNKNOWN_XML_LANGUAGE;
147                 return;
148             }
149             else {
150                 /* Well, we hope this was the Language we are searching for.. let's try with it :| */
151                 tree_ctx->tree->lang = lang_table;
152             }
153         }
154     }
155 
156 #if defined( WBXML_SUPPORT_SYNCML )
157 
158     /* If this is an embedded (not root) document, skip it
159      * Actually SyncML DevInf and DM DDF are known as such
160      * potentially embedded documents.
161      */
162     if ((
163          (WBXML_STRCMP(localName, "syncml:devinf:DevInf") == 0) ||
164          (WBXML_STRCMP(localName, "syncml:dmddf1.2:MgmtTree") == 0)
165         )&&
166         (tree_ctx->current != NULL))
167     {
168         tree_ctx->skip_start = XML_GetCurrentByteIndex(tree_ctx->xml_parser);
169 
170         /* Skip this node */
171         tree_ctx->skip_lvl++;
172 
173         return;
174     }
175 
176 #endif /* WBXML_SUPPORT_SYNCML */
177 
178     /* Add Element Node */
179     tree_ctx->current = wbxml_tree_add_xml_elt_with_attrs(tree_ctx->tree,
180                                                           tree_ctx->current,
181                                                           (WB_UTINY *) localName,
182                                                           (const WB_UTINY**) attrs);
183 
184     if (tree_ctx->current == NULL) {
185         tree_ctx->error = WBXML_ERROR_NOT_ENOUGH_MEMORY;
186     }
187 }
188 
189 
wbxml_tree_clb_xml_end_element(void * ctx,const XML_Char * localName)190 void wbxml_tree_clb_xml_end_element(void           *ctx,
191                                     const XML_Char *localName)
192 {
193     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
194     WBXMLBuffer *content = NULL;
195     WBXMLTreeNode *node = NULL;
196     WBXMLError ret = WBXML_OK;
197 
198     WBXML_DEBUG((WBXML_PARSER, "Expat element end callback ('%s')", localName));
199 
200     /* If the node is flagged as binary node
201      * then the data is base64 encoded in the XML document
202      * and the data must be decoded in one step.
203      * Examples: Microsoft ActiveSync tags ConversationId or MIME
204      */
205 
206     node = tree_ctx->current;
207     if (node && node->type == WBXML_TREE_ELEMENT_NODE &&
208         node->name->type == WBXML_VALUE_TOKEN &&
209         node->name->u.token->options & WBXML_TAG_OPTION_BINARY)
210     {
211         if (node->content == NULL)
212         {
213             WBXML_DEBUG((WBXML_PARSER, "    Binary tag: No content => no conversion!"));
214         } else {
215             WBXML_DEBUG((WBXML_PARSER, "    Binary tag: Convert base64 data"));
216             ret = wbxml_buffer_decode_base64(node->content);
217             if (ret != WBXML_OK)
218             {
219                 WBXML_DEBUG((WBXML_PARSER, "    Binary tag: Base64 decoder failed!"));
220                 tree_ctx->error = ret;
221             } else {
222                 /* Add the buffer as a regular string node (since libwbxml doesn't
223                  * offer a way to specify an opaque data node). The WBXML
224                  * encoder is responsible for generating correct opaque data for
225                  * nodes like this.
226                  */
227                 if (wbxml_tree_add_text(tree_ctx->tree,
228                                         tree_ctx->current,
229                                         (const WB_UTINY*)wbxml_buffer_get_cstr(node->content),
230                                         wbxml_buffer_len(node->content)) == NULL)
231                 {
232                     WBXML_DEBUG((WBXML_PARSER, "    Binary tag: Cannot add base64 decoded node!"));
233                     tree_ctx->error = WBXML_ERROR_INTERNAL;
234                 }
235             }
236             /* safe cleanup */
237             content = node->content;
238             node->content = NULL;
239             wbxml_buffer_destroy(content);
240         }
241     }
242 
243     if (tree_ctx->expat_utf16) {
244         /** @todo Convert from UTF-16 to UTF-8 */
245     }
246 
247     /* Check for Error */
248     if (tree_ctx->error != WBXML_OK)
249         return;
250 
251     /* Are we skipping a whole node ? */
252     if (tree_ctx->skip_lvl > 0) {
253         if (tree_ctx->skip_lvl == 1)
254         {
255             /* End of skipped node */
256 
257 #if defined( WBXML_SUPPORT_SYNCML )
258             if (WBXML_STRCMP(localName, "syncml:devinf:DevInf") == 0 ||
259 	        WBXML_STRCMP(localName, "syncml:dmddf1.2:MgmtTree") == 0) {
260 		/* definitions first ... or some compilers don't like it */
261                 WBXMLBuffer *embed_doc = NULL;
262                 WBXMLTree *tree = NULL;
263 		const WBXMLLangEntry *lang;
264 
265                 /* Get embedded DevInf or DM DDF Document */
266                 embed_doc = wbxml_buffer_create(tree_ctx->input_buff + tree_ctx->skip_start,
267                                                  XML_GetCurrentByteIndex(tree_ctx->xml_parser) - tree_ctx->skip_start,
268                                                  XML_GetCurrentByteIndex(tree_ctx->xml_parser) - tree_ctx->skip_start + 10);
269                 if (embed_doc == NULL) {
270                     tree_ctx->error = WBXML_ERROR_NOT_ENOUGH_MEMORY;
271                     wbxml_buffer_destroy(embed_doc);
272                     return;
273                 }
274 
275                 if (tree_ctx->expat_utf16) {
276                     /** @todo Convert from UTF-16 to UTF-8 */
277                 }
278 
279                 /* Check Buffer Creation and add the closing tag */
280 		if ((WBXML_STRCMP(localName, "syncml:devinf:DevInf") == 0 &&
281 		     (!wbxml_buffer_append_cstr(embed_doc, "</DevInf>")))
282                     ||
283 		    (WBXML_STRCMP(localName, "syncml:dmddf1.2:MgmtTree") == 0 &&
284 		     (!wbxml_buffer_append_cstr(embed_doc, "</MgmtTree>"))))
285                 {
286                     tree_ctx->error = WBXML_ERROR_NOT_ENOUGH_MEMORY;
287                     wbxml_buffer_destroy(embed_doc);
288                     return;
289                 }
290 
291                 /* Add doctype to give the XML parser a chance */
292 		if (WBXML_STRCMP(localName, "syncml:dmddf1.2:MgmtTree") == 0 &&
293 		    tree_ctx->tree->lang->langID != WBXML_LANG_SYNCML_SYNCML12)
294 		{
295                     tree_ctx->error = WBXML_ERROR_UNKNOWN_XML_LANGUAGE;
296                     wbxml_buffer_destroy(embed_doc);
297                     return;
298 		}
299 		switch(tree_ctx->tree->lang->langID)
300 		{
301 			case WBXML_LANG_SYNCML_SYNCML10:
302 				lang = wbxml_tables_get_table(WBXML_LANG_SYNCML_DEVINF10);
303 				break;
304 			case WBXML_LANG_SYNCML_SYNCML11:
305 				lang = wbxml_tables_get_table(WBXML_LANG_SYNCML_DEVINF11);
306 				break;
307 			case WBXML_LANG_SYNCML_SYNCML12:
308 				if (WBXML_STRCMP(localName, "syncml:dmddf1.2:MgmtTree") == 0) {
309 					lang = wbxml_tables_get_table(WBXML_LANG_SYNCML_DMDDF12);
310 				} else {
311 					lang = wbxml_tables_get_table(WBXML_LANG_SYNCML_DEVINF12);
312 				}
313 				break;
314 			default:
315 				tree_ctx->error = WBXML_ERROR_UNKNOWN_XML_LANGUAGE;
316 				wbxml_buffer_destroy(embed_doc);
317 				return;
318 		}
319 
320 		assert (lang!= NULL);
321 		if (lang == NULL) {
322 			tree_ctx->error = WBXML_ERROR_UNKNOWN_XML_LANGUAGE;
323 			wbxml_buffer_destroy(embed_doc);
324 			return;
325 		}
326 
327 		/* DOCTYPE in reverse order */
328 		if (!wbxml_buffer_insert_cstr(embed_doc,(WB_UTINY *) "\">\n", 0) ||                     /* > */
329 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) lang->publicID->xmlDTD, 0) ||      /* DTD */
330 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) "\" \"", 0) ||                     /* DTD */
331 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) lang->publicID->xmlPublicID, 0) || /* Public ID */
332 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) " PUBLIC \"", 0) ||                /*  PUBLIC " */
333 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) lang->publicID->xmlRootElt, 0) ||  /* Root Element */
334 		    !wbxml_buffer_insert_cstr(embed_doc, (WB_UTINY *) "<!DOCTYPE ", 0))                  /* <!DOCTYPE */
335 		{
336 			tree_ctx->error = WBXML_ERROR_ENCODER_APPEND_DATA;
337                 	wbxml_buffer_destroy(embed_doc);
338 			return;
339 		}
340 
341                 WBXML_DEBUG((WBXML_PARSER, "\t Embedded Doc : '%s'", wbxml_buffer_get_cstr(embed_doc)));
342 
343                 /* Parse 'DevInf' Document */
344                 if ((ret = wbxml_tree_from_xml(wbxml_buffer_get_cstr(embed_doc),
345                                                wbxml_buffer_len(embed_doc),
346                                                &tree)) != WBXML_OK)
347                 {
348                     tree_ctx->error = ret;
349                     wbxml_buffer_destroy(embed_doc);
350                     return;
351                 }
352 
353                 /* Add Tree Node */
354                 tree_ctx->current = wbxml_tree_add_tree(tree_ctx->tree,
355                                                         tree_ctx->current,
356                                                         tree);
357                 if (tree_ctx->current == NULL)
358                 {
359                     tree_ctx->error = WBXML_ERROR_INTERNAL;
360                     wbxml_tree_destroy(tree);
361                     wbxml_buffer_destroy(embed_doc);
362                     return;
363                 }
364 
365                 /* Clean-up */
366                 wbxml_buffer_destroy(embed_doc);
367                 tree_ctx->skip_lvl = 0;
368             }
369 #endif /* WBXML_SUPPORT_SYNCML */
370         }
371         else {
372             tree_ctx->skip_lvl--;
373             return;
374         }
375     }
376 
377     if (tree_ctx->current == NULL) {
378         tree_ctx->error = WBXML_ERROR_INTERNAL;
379         return;
380     }
381 
382     if (tree_ctx->current->parent == NULL) {
383         /* This must be the Root Element */
384         if (tree_ctx->current != tree_ctx->tree->root) {
385             tree_ctx->error = WBXML_ERROR_INTERNAL;
386         }
387     }
388     else {
389 #if defined ( WBXML_SUPPORT_SYNCML )
390         /* Have we added a missing CDATA section ?
391          * If so, we assume that now that we have reached an end of Element,
392          * the CDATA section ended, and so we go back to parent.
393          */
394         if ((tree_ctx->current != NULL) && (tree_ctx->current->type == WBXML_TREE_CDATA_NODE))
395             tree_ctx->current = tree_ctx->current->parent;
396 #endif /* WBXML_SUPPORT_SYNCML */
397 
398         /* Go back one step upper in the tree */
399         tree_ctx->current = tree_ctx->current->parent;
400     }
401 }
402 
403 
wbxml_tree_clb_xml_start_cdata(void * ctx)404 void wbxml_tree_clb_xml_start_cdata(void *ctx)
405 {
406     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
407 
408     /* Check for Error */
409     if (tree_ctx->error != WBXML_OK)
410         return;
411 
412     /* Are we skipping a whole node ? */
413     if (tree_ctx->skip_lvl > 0)
414         return;
415 
416     /* Add CDATA Node */
417     tree_ctx->current = wbxml_tree_add_cdata(tree_ctx->tree, tree_ctx->current);
418     if (tree_ctx->current == NULL) {
419         tree_ctx->error = WBXML_ERROR_INTERNAL;
420     }
421 }
422 
423 
wbxml_tree_clb_xml_end_cdata(void * ctx)424 void wbxml_tree_clb_xml_end_cdata(void *ctx)
425 {
426     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
427 
428     /* Check for Error */
429     if (tree_ctx->error != WBXML_OK)
430         return;
431 
432     /* Are we skipping a whole node ? */
433     if (tree_ctx->skip_lvl > 0)
434         return;
435 
436     if (tree_ctx->current == NULL) {
437         tree_ctx->error = WBXML_ERROR_INTERNAL;
438         return;
439     }
440 
441     if (tree_ctx->current->parent == NULL) {
442         /* This must be the Root Element */
443         if (tree_ctx->current != tree_ctx->tree->root) {
444             tree_ctx->error = WBXML_ERROR_INTERNAL;
445         }
446     }
447     else {
448         /* Go back one step upper in the tree */
449         tree_ctx->current = tree_ctx->current->parent;
450     }
451 }
452 
453 
wbxml_tree_clb_xml_characters(void * ctx,const XML_Char * ch,int len)454 void wbxml_tree_clb_xml_characters(void           *ctx,
455                                    const XML_Char *ch,
456                                    int             len)
457 {
458     WBXMLTreeNode *node;
459     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
460 
461     WBXML_DEBUG((WBXML_PARSER, "Expat text callback"));
462 
463     if (tree_ctx->expat_utf16) {
464         /** @todo Convert from UTF-16 to UTF-8 */
465     }
466 
467     /* Check for Error */
468     if (tree_ctx->error != WBXML_OK)
469         return;
470 
471     /* Are we skipping a whole node ? */
472     if (tree_ctx->skip_lvl > 0)
473         return;
474 
475 #if defined ( WBXML_SUPPORT_SYNCML )
476     /* Specific treatment for SyncML */
477     switch (wbxml_tree_node_get_syncml_data_type(tree_ctx->current)) {
478     case WBXML_SYNCML_DATA_TYPE_DIRECTORY_VCARD:
479     case WBXML_SYNCML_DATA_TYPE_VCALENDAR:
480     case WBXML_SYNCML_DATA_TYPE_VCARD:
481     case WBXML_SYNCML_DATA_TYPE_VOBJECT:
482         /* SyncML has some real design bugs
483          * because the authors of the specification did not understand XML.
484          *
485          * There must be a hack to preserve the CRLFs of vFormat objects.
486          * The only chance to do this is the detection of the vFormat itself
487          * and the conversion of every LF to a CRLF.
488          *
489          * The line breaks are always in a single text node.
490          * So a CR is appended to get a CRLF at the end.
491          */
492 
493         if (len == 1 && ch[0] == '\n') /* line break - LF */
494         {
495             ch = "\r\n";
496             len = 2;
497         }
498 
499         /* Do not break here.
500          * The CDATA handling is required for vFormat objects too.
501          */
502     case WBXML_SYNCML_DATA_TYPE_CLEAR:
503         /*
504          * Add a missing CDATA section node
505          *
506          * Example:
507          * <Add>
508          *   <CmdID>6</CmdID>
509          *   <Meta><Type xmlns='syncml:metinf'>text/x-vcard</Type></Meta>
510          *   <Item>
511          *     <Source>
512          *         <LocURI>pas-id-3F4B790300000000</LocURI>
513          *     </Source>
514          *     <Data>BEGIN:VCARD
515          *  VERSION:2.1
516          *  X-EVOLUTION-FILE-AS:Ximian, Inc.
517          *  N:
518          *  LABEL;WORK;ENCODING=QUOTED-PRINTABLE:401 Park Drive  3 West=0ABoston, MA
519          *  02215=0AUSA
520          *  TEL;WORK;VOICE:(617) 236-0442
521          *  TEL;WORK;FAX:(617) 236-8630
522          *  EMAIL;INTERNET:[EMAIL PROTECTED]
523          *  URL:www.ximian.com/
524          *  ORG:Ximian, Inc.
525          *  NOTE:Welcome to the Ximian Addressbook.
526          *  UID:pas-id-3F4B790300000000
527          *  END:VCARD</Data>
528          *   </Item>
529          * </Add>
530          *
531          * The end of CDATA section is assumed to be reached when parsing the end
532          * of </Data> element.
533          *
534          * This kind of document is erroneous, but we must handle it.
535          * Normally, this should be:
536          *
537          *  ...
538          *     <Data><!CDATA[[BEGIN:VCARD
539          *  VERSION:2.1
540          *  X-EVOLUTION-FILE-AS:Ximian, Inc.
541          *  ...
542          *  UID:pas-id-3F4B790300000000
543          *  END:VCARD
544          *  ]]></Data>
545          *  ...
546          */
547 
548         /*
549          * We add a missing CDATA section if we are not already in a CDATA section.
550          *
551          * We don't add a CDATA section if we have already added a CDATA section. This
552          * permits to correctly handle good XML documents like this:
553          *
554          *  ...
555          *     <Data><!CDATA[[BEGIN:VCARD
556          *  VERSION:2.1
557          *  X-EVOLUTION-FILE-AS:Ximian, Inc.
558          *  ...
559          *  UID:pas-id-3F4B790300000000
560          *  END:VCARD
561          *  ]]>
562          *     </Data>
563          *  ...
564          *
565          * In this example, the spaces beetwen "]]>" and "</Data>" must not be added
566          * to a CDATA section.
567          */
568         if ((tree_ctx->current != NULL) &&
569             (tree_ctx->current->type != WBXML_TREE_CDATA_NODE) &&
570             !((tree_ctx->current->children != NULL) &&
571               (tree_ctx->current->children->type == WBXML_TREE_CDATA_NODE)))
572         {
573             /* Add CDATA Node */
574             tree_ctx->current = wbxml_tree_add_cdata(tree_ctx->tree, tree_ctx->current);
575             if (tree_ctx->current == NULL) {
576                 tree_ctx->error = WBXML_ERROR_INTERNAL;
577                 return;
578             }
579         }
580 
581         /* Now we can add the Text Node */
582         break;
583 
584     default:
585         /* NOP */
586         break;
587     } /* switch */
588 #endif /* WBXML_SUPPORT_SYNCML */
589 
590     /* We expect that "byte array" or BLOB types are
591      * encoded in Base 64 in the XML code, since they may contain binary data.
592      */
593 
594     node = tree_ctx->current;
595     if (node && node->type == WBXML_TREE_ELEMENT_NODE &&
596         node->name->type == WBXML_VALUE_TOKEN &&
597         node->name->u.token->options & WBXML_TAG_OPTION_BINARY)
598     {
599         WBXML_DEBUG((WBXML_PARSER, "    Binary tag: Caching base64 encoded data for later conversion."));
600         if (node->content == NULL)
601         {
602             node->content = wbxml_buffer_create(ch, len, 1);
603             if (node->content == NULL)
604                 tree_ctx->error = WBXML_ERROR_NOT_ENOUGH_MEMORY;
605         } else {
606             if (!wbxml_buffer_append_data(node->content, ch, len))
607                 tree_ctx->error = WBXML_ERROR_NOT_ENOUGH_MEMORY;
608         }
609         return;
610     }
611 
612     /* Add Text Node */
613     if (wbxml_tree_add_text(tree_ctx->tree,
614                             tree_ctx->current,
615                             (const WB_UTINY*) ch,
616                             len) == NULL)
617     {
618         tree_ctx->error = WBXML_ERROR_INTERNAL;
619     }
620 }
621 
622 
wbxml_tree_clb_xml_pi(void * ctx,const XML_Char * target,const XML_Char * data)623 void wbxml_tree_clb_xml_pi(void           *ctx,
624                            const XML_Char *target,
625                            const XML_Char *data)
626 {
627     WBXMLTreeClbCtx *tree_ctx = (WBXMLTreeClbCtx *) ctx;
628 
629     if (tree_ctx->expat_utf16) {
630         /** @todo Convert from UTF-16 to UTF-8 */
631     }
632 
633     /* Check for Error */
634     if (tree_ctx->error != WBXML_OK)
635         return;
636 
637     /* Are we skipping a whole node ? */
638     if (tree_ctx->skip_lvl > 0)
639         return;
640 
641     /** @todo wbxml2xml_clb_pi() */
642 }
643 
644 #endif /* HAVE_EXPAT */
645