1 /*
2  * libwbxml, the WBXML Library.
3  * Copyright (C) 2002-2008 Aymerick Jehanne <aymerick@jehanne.org>
4  * Copyright (C) 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_conv.c
28  * @ingroup wbxml_conv
29  *
30  * @author Aymerick Jehanne <aymerick@jehanne.org>
31  * @date 03/02/23
32  *
33  * @brief WBXML Convertion Library (XML to WBXML, and WBXML to XML)
34  */
35 
36 #include "wbxml_conv.h"
37 #include "wbxml_tree.h"
38 #include "wbxml_log.h"
39 
40 /****************************
41  *    converter objects     *
42  ****************************
43  */
44 
45 struct WBXMLConvWBXML2XML_s {
46     WBXMLGenXMLType gen_type;    /**< WBXML_GEN_XML_COMPACT | WBXML_GEN_XML_INDENT | WBXML_GEN_XML_CANONICAL (Default: WBXML_GEN_XML_INDENT) */
47     WBXMLLanguage lang;          /**< Force document Language (overwrite document Public ID) */
48     WBXMLCharsetMIBEnum charset; /**< Set document Language (does not overwrite document character set) */
49     WB_UTINY indent;             /**< Indentation Delta, when using WBXML_GEN_XML_INDENT Generation Type (Default: 0) */
50     WB_BOOL keep_ignorable_ws;   /**< Keep Ignorable Whitespaces (Default: FALSE) */
51 };
52 
53 struct WBXMLConvXML2WBXML_s {
54     WBXMLVersion wbxml_version; /**< WBXML Version */
55     WB_BOOL keep_ignorable_ws;  /**< Keep Ignorable Whitespaces (Default: FALSE) */
56     WB_BOOL use_strtbl;         /**< Generate String Table (Default: TRUE) */
57     WB_BOOL produce_anonymous;  /**< Produce an anonymous document (Default: FALSE) */
58 };
59 
60 /****************************
61  *     Public Functions     *
62  ****************************
63  */
64 
wbxml_conv_wbxml2xml_create(WBXMLConvWBXML2XML ** conv)65 WBXML_DECLARE(WBXMLError) wbxml_conv_wbxml2xml_create(WBXMLConvWBXML2XML **conv)
66 {
67     if (conv == NULL) {
68         return WBXML_ERROR_BAD_PARAMETER;
69     }
70 
71     *conv = wbxml_malloc(sizeof(WBXMLConvWBXML2XML));
72     if (*conv == NULL) {
73         return WBXML_ERROR_NOT_ENOUGH_MEMORY;
74     }
75 
76     (*conv)->gen_type = WBXML_GEN_XML_INDENT;
77     (*conv)->lang     = WBXML_LANG_UNKNOWN;
78     (*conv)->charset  = WBXML_CHARSET_UNKNOWN;
79     (*conv)->indent   = 0;
80     (*conv)->keep_ignorable_ws = FALSE;
81 
82     return WBXML_OK;
83 }
84 
85 /**
86  * @brief Set the XML generation type (default: WBXML_GEN_XML_INDENT).
87  * @param conv     [in] the converter
88  * @param gen_type [in] generation type
89  */
wbxml_conv_wbxml2xml_set_gen_type(WBXMLConvWBXML2XML * conv,WBXMLGenXMLType gen_type)90 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_set_gen_type(WBXMLConvWBXML2XML *conv, WBXMLGenXMLType gen_type)
91 {
92     conv->gen_type = gen_type;
93 }
94 
95 /**
96  * @brief Set the used WBXML language.
97  *        The language is usually detected by the specified public ID in the document.
98  *        If the public ID is set then it overrides the language.
99  * @param conv [in] the converter
100  * @param lang [in] language (e.g. SYNCML12)
101  */
wbxml_conv_wbxml2xml_set_language(WBXMLConvWBXML2XML * conv,WBXMLLanguage lang)102 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_set_language(WBXMLConvWBXML2XML *conv, WBXMLLanguage lang)
103 {
104     conv->lang = lang;
105 }
106 
107 /**
108  * @brief Set the used character set.
109  *        The default character set is UTF-8.
110  *        If the document specifies a character set by it own
111  *        then this character set overrides the parameter charset.
112  * @param conv    [in] the converter
113  * @param charset [in] the character set
114  */
wbxml_conv_wbxml2xml_set_charset(WBXMLConvWBXML2XML * conv,WBXMLCharsetMIBEnum charset)115 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_set_charset(WBXMLConvWBXML2XML *conv, WBXMLCharsetMIBEnum charset)
116 {
117     conv->charset = charset;
118 }
119 
120 /**
121  * @brief Set the indent of the generated XML document (please see EXPAT default).
122  * @param conv   [in] the converter
123  * @param indent [in] the number of blanks
124  */
wbxml_conv_wbxml2xml_set_indent(WBXMLConvWBXML2XML * conv,WB_UTINY indent)125 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_set_indent(WBXMLConvWBXML2XML *conv, WB_UTINY indent)
126 {
127     conv->indent = indent;
128 }
129 
130 /**
131  * @brief Enable whitespace preservation (default: FALSE).
132  * @param conv     [in] the converter
133  */
wbxml_conv_wbxml2xml_enable_preserve_whitespaces(WBXMLConvWBXML2XML * conv)134 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_enable_preserve_whitespaces(WBXMLConvWBXML2XML *conv)
135 {
136     conv->keep_ignorable_ws = TRUE;
137 }
138 
139 /**
140  * @brief Convert WBXML to XML
141  * @param conv      [in] the converter
142  * @param wbxml     [in] WBXML Document to convert
143  * @param wbxml_len [in] Length of WBXML Document
144  * @param xml       [out] Resulting XML Document
145  * @param xml_len   [out] XML Document length
146  * @return WBXML_OK if conversion succeeded, an Error Code otherwise
147  */
wbxml_conv_wbxml2xml_run(WBXMLConvWBXML2XML * conv,WB_UTINY * wbxml,WB_ULONG wbxml_len,WB_UTINY ** xml,WB_ULONG * xml_len)148 WBXML_DECLARE(WBXMLError) wbxml_conv_wbxml2xml_run(WBXMLConvWBXML2XML *conv,
149                                                    WB_UTINY  *wbxml,
150                                                    WB_ULONG   wbxml_len,
151                                                    WB_UTINY **xml,
152                                                    WB_ULONG  *xml_len)
153 {
154     WBXMLGenXMLParams params;
155     WBXMLTree *wbxml_tree = NULL;
156     WB_ULONG   dummy_len = 0;
157     WBXMLError ret = WBXML_OK;
158 
159     /* Copy options */
160     params.gen_type          = conv->gen_type;
161     params.lang              = conv->lang;
162     params.charset           = conv->charset;
163     params.keep_ignorable_ws = conv->keep_ignorable_ws;
164     params.indent            = conv->indent;
165 
166     /* Check Parameters (we allow 'xml_len' to be NULL for backward compatibility) */
167     if ((wbxml == NULL) || (wbxml_len == 0) || (xml == NULL))
168         return WBXML_ERROR_BAD_PARAMETER;
169 
170     if (xml_len == NULL)
171         xml_len = &dummy_len;
172 
173     *xml = NULL;
174     *xml_len = 0;
175 
176     /* Parse WBXML to WBXML Tree */
177     ret = wbxml_tree_from_wbxml(wbxml, wbxml_len, conv->lang, conv->charset, &wbxml_tree);
178     if (ret != WBXML_OK) {
179         WBXML_ERROR((WBXML_CONV, "wbxml2xml conversion failed - WBXML Parser Error: %s",
180                                  wbxml_errors_string(ret)));
181 
182         return ret;
183     }
184     else {
185         /* Convert Tree to XML */
186         ret = wbxml_tree_to_xml(wbxml_tree, xml, xml_len, &params);
187         if (ret != WBXML_OK) {
188             WBXML_ERROR((WBXML_CONV, "wbxml2xml conversion failed - WBXML Encoder Error: %s",
189                                      wbxml_errors_string(ret)));
190         }
191 
192         /* Clean-up */
193         wbxml_tree_destroy(wbxml_tree);
194 
195         return ret;
196     }
197 }
198 
199 /**
200  * @brief Destroy the converter object.
201  * @param [in] the converter
202  */
wbxml_conv_wbxml2xml_destroy(WBXMLConvWBXML2XML * conv)203 WBXML_DECLARE(void) wbxml_conv_wbxml2xml_destroy(WBXMLConvWBXML2XML *conv)
204 {
205     wbxml_free(conv);
206 }
207 
208 /**
209  * @brief Create a new WBXML to XML converter with the default configuration.
210  * @param conv [out] a reference to the pointer of the new converter
211  * @return WBXML_OK if conversion succeeded, an Error Code otherwise
212  */
wbxml_conv_xml2wbxml_create(WBXMLConvXML2WBXML ** conv)213 WBXML_DECLARE(WBXMLError) wbxml_conv_xml2wbxml_create(WBXMLConvXML2WBXML **conv)
214 {
215     if (conv == NULL) {
216         return WBXML_ERROR_BAD_PARAMETER;
217     }
218 
219     *conv = wbxml_malloc(sizeof(WBXMLConvXML2WBXML));
220     if (*conv == NULL) {
221         return WBXML_ERROR_NOT_ENOUGH_MEMORY;
222     }
223 
224     (*conv)->wbxml_version     = WBXML_VERSION_13;
225     (*conv)->keep_ignorable_ws = FALSE;
226     (*conv)->use_strtbl        = TRUE;
227     (*conv)->produce_anonymous = FALSE;
228 
229     return WBXML_OK;
230 }
231 
232 /**
233  * @brief Set the WBXML version (default: 1.3).
234  * @param conv   [in] the converter
235  * @param indent [in] the number of blanks
236  */
wbxml_conv_xml2wbxml_set_version(WBXMLConvXML2WBXML * conv,WBXMLVersion wbxml_version)237 WBXML_DECLARE(void) wbxml_conv_xml2wbxml_set_version(WBXMLConvXML2WBXML *conv,
238                                                      WBXMLVersion wbxml_version)
239 {
240     conv->wbxml_version = wbxml_version;
241 }
242 
243 /**
244  * @brief Enable whitespace preservation (default: FALSE/DISABLED).
245  * @param conv     [in] the converter
246  */
wbxml_conv_xml2wbxml_enable_preserve_whitespaces(WBXMLConvXML2WBXML * conv)247 WBXML_DECLARE(void) wbxml_conv_xml2wbxml_enable_preserve_whitespaces(WBXMLConvXML2WBXML *conv)
248 {
249     conv->keep_ignorable_ws = TRUE;
250 }
251 
252 /**
253  * @brief Disable string table (default: TRUE/ENABLED).
254  * @param conv     [in] the converter
255  */
wbxml_conv_xml2wbxml_disable_string_table(WBXMLConvXML2WBXML * conv)256 WBXML_DECLARE(void) wbxml_conv_xml2wbxml_disable_string_table(WBXMLConvXML2WBXML *conv)
257 {
258     conv->use_strtbl = FALSE;
259 }
260 
261 /**
262  * @brief Disable public ID (default: TRUE/ENABLED).
263  * @param conv     [in] the converter
264  */
wbxml_conv_xml2wbxml_disable_public_id(WBXMLConvXML2WBXML * conv)265 WBXML_DECLARE(void) wbxml_conv_xml2wbxml_disable_public_id(WBXMLConvXML2WBXML *conv)
266 {
267     conv->produce_anonymous = TRUE;
268 }
269 
270 /**
271  * @brief Convert XML to WBXML
272  * @param conv      [in] the converter
273  * @param xml       [in] XML Document to convert
274  * @param xml_len   [in] Length of XML Document
275  * @param wbxml     [out] Resulting WBXML Document
276  * @param wbxml_len [out] Length of resulting WBXML Document
277  * @return WBXML_OK if conversion succeeded, an Error Code otherwise
278  */
wbxml_conv_xml2wbxml_run(WBXMLConvXML2WBXML * conv,WB_UTINY * xml,WB_ULONG xml_len,WB_UTINY ** wbxml,WB_ULONG * wbxml_len)279 WBXML_DECLARE(WBXMLError) wbxml_conv_xml2wbxml_run(WBXMLConvXML2WBXML *conv,
280                                                    WB_UTINY  *xml,
281                                                    WB_ULONG   xml_len,
282                                                    WB_UTINY **wbxml,
283                                                    WB_ULONG  *wbxml_len)
284 {
285     WBXMLTree *wbxml_tree = NULL;
286     WBXMLError ret = WBXML_OK;
287     WBXMLGenWBXMLParams params;
288 
289     /* Check Parameters */
290     if ((xml == NULL) || (xml_len == 0) || (wbxml == NULL) || (wbxml_len == NULL))
291         return WBXML_ERROR_BAD_PARAMETER;
292 
293     /* copy options */
294     params.wbxml_version     = conv->wbxml_version;
295     params.keep_ignorable_ws = conv->keep_ignorable_ws;
296     params.use_strtbl        = conv->use_strtbl;
297     params.produce_anonymous = conv->produce_anonymous;
298 
299     *wbxml = NULL;
300     *wbxml_len = 0;
301 
302     /* Parse XML to WBXML Tree */
303     ret = wbxml_tree_from_xml(xml, xml_len, &wbxml_tree);
304     if (ret != WBXML_OK) {
305         WBXML_ERROR((WBXML_CONV, "xml2wbxml conversion failed - Error: %s",
306                                   wbxml_errors_string(ret)));
307 
308         return ret;
309     }
310     else {
311         /* Convert Tree to WBXML */
312         ret = wbxml_tree_to_wbxml(wbxml_tree, wbxml, wbxml_len, &params);
313         if (ret != WBXML_OK) {
314             WBXML_ERROR((WBXML_CONV, "xml2wbxml conversion failed - WBXML Encoder Error: %s",
315                                      wbxml_errors_string(ret)));
316         }
317 
318         /* Clean-up */
319         wbxml_tree_destroy(wbxml_tree);
320 
321         return ret;
322     }
323 }
324 
325 
326 /**
327  * @brief Destroy the converter object.
328  * @param [in] the converter
329  */
wbxml_conv_xml2wbxml_destroy(WBXMLConvXML2WBXML * conv)330 WBXML_DECLARE(void) wbxml_conv_xml2wbxml_destroy(WBXMLConvXML2WBXML *conv)
331 {
332     wbxml_free(conv);
333 }
334 
335 /**************************************
336  * Public Functions - DEPRECATED in API
337  */
338 
wbxml_conv_wbxml2xml_withlen(WB_UTINY * wbxml,WB_ULONG wbxml_len,WB_UTINY ** xml,WB_ULONG * xml_len,WBXMLGenXMLParams * params)339 WBXML_DECLARE(WBXMLError) wbxml_conv_wbxml2xml_withlen(WB_UTINY  *wbxml,
340                                                        WB_ULONG   wbxml_len,
341                                                        WB_UTINY **xml,
342                                                        WB_ULONG  *xml_len,
343                                                        WBXMLGenXMLParams *params)
344 {
345     WBXMLConvWBXML2XML *conv = NULL;
346     WBXMLError ret = WBXML_OK;
347 
348     ret = wbxml_conv_wbxml2xml_create(&conv);
349     if (ret != WBXML_OK)
350         return ret;
351 
352     wbxml_conv_wbxml2xml_set_gen_type(conv, params->gen_type);
353     wbxml_conv_wbxml2xml_set_language(conv, params->lang);
354     wbxml_conv_wbxml2xml_set_charset(conv, params->charset);
355     wbxml_conv_wbxml2xml_set_indent(conv, params->indent);
356     if (params->keep_ignorable_ws)
357         wbxml_conv_wbxml2xml_enable_preserve_whitespaces(conv);
358     ret = wbxml_conv_wbxml2xml_run(conv, wbxml, wbxml_len, xml, xml_len);
359     wbxml_conv_wbxml2xml_destroy(conv);
360     return ret;
361 }
362 
wbxml_conv_xml2wbxml_withlen(WB_UTINY * xml,WB_ULONG xml_len,WB_UTINY ** wbxml,WB_ULONG * wbxml_len,WBXMLGenWBXMLParams * params)363 WBXML_DECLARE(WBXMLError) wbxml_conv_xml2wbxml_withlen(WB_UTINY  *xml,
364                                                        WB_ULONG   xml_len,
365                                                        WB_UTINY **wbxml,
366                                                        WB_ULONG  *wbxml_len,
367                                                        WBXMLGenWBXMLParams *params)
368 {
369     WBXMLConvXML2WBXML *conv = NULL;
370     WBXMLError ret = WBXML_OK;
371 
372     ret = wbxml_conv_xml2wbxml_create(&conv);
373     if (ret != WBXML_OK)
374         return ret;
375 
376     wbxml_conv_xml2wbxml_set_version(conv, params->wbxml_version);
377     if (params->keep_ignorable_ws)
378         wbxml_conv_xml2wbxml_enable_preserve_whitespaces(conv);
379     if (!params->use_strtbl)
380         wbxml_conv_xml2wbxml_disable_string_table(conv);
381     if (params->produce_anonymous)
382         wbxml_conv_xml2wbxml_disable_public_id(conv);
383     ret = wbxml_conv_xml2wbxml_run(conv, xml, xml_len, wbxml, wbxml_len);
384     wbxml_conv_xml2wbxml_destroy(conv);
385     return ret;
386 }
387