/* * libwbxml, the WBXML Library. * Copyright (C) 2002-2005 Aymerick Jehanne * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * LGPL v2.1: http://www.gnu.org/copyleft/lesser.txt * * Contact: libwbxml@aymerick.com * Home: http://libwbxml.aymerick.com */ /** * @file wbxml_encoder.c * @ingroup wbxml_encoder * * @author Aymerick Jehanne * @date 11/11/02 * * @brief WBXML Encoder - Encodes a WBXML Tree to WBXML or to XML * * @note Inspired from kannel WML Encoder (http://www.kannel.org) * * @note [OMA WV 1.1] : OMA-WV-CSP_WBXML-V1_1-20021001-A.pdf * * @todo Parse CDDATA * @todo Parse PI * @todo Handle Charsets Encoding * @todo Really generate ENTITY tokens * @todo Handle Namespaces ! * @todo For canonical XML output: Sort the Attributes * @todo When adding string to String Table, check that this is not a Content Text that will be tokenized * @todo For Wireless-Village CSP : * - Encode "Date and Time" in OPAQUE (OMA-WV-CSP_WBXML-V1_1-20021001-A.pdf - 6.6) * * @todo Review the canonical XML generation: * - http://www.jclark.com/xml/canonxml.html * - http://www.w3.org/TR/2004/REC-xml-20040204/ */ #include /* For isdigit() */ #include "wbxml.h" /** * Compilation Flag: WBXML_ENCODER_USE_STRTBL * ----------------- * Do We Use String Table when Encoding to WBXML ? * (NOTE: We still use String Table for Unknown Public ID, even if this flag is not set) */ /* WBXML Header: version publicid charset length * u_int8 mb_u_int32 mb_u_int32 mb_u_int32 * 1 octet 5 octets 5 octets 5 octets : 16 octets * mb_u_int32: 5 octets (to handle continuation bits) */ #define WBXML_HEADER_MAX_LEN 16 /* Memory management related defines */ #define WBXML_ENCODER_XML_DOC_MALLOC_BLOCK 5000 #define WBXML_ENCODER_WBXML_DOC_MALLOC_BLOCK 1000 #define WBXML_ENCODER_XML_HEADER_MALLOC_BLOCK 250 #define WBXML_ENCODER_WBXML_HEADER_MALLOC_BLOCK WBXML_HEADER_MAX_LEN /* WBXML Default Charset: UTF-8 (106) */ #define WBXML_ENCODER_DEFAULT_CHARSET 0x6a /* String Terminating NULL Char */ #define WBXML_STR_END '\0' /* Minimum String Size needed for String Table - @note Set to '3' for Prov 1.0 */ #define WBXML_ENCODER_STRING_TABLE_MIN 3 /** * Default charset of the outputed WBXML document. Used only in this case : * - No charset was indicated thanks to the function 'wbxml_encoder_set_output_charset()' * - and the WBXML Tree field 'orig_charset' is set to WBXML_CHARSET_UNKNOWN (ie. charset * information not found in original document) */ #define WBXML_ENCODER_WBXML_DEFAULT_CHARSET WBXML_CHARSET_UTF_8 /** * Default charset of the outputed XML document. Used only in this case : * - No charset was indicated thanks to the function 'wbxml_encoder_set_output_charset()' * - and the WBXML Tree field 'orig_charset' is set to WBXML_CHARSET_UNKNOWN (ie. charset * information not found in original document) */ #define WBXML_ENCODER_XML_DEFAULT_CHARSET WBXML_CHARSET_UTF_8 /** * If defined, generate empty XML elements (eg: ), else generate * full "end element" (eg: ) * * @todo This must be a 'WBXMLGenXMLParams' parameter */ #define WBXML_ENCODER_XML_GEN_EMPTY_ELT /** * If defined, do not indent elements that have no element child (eg: bar), * else indent anyway (eg: * bar * ) * * @todo This must be a 'WBXMLGenXMLParams' parameter */ #define WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT /** * @warning For now 'current_tag' field is only used for WV Content Encoding. And for this use, it works. * But this field is reset after End Tag, and as there is no Linked List mecanism, this is bad for * cascading elements: we don't fill this field with parent Tag when parsing End Tag. * * @warning For now 'current_text_parent' field is only used for DRM REL Content Encoding. It should not be * used for another purpose. */ struct WBXMLEncoder_s { WBXMLTree *tree; /**< WBXML Tree to Encode */ const WBXMLLangEntry *lang; /**< Language table to use */ WBXMLBuffer *output; /**< The output (wbxml or xml) we are producing */ WBXMLBuffer *output_header; /**< The output header (used if Flow Mode encoding is activated) */ const WBXMLTagEntry *current_tag; /**< Current Tag (See The Warning For This Field !) */ const WBXMLTreeNode *current_text_parent; /**< Text parent of current Node (See The Warning For This Field !) */ const WBXMLAttrEntry *current_attr; /**< Current Attribute */ WB_UTINY tagCodePage; /**< Current Tag Code Page */ WB_UTINY attrCodePage; /**< Current Attribute Code Page */ WB_BOOL ignore_empty_text; /**< Do we ignore empty text nodes (ie: ignorable whitespaces)? */ WB_BOOL remove_text_blanks; /**< Do we remove leading and trailing blanks in text nodes ? */ WBXMLEncoderOutputType output_type; /**< Output Type */ WBXMLGenXMLType xml_gen_type; /**< XML Generation Type */ WB_UTINY indent_delta; /**< Indent Delta (number of spaces) */ WB_UTINY indent; /**< Current Indent */ WB_BOOL in_content; /**< We are in Content Text (used for indentation when generating XML output) */ WB_BOOL in_cdata; /**< We are in a CDATA section (and so, content must be generaed "as is") */ WBXMLBuffer *cdata; /**< Current CDATA Buffer */ #if defined( WBXML_ENCODER_USE_STRTBL ) WBXMLList *strstbl; /**< String Table we are creating */ WB_ULONG strstbl_len; /**< String Table Length */ WB_BOOL use_strtbl; /**< Do we use String Table when generating WBXML output ? (default: YES) */ #endif /* WBXML_ENCODER_USE_STRTBL */ WB_BOOL xml_encode_header; /**< Do we generate XML Header ? */ WBXMLVersion wbxml_version; /**< WBXML Version to use (when generating WBXML output) */ WBXMLCharsetMIBEnum output_charset; /**< Output charset encoding */ WB_BOOL flow_mode; /**< Is Flow Mode encoding activated ? */ WB_ULONG pre_last_node_len; /**< Output buffer length before last node encoding */ WB_BOOL textual_publicid; /**< Generate textual Public ID instead of token (when generating WBXML output) */ }; #if defined( WBXML_ENCODER_USE_STRTBL ) /** * @brief The WBXML String Table Element */ typedef struct WBXMLStringTableElement_t { WBXMLBuffer *string; /**< String */ WB_ULONG offset; /**< Offset of String in String Table */ WB_ULONG count; /**< Number of times this String is referenced in the XML Document */ WB_BOOL stat; /**< If set to TRUE, this is a static String that we must not destroy in wbxml_strtbl_element_destroy() function */ } WBXMLStringTableElement; #endif /* WBXML_ENCODER_USE_STRTBL */ /** * @brief WBXML Value Element Context: In Content or in Attribute Value */ typedef enum WBXMLValueElementCtx_e { WBXML_VALUE_ELEMENT_CTX_CONTENT = 0, /**< Text Content */ WBXML_VALUE_ELEMENT_CTX_ATTR /**< Attribute Value */ } WBXMLValueElementCtx; /** * @brief WBXML Value Element Type: string / tableref / extension / opaque */ typedef enum WBXMLValueElementType_e { WBXML_VALUE_ELEMENT_STRING = 0, /**< Inline String */ WBXML_VALUE_ELEMENT_EXTENSION, /**< Extension Token */ WBXML_VALUE_ELEMENT_OPAQUE, /**< Opaque Buffer */ WBXML_VALUE_ELEMENT_ATTR_TOKEN /**< Attribute Value Token */ #if defined( WBXML_ENCODER_USE_STRTBL ) , WBXML_VALUE_ELEMENT_TABLEREF /**< String Table Reference */ #endif /* WBXML_ENCODER_USE_STRTBL */ } WBXMLValueElementType; /** * @brief WBXML Value Element Structure */ typedef struct WBXMLValueElement_t { WBXMLValueElementType type; /**< Cf WBXMLValueElementType enum */ union { WBXMLBuffer *str; /**< WBXML_VALUE_ELEMENT_STRING */ const WBXMLExtValueEntry *ext; /**< WBXML_VALUE_ELEMENT_EXTENSION */ WBXMLBuffer *buff; /**< WBXML_VALUE_ELEMENT_OPAQUE */ const WBXMLAttrValueEntry *attr; /**< WBXML_VALUE_ELEMENT_ATTR_TOKEN */ #if defined( WBXML_ENCODER_USE_STRTBL ) WB_ULONG index; /**< WBXML_VALUE_ELEMENT_TABLEREF */ #endif /* WBXML_ENCODER_USE_STRTBL */ } u; } WBXMLValueElement; /*************************************************** * Private Functions prototypes */ /******************************* * Common Functions */ #if 0 static WB_BOOL convert_char_to_ucs4(WB_UTINY ch, WB_ULONG *result); #endif /* 0 */ static WBXMLEncoder *encoder_duplicate(WBXMLEncoder *encoder); static WBXMLError encoder_encode_tree(WBXMLEncoder *encoder); static WB_BOOL encoder_init_output(WBXMLEncoder *encoder); /******************************* * WBXML Tree Parsing Functions */ static WBXMLError parse_node(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL enc_end); static WBXMLError parse_element(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content); static WBXMLError parse_element_end(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content); static WBXMLError parse_attribute(WBXMLEncoder *encoder, WBXMLAttribute *attribute); static WBXMLError parse_text(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WBXMLError parse_cdata(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WBXMLError parse_pi(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WBXMLError parse_tree(WBXMLEncoder *encoder, WBXMLTreeNode *node); /******************************* * WBXML Output Functions */ /* Build WBXML Result */ static WBXMLError wbxml_build_result(WBXMLEncoder *encoder, WB_UTINY **wbxml, WB_ULONG *wbxml_len); static WBXMLError wbxml_fill_header(WBXMLEncoder *encoder, WBXMLBuffer *header); /* WBXML Encoding Functions */ static WBXMLError wbxml_encode_end(WBXMLEncoder *encoder); static WBXMLError wbxml_encode_tag(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content); static WBXMLError wbxml_encode_tag_literal(WBXMLEncoder *encoder, WB_UTINY *tag, WB_UTINY mask); static WBXMLError wbxml_encode_tag_token(WBXMLEncoder *encoder, WB_UTINY token, WB_UTINY page); static WBXMLError wbxml_encode_attr(WBXMLEncoder *encoder, WBXMLAttribute *attribute); static WBXMLError wbxml_encode_attr_start(WBXMLEncoder *encoder, WBXMLAttribute *attribute, WB_UTINY **value); static WBXMLError wbxml_encode_value_element_buffer(WBXMLEncoder *encoder, WB_UTINY *value, WBXMLValueElementCtx ctx); static WBXMLError wbxml_encode_value_element_list(WBXMLEncoder *encoder, WBXMLList *list); static WBXMLError wbxml_encode_attr_start_literal(WBXMLEncoder *encoder, const WB_UTINY *attr); static WBXMLError wbxml_encode_attr_token(WBXMLEncoder *encoder, WB_UTINY token, WB_UTINY page); static WBXMLError wbxml_encode_inline_string(WBXMLEncoder *encoder, WBXMLBuffer *str); static WBXMLError wbxml_encode_inline_integer_extension_token(WBXMLEncoder *encoder, WB_UTINY ext, WB_UTINY value); #if 0 static WBXMLError wbxml_encode_entity(WBXMLEncoder *encoder, WB_ULONG value); #endif /* 0 */ static WBXMLError wbxml_encode_opaque(WBXMLEncoder *encoder, WBXMLBuffer *buff); #if defined( WBXML_ENCODER_USE_STRTBL ) static WBXMLError wbxml_encode_tableref(WBXMLEncoder *encoder, WB_ULONG offset); #endif /* WBXML_ENCODER_USE_STRTBL */ static WBXMLValueElement *wbxml_value_element_create(void); static void wbxml_value_element_destroy(WBXMLValueElement *elt); static void wbxml_value_element_destroy_item(void *elt); static WBXMLError wbxml_encode_tree(WBXMLEncoder *encoder, WBXMLTree *tree); #if ( defined( WBXML_SUPPORT_SI ) || defined( WBXML_SUPPORT_EMN ) ) static WBXMLError wbxml_encode_datetime(WBXMLEncoder *encoder, WB_UTINY *buffer); #endif /* WBXML_SUPPORT_SI || WBXML_SUPPORT_EMN */ #if defined( WBXML_SUPPORT_WV ) static WBXMLError wbxml_encode_wv_content(WBXMLEncoder *encoder, WB_UTINY *buffer); static WBXMLError wbxml_encode_wv_integer(WBXMLEncoder *encoder, WB_UTINY *buffer); static WBXMLError wbxml_encode_wv_datetime(WBXMLEncoder *encoder, WB_UTINY *buffer); #endif /* WBXML_SUPPORT_WV */ #if defined( WBXML_SUPPORT_DRMREL ) static WBXMLError wbxml_encode_drmrel_content(WBXMLEncoder *encoder, WB_UTINY *buffer); #endif /* WBXML_SUPPORT_DRMREL */ #if defined( WBXML_ENCODER_USE_STRTBL ) /* WBXML String Table Functions */ static WBXMLStringTableElement *wbxml_strtbl_element_create(WBXMLBuffer *string, WB_BOOL is_stat); static void wbxml_strtbl_element_destroy(WBXMLStringTableElement *element); static void wbxml_strtbl_element_destroy_item(void *element); static WBXMLError wbxml_strtbl_initialize(WBXMLEncoder *encoder, WBXMLTreeNode *root); static void wbxml_strtbl_collect_strings(WBXMLEncoder *encoder, WBXMLTreeNode *node, WBXMLList *strings); static WBXMLError wbxml_strtbl_collect_words(WBXMLList *elements, WBXMLList **result); static WBXMLError wbxml_strtbl_construct(WBXMLBuffer *buff, WBXMLList *strstbl); static WBXMLError wbxml_strtbl_check_references(WBXMLEncoder *encoder, WBXMLList **strings, WBXMLList **one_ref, WB_BOOL stat_buff); static WB_BOOL wbxml_strtbl_add_element(WBXMLEncoder *encoder, WBXMLStringTableElement *elt, WB_ULONG *index, WB_BOOL *added); #endif /* WBXML_ENCODER_USE_STRTBL */ /******************************* * XML Output Functions */ /** New Line */ #define WBXML_ENCODER_XML_NEW_LINE ((WB_UTINY *)"\n") /* XML Header Macros */ #define WBXML_ENCODER_XML_HEADER "" #define WBXML_ENCODER_XML_DOCTYPE "" /* Global vars for XML Normalization */ const WB_UTINY xml_lt[5] = "<"; /**< < */ const WB_UTINY xml_gt[5] = ">"; /**< > */ const WB_UTINY xml_amp[6] = "&"; /**< & */ const WB_UTINY xml_quot[7] = """; /**< " */ const WB_UTINY xml_apos[7] = "'"; /**< ' */ const WB_UTINY xml_slashr[6] = " "; /**< */ const WB_UTINY xml_slashn[6] = " "; /**< */ const WB_UTINY xml_tab[5] = " "; /**< */ /* Build XML Result */ static WBXMLError xml_build_result(WBXMLEncoder *encoder, WB_UTINY **xml, WB_ULONG *xml_len); static WBXMLError xml_fill_header(WBXMLEncoder *encoder, WBXMLBuffer *header); /* XML Encoding Functions */ static WBXMLError xml_encode_tag(WBXMLEncoder *encoer, WBXMLTreeNode *node); static WBXMLError xml_encode_end_tag(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WBXMLError xml_encode_attr(WBXMLEncoder *encoder, WBXMLAttribute *attribute); static WBXMLError xml_encode_end_attrs(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WBXMLError xml_encode_text(WBXMLEncoder *encoder, WBXMLTreeNode *node); static WB_BOOL xml_encode_new_line(WBXMLBuffer *buff); static WB_BOOL xml_fix_text(WBXMLBuffer *buff, WB_BOOL normalize); static WBXMLError xml_encode_cdata(WBXMLEncoder *encoder); static WBXMLError xml_encode_end_cdata(WBXMLEncoder *encoder); static WBXMLError xml_encode_tree(WBXMLEncoder *encoder, WBXMLTree *tree); /*************************************************** * Public Functions */ WBXML_DECLARE(WBXMLEncoder *) wbxml_encoder_create_real(void) { WBXMLEncoder *encoder = NULL; encoder = (WBXMLEncoder *) wbxml_malloc(sizeof(WBXMLEncoder)); if (encoder == NULL) { return NULL; } #if defined( WBXML_ENCODER_USE_STRTBL ) if ((encoder->strstbl = wbxml_list_create()) == NULL) { wbxml_free(encoder); return NULL; } encoder->use_strtbl = TRUE; encoder->strstbl_len = 0; #endif /* WBXML_ENCODER_USE_STRTBL */ encoder->tree = NULL; encoder->lang = NULL; encoder->output = NULL; encoder->output_header = NULL; encoder->current_tag = NULL; encoder->current_text_parent = NULL; encoder->current_attr = NULL; encoder->tagCodePage = 0; encoder->attrCodePage = 0; encoder->ignore_empty_text = FALSE; encoder->remove_text_blanks = FALSE; encoder->output_type = WBXML_ENCODER_OUTPUT_WBXML; encoder->xml_gen_type = WBXML_GEN_XML_COMPACT; encoder->indent_delta = 1; encoder->indent = 0; encoder->in_content = FALSE; encoder->in_cdata = FALSE; encoder->cdata = NULL; encoder->xml_encode_header = TRUE; /* Default Version: WBXML 1.3 */ encoder->wbxml_version = WBXML_VERSION_13; /** * Default Output Charset Encoding is the one found in WBXML Tree, * so set it as 'unknown' for now. */ encoder->output_charset = WBXML_CHARSET_UNKNOWN; encoder->flow_mode = FALSE; encoder->pre_last_node_len = 0; encoder->textual_publicid = FALSE; return encoder; } WBXML_DECLARE(void) wbxml_encoder_destroy(WBXMLEncoder *encoder) { if (encoder == NULL) return; wbxml_buffer_destroy(encoder->output); wbxml_buffer_destroy(encoder->output_header); wbxml_buffer_destroy(encoder->cdata); #if defined( WBXML_ENCODER_USE_STRTBL ) wbxml_list_destroy(encoder->strstbl, wbxml_strtbl_element_destroy_item); #endif /* WBXML_ENCODER_USE_STRTBL */ wbxml_free(encoder); } WBXML_DECLARE(void) wbxml_encoder_reset(WBXMLEncoder *encoder) { if (encoder == NULL) return; encoder->tree = NULL; wbxml_buffer_destroy(encoder->output); encoder->output = NULL; wbxml_buffer_destroy(encoder->output_header); encoder->output_header = NULL; encoder->current_tag = NULL; encoder->current_attr = NULL; encoder->tagCodePage = 0; encoder->attrCodePage = 0; encoder->in_content = FALSE; encoder->in_cdata = FALSE; wbxml_buffer_destroy(encoder->cdata); encoder->cdata = NULL; encoder->pre_last_node_len = 0; #if defined( WBXML_ENCODER_USE_STRTBL ) wbxml_list_destroy(encoder->strstbl, wbxml_strtbl_element_destroy_item); encoder->strstbl = NULL; encoder->strstbl_len = 0; #endif /* WBXML_ENCODER_USE_STRTBL */ } WBXML_DECLARE(void) wbxml_encoder_set_ignore_empty_text(WBXMLEncoder *encoder, WB_BOOL set_ignore) { if (encoder == NULL) return; encoder->ignore_empty_text = set_ignore; } WBXML_DECLARE(void) wbxml_encoder_set_remove_text_blanks(WBXMLEncoder *encoder, WB_BOOL set_remove) { if (encoder == NULL) return; encoder->remove_text_blanks = set_remove; } WBXML_DECLARE(void) wbxml_encoder_set_output_charset(WBXMLEncoder *encoder, WBXMLCharsetMIBEnum charset) { if (encoder == NULL) return; /* Tell which Output Charset Encoding to use (this overides the Charset Encoding found in WBXML Tree) */ encoder->output_charset = charset; } WBXML_DECLARE(void) wbxml_encoder_set_use_strtbl(WBXMLEncoder *encoder, WB_BOOL use_strtbl) { #if defined( WBXML_ENCODER_USE_STRTBL ) if (encoder == NULL) return; encoder->use_strtbl = use_strtbl; #endif /* WBXML_ENCODER_USE_STRTBL */ } WBXML_DECLARE(void) wbxml_encoder_set_wbxml_version(WBXMLEncoder *encoder, WBXMLVersion version) { if (encoder == NULL) return; if (version != WBXML_VERSION_UNKNOWN) encoder->wbxml_version = version; } WBXML_DECLARE(void) wbxml_encoder_set_xml_gen_type(WBXMLEncoder *encoder, WBXMLGenXMLType gen_type) { if (encoder == NULL) return; encoder->xml_gen_type = gen_type; } WBXML_DECLARE(void) wbxml_encoder_set_indent(WBXMLEncoder *encoder, WB_UTINY indent) { if (encoder == NULL) return; encoder->indent_delta = indent; } WBXML_DECLARE(void) wbxml_encoder_set_tree(WBXMLEncoder *encoder, WBXMLTree *tree) { if (encoder == NULL) return; encoder->tree = tree; } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_tree_to_wbxml(WBXMLEncoder *encoder, WB_UTINY **wbxml, WB_ULONG *wbxml_len) { WBXMLError ret = WBXML_OK; /* Check Parameters */ if (encoder == NULL) return WBXML_ERROR_BAD_PARAMETER; /* Init ret values */ *wbxml = NULL; *wbxml_len = 0; /* We output WBXML */ wbxml_encoder_set_output_type(encoder, WBXML_ENCODER_OUTPUT_WBXML); /* Encode */ if ((ret = encoder_encode_tree(encoder)) != WBXML_OK) return ret; /* Get result */ return wbxml_encoder_get_output(encoder, wbxml, wbxml_len); } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_tree_to_xml(WBXMLEncoder *encoder, WB_UTINY **xml, WB_ULONG *xml_len) { WBXMLError ret = WBXML_OK; /* Check Parameters */ if (encoder == NULL) return WBXML_ERROR_BAD_PARAMETER; /* Init ret values */ *xml = NULL; *xml_len = 0; /* We output WBXML */ wbxml_encoder_set_output_type(encoder, WBXML_ENCODER_OUTPUT_XML); /* Encode */ if ((ret = encoder_encode_tree(encoder)) != WBXML_OK) return ret; /* Get result */ return wbxml_encoder_get_output(encoder, xml, xml_len); } WBXML_DECLARE(WBXMLError) wbxml_encoder_set_flow_mode(WBXMLEncoder *encoder, WB_BOOL flow_mode) { if (encoder == NULL) return WBXML_ERROR_BAD_PARAMETER; encoder->flow_mode = TRUE; /* Don't use String Tables */ wbxml_encoder_set_use_strtbl(encoder, FALSE); return WBXML_OK; } WBXML_DECLARE(void) wbxml_encoder_set_output_type(WBXMLEncoder *encoder, WBXMLEncoderOutputType output_type) { if (encoder == NULL) return; encoder->output_type = output_type; } WBXML_DECLARE(void) wbxml_encoder_set_lang(WBXMLEncoder *encoder, WBXMLLanguage lang) { if (encoder == NULL) return; encoder->lang = wbxml_tables_get_table(lang); } WBXML_DECLARE(void) wbxml_encoder_set_text_public_id(WBXMLEncoder *encoder, WB_BOOL gen_text) { if (encoder == NULL) return; encoder->textual_publicid = gen_text; } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_node(WBXMLEncoder *encoder, WBXMLTreeNode *node) { return wbxml_encoder_encode_node_with_elt_end(encoder, node, TRUE); } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_node_with_elt_end(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL enc_end) { WB_ULONG prev_len = 0; WBXMLError ret = WBXML_OK; if ((encoder == NULL) || (node == NULL)) return WBXML_ERROR_BAD_PARAMETER; if (encoder->flow_mode == FALSE) { WBXML_WARNING((WBXML_ENCODER, "You should NOT call wbxml_encoder_encode_node() if you are not in Flow Mode encoding ! (use wbxml_encoder_set_flow_mode(encoder, TRUE))")); } /* Check that language table has been set */ if (encoder->lang == NULL) return WBXML_ERROR_BAD_PARAMETER; /* Init Output Buffer if needed */ if (!encoder_init_output(encoder)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Backup length */ prev_len = wbxml_buffer_len(encoder->output); /* Check if result header is not already built */ if ((encoder->flow_mode == TRUE) && (encoder->output_header == NULL) && !((encoder->xml_encode_header == FALSE) && (encoder->output_type == WBXML_ENCODER_OUTPUT_XML))) { /* Build result header */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_XML: if ((encoder->output_header = wbxml_buffer_create("", 0, WBXML_ENCODER_XML_HEADER_MALLOC_BLOCK)) == NULL) ret = WBXML_ERROR_NOT_ENOUGH_MEMORY; else ret = xml_fill_header(encoder, encoder->output_header); break; case WBXML_ENCODER_OUTPUT_WBXML: if ((encoder->output_header = wbxml_buffer_create("", 0, WBXML_ENCODER_WBXML_HEADER_MALLOC_BLOCK)) == NULL) ret = WBXML_ERROR_NOT_ENOUGH_MEMORY; else ret = wbxml_fill_header(encoder, encoder->output_header); break; default: ret = WBXML_ERROR_BAD_PARAMETER; break; } } if (ret != WBXML_OK) return ret; if ((ret = parse_node(encoder, node, enc_end)) == WBXML_OK) encoder->pre_last_node_len = prev_len; return ret; } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_tree(WBXMLEncoder *encoder, WBXMLTree *tree) { const WBXMLLangEntry *lang = NULL; WBXMLError ret = WBXML_OK; if ((encoder == NULL) || (tree == NULL)) return WBXML_ERROR_BAD_PARAMETER; /* Backup encoder lang */ lang = encoder->lang; /* Set Tree lang to encoder */ encoder->lang = tree->lang; /* Encode root node */ ret = wbxml_encoder_encode_node(encoder, tree->root); /* Revert encoder lang */ encoder->lang = lang; return ret; } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_raw_elt_start(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content) { /* Init Output Buffer if needed */ if (!encoder_init_output(encoder)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; return parse_element(encoder, node, has_content); } WBXML_DECLARE(WBXMLError) wbxml_encoder_encode_raw_elt_end(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content) { /* Init Output Buffer if needed */ if (!encoder_init_output(encoder)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; return parse_element_end(encoder, node, has_content); } WBXML_DECLARE(WBXMLError) wbxml_encoder_get_output(WBXMLEncoder *encoder, WB_UTINY **result, WB_ULONG *result_len) { if ((encoder == NULL) || (result == NULL) || (result_len == NULL)) return WBXML_ERROR_BAD_PARAMETER; switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_XML: return xml_build_result(encoder, result, result_len); case WBXML_ENCODER_OUTPUT_WBXML: return wbxml_build_result(encoder, result, result_len); default: return WBXML_ERROR_BAD_PARAMETER; } } WBXML_DECLARE(WB_ULONG) wbxml_encoder_get_output_len(WBXMLEncoder *encoder) { if (encoder == NULL) return 0; return wbxml_buffer_len(encoder->output_header) + wbxml_buffer_len(encoder->output); } WBXML_DECLARE(void) wbxml_encoder_delete_output_bytes(WBXMLEncoder *encoder, WB_ULONG nb) { if (encoder == NULL) return; wbxml_buffer_delete(encoder->output, wbxml_buffer_len(encoder->output) - nb, nb); } WBXML_DECLARE(void) wbxml_encoder_delete_last_node(WBXMLEncoder *encoder) { if (encoder == NULL) return; wbxml_encoder_delete_output_bytes(encoder, wbxml_buffer_len(encoder->output) - encoder->pre_last_node_len); } /*************************************************** * Private Functions */ /**************************** * Common Functions */ #if 0 /** * @brief Convert a char to UCS-4 * @param ch [in] The character to convert * @param result [out] The UCS-4 code * @return TRUE if convertion succeeded, FALSE otherwise */ static WB_BOOL convert_char_to_ucs4(WB_UTINY ch, WB_ULONG *result) { /** @todo convert_char_to_ucs4() */ return FALSE; } #endif /* 0 */ /** * @brief Duplicate a WBXML Encoder * @param encoder [in] The WBXML Encoder to Duplicate * @return The duplicated WBXML Encoder, or NULL if error * @note Only options (parameters) fields are duplicated, others are reset */ static WBXMLEncoder *encoder_duplicate(WBXMLEncoder *encoder) { WBXMLEncoder *result = NULL; if ((result = wbxml_encoder_create()) == NULL) return NULL; result->ignore_empty_text = encoder->ignore_empty_text; result->remove_text_blanks = encoder->remove_text_blanks; result->output_type = encoder->output_type; result->xml_gen_type = encoder->xml_gen_type; result->indent_delta = encoder->indent_delta; result->indent = encoder->indent; #if defined( WBXML_ENCODER_USE_STRTBL ) result->use_strtbl = encoder->use_strtbl; #endif /* WBXML_ENCODER_USE_STRTBL */ /* Do NOT generate XML Header */ result->xml_encode_header = FALSE; result->wbxml_version = encoder->wbxml_version; return result; } static WBXMLError encoder_encode_tree(WBXMLEncoder *encoder) { WBXMLError ret = WBXML_OK; /* Check Parameters */ if ((encoder == NULL) || (encoder->tree == NULL) || ((encoder->lang == NULL) && (encoder->tree->lang == NULL)) || ((encoder->output_type != WBXML_ENCODER_OUTPUT_XML) && (encoder->output_type != WBXML_ENCODER_OUTPUT_WBXML))) { return WBXML_ERROR_BAD_PARAMETER; } if (encoder->lang == NULL) encoder->lang = encoder->tree->lang; /* Choose Output Charset */ if (encoder->output_charset == WBXML_CHARSET_UNKNOWN) { /* User has not choosen the Output Charset Encoding */ if (encoder->tree->orig_charset != WBXML_CHARSET_UNKNOWN) { /* Use the original Charset Encoding found when we have parsed the original document */ encoder->output_charset = encoder->tree->orig_charset; } else { /* Use default charset */ encoder->output_charset = WBXML_ENCODER_XML_DEFAULT_CHARSET; } } /* Init Output Buffer */ if (!encoder_init_output(encoder)) { wbxml_encoder_destroy(encoder); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } #if defined( WBXML_ENCODER_USE_STRTBL ) if (encoder->output_type == WBXML_ENCODER_OUTPUT_WBXML) { /* Choose if we will use String Table */ switch (encoder->lang->langID) { #if defined( WBXML_SUPPORT_WV ) /* Wireless-Village CSP 1.1 / 1.2: content can be tokenized, so we mustn't interfere with String Table stuff */ case WBXML_LANG_WV_CSP11: case WBXML_LANG_WV_CSP12: encoder->use_strtbl = FALSE; break; #endif /* WBXML_SUPPORT_WV */ #if defined( WBXML_SUPPORT_OTA_SETTINGS ) /* Nokia Ericsson OTA Settings : string tables are not supported */ case WBXML_LANG_OTA_SETTINGS: encoder->use_strtbl = FALSE; break; #endif /* WBXML_SUPPORT_OTA_SETTINGS */ default: /* Use Default Value */ break; } /* Init String Table */ if (encoder->use_strtbl) { /** * @bug If 'output_charset' is different from UTF-8, the string table initialization * also is erroneous !!! */ if ((ret = wbxml_strtbl_initialize(encoder, encoder->tree->root)) != WBXML_OK) return ret; } } #endif /* WBXML_ENCODER_USE_STRTBL */ /* Let's begin WBXML Tree Parsing */ return parse_node(encoder, encoder->tree->root, TRUE); } static WB_BOOL encoder_init_output(WBXMLEncoder *encoder) { WB_ULONG malloc_block = 0; if (encoder == NULL) return FALSE; /* Check if output already inited */ if (encoder->output != NULL) return TRUE; /* Get malloc block */ if (encoder->output_type == WBXML_ENCODER_OUTPUT_WBXML) malloc_block = WBXML_ENCODER_WBXML_DOC_MALLOC_BLOCK; else malloc_block = WBXML_ENCODER_XML_DOC_MALLOC_BLOCK; /* Init Output Buffer */ encoder->output = wbxml_buffer_create("", 0, malloc_block); if (encoder->output == NULL) return FALSE; return TRUE; } /********************************* * WBXML Tree Parsing Functions */ /** * @brief Parse an XML Node * @param encoder The WBXML Encoder * @param node The node to parse * @param enc_end If node is an element, do we encoded its end ? * @return WBXML_OK if parsing is OK, an error code otherwise * @note We have recurrency in this function */ static WBXMLError parse_node(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL enc_end) { WBXMLError ret = WBXML_OK; /* Parse this node */ switch (node->type) { case WBXML_TREE_ELEMENT_NODE: ret = parse_element(encoder, node, node->children != NULL); break; case WBXML_TREE_TEXT_NODE: ret = parse_text(encoder, node); break; case WBXML_TREE_CDATA_NODE: ret = parse_cdata(encoder, node); break; case WBXML_TREE_PI_NODE: ret = parse_pi(encoder, node); break; case WBXML_TREE_TREE_NODE: ret = parse_tree(encoder, node); break; default: return WBXML_ERROR_XML_NODE_NOT_ALLOWED; } if (ret != WBXML_OK) return ret; /* Check if node has children */ if (node->children != NULL) { /* Parse Child */ if ((ret = parse_node(encoder, node->children, TRUE)) != WBXML_OK) return ret; } /* Handle end of Element or CDATA section */ switch (node->type) { case WBXML_TREE_ELEMENT_NODE: if (enc_end) { switch(encoder->output_type) { case WBXML_ENCODER_OUTPUT_XML: #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) if (node->children != NULL) { #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ /* Encode end tag */ if ((ret = xml_encode_end_tag(encoder, node)) != WBXML_OK) return ret; WBXML_DEBUG((WBXML_ENCODER, "End Element")); #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) } #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ break; case WBXML_ENCODER_OUTPUT_WBXML: if (node->children != NULL) { /* Add a WBXML End tag */ if ((ret = wbxml_encode_end(encoder)) != WBXML_OK) return ret; WBXML_DEBUG((WBXML_ENCODER, "End Element")); } break; default: /* hu ? */ break; } /* switch */ } /* if */ break; case WBXML_TREE_CDATA_NODE: /* End of CDATA section */ encoder->in_cdata = FALSE; WBXML_DEBUG((WBXML_ENCODER, "End CDATA")); switch(encoder->output_type) { case WBXML_ENCODER_OUTPUT_XML: /* Encode XML "End of CDATA section" */ if ((ret = xml_encode_end_cdata(encoder)) != WBXML_OK) return ret; break; case WBXML_ENCODER_OUTPUT_WBXML: if (encoder->cdata == NULL) { /* Must never happen */ return WBXML_ERROR_INTERNAL; } /* Encode CDATA Buffer into Opaque */ if (wbxml_buffer_len(encoder->cdata) > 0) { if ((ret = wbxml_encode_opaque(encoder, encoder->cdata)) != WBXML_OK) return ret; } /* Reset CDATA Buffer */ wbxml_buffer_destroy(encoder->cdata); encoder->cdata = NULL; break; default: /* hu ? */ break; } /* switch */ break; default: /* NOP */ break; } /* Reset Current Tag */ encoder->current_tag = NULL; /* Parse next node */ if (node->next != NULL) return parse_node(encoder, node->next, TRUE); else return WBXML_OK; } /** * @brief Parse an XML Element * @param encoder The WBXML Encoder * @param node The element to parse * @param has_content Does the element has content ? * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_element(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content) { WB_ULONG i = 0; WBXMLError ret = WBXML_OK; WBXML_DEBUG((WBXML_ENCODER, "Element: <%s>", wbxml_tag_get_xml_name(node->name))); /* Encode: Element Name */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: if ((ret = wbxml_encode_tag(encoder, node, has_content)) != WBXML_OK) return ret; break; case WBXML_ENCODER_OUTPUT_XML: if ((ret = xml_encode_tag(encoder, node)) != WBXML_OK) return ret; break; default: return WBXML_ERROR_INTERNAL; } /** @todo Check handling of Namespaces */ /** @todo For Canonical XML Output: Attributes MUST be sorted */ /* Parse: Attributes List */ if (node->attrs != NULL) { for (i = 0; i < wbxml_list_len(node->attrs); i++) { /* Parse: Attribute */ if ((ret = parse_attribute(encoder, wbxml_list_get(node->attrs, i))) != WBXML_OK) return ret; } } /* Encode: End of attributes */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: /** @todo Check if the attributes will really be tokenized. There can be ignored attributes, and so NO attribute * tokenized at all. */ if ((node->attrs != NULL) && (encoder->lang->attrTable != NULL) /** @todo Fast patch for SyncML (change this) */) { if ((ret = wbxml_encode_end(encoder)) != WBXML_OK) return ret; WBXML_DEBUG((WBXML_ENCODER, "End Attributes")); } break; case WBXML_ENCODER_OUTPUT_XML: /* Encode end of attributes */ if ((ret = xml_encode_end_attrs(encoder, node)) != WBXML_OK) return ret; WBXML_DEBUG((WBXML_ENCODER, "End Attributes")); break; default: return WBXML_ERROR_INTERNAL; } return WBXML_OK; } /** * @brief Parse an Element End * @param encoder The WBXML Encoder * @param node The element to parse * @param has_content Does the element has content ? * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_element_end(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content) { WBXMLError ret = WBXML_OK; if (encoder->output_type == WBXML_ENCODER_OUTPUT_XML) { #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) if (has_content) { #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ /* Encode end tag */ ret = xml_encode_end_tag(encoder, node); WBXML_DEBUG((WBXML_ENCODER, "End Element")); #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) } #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ } else if (encoder->output_type == WBXML_ENCODER_OUTPUT_WBXML) { if (has_content) { /* Add a WBXML End tag */ ret = wbxml_encode_end(encoder); WBXML_DEBUG((WBXML_ENCODER, "End Element")); } } return ret; } /** * @brief Parse an XML Attribute * @param encoder The WBXML Encoder * @param attribute The XML Attribute to parse * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_attribute(WBXMLEncoder *encoder, WBXMLAttribute *attribute) { if (encoder->lang == NULL) return WBXML_ERROR_LANG_TABLE_UNDEFINED; if (encoder->lang->attrTable == NULL) return WBXML_OK; /* Check that this attribute has a name */ if (attribute->name == NULL) return WBXML_ERROR_XML_NULL_ATTR_NAME; WBXML_DEBUG((WBXML_ENCODER, "Attribute: %s = %s", wbxml_attribute_get_xml_name(attribute), wbxml_attribute_get_xml_value(attribute))); /* Encode: Attribute */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: return wbxml_encode_attr(encoder, attribute); case WBXML_ENCODER_OUTPUT_XML: return xml_encode_attr(encoder, attribute); default: return WBXML_ERROR_INTERNAL; } } /** * @brief Parse an XML Text * @param encoder The WBXML Encoder * @param node The text to parse * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_text(WBXMLEncoder *encoder, WBXMLTreeNode *node) { WBXMLError ret = WBXML_OK; /* Do not modify text inside a CDATA section */ if (!encoder->in_cdata) { /* If Canonical Form: "Ignorable white space is considered significant and is treated equivalently to data" */ if ((encoder->output_type != WBXML_ENCODER_OUTPUT_XML) || (encoder->xml_gen_type != WBXML_GEN_XML_CANONICAL)) { /* Ignore blank nodes */ if ((encoder->ignore_empty_text) && (wbxml_buffer_contains_only_whitespaces(node->content))) return WBXML_OK; /* Strip Blanks */ if (encoder->remove_text_blanks) wbxml_buffer_strip_blanks(node->content); } } WBXML_DEBUG((WBXML_ENCODER, "Text: <%s>", wbxml_buffer_get_cstr(node->content))); /* Encode Text */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: if (encoder->in_cdata) { if (encoder->cdata == NULL) { /* Must never happen */ return WBXML_ERROR_INTERNAL; } #if defined( WBXML_SUPPORT_SYNCML ) if ((encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML10) || (encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML11) || (encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML12)) { /** @todo We suppose that Opaque Data in SyncML messages can only be vCard or vCal documents. CHANGE THAT ! */ if (node->content != NULL) { if (wbxml_buffer_get_cstr(node->content)[0] == 0x0a && wbxml_buffer_len(node->content) == 1) { wbxml_buffer_insert_cstr(node->content, (WB_UTINY*) "\r", 0); } } } #endif /* WBXML_SUPPORT_SYNCML */ /* Add text into CDATA Buffer */ if (!wbxml_buffer_append(encoder->cdata, node->content)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } else { /* Encode text */ encoder->current_text_parent = node->parent; ret = wbxml_encode_value_element_buffer(encoder, wbxml_buffer_get_cstr(node->content), WBXML_VALUE_ELEMENT_CTX_CONTENT); encoder->current_text_parent = NULL; return ret; } case WBXML_ENCODER_OUTPUT_XML: return xml_encode_text(encoder, node); default: return WBXML_ERROR_INTERNAL; } } /** * @brief Parse an XML CDATA * @param encoder The WBXML Encoder * @param node The CDATA to parse * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_cdata(WBXMLEncoder *encoder, WBXMLTreeNode *node) { WBXML_DEBUG((WBXML_ENCODER, "CDATA Begin")); /* Keep in mind that we are in a CDATA section */ encoder->in_cdata = TRUE; /* Encode CDATA */ switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: if (encoder->cdata != NULL) { /* Must never happend */ return WBXML_ERROR_INTERNAL; } /* Create a new CDATA Buffer */ if ((encoder->cdata = wbxml_buffer_create("", 0, 0)) == NULL) { return WBXML_ERROR_NOT_ENOUGH_MEMORY; } return WBXML_OK; case WBXML_ENCODER_OUTPUT_XML: return xml_encode_cdata(encoder); default: return WBXML_ERROR_INTERNAL; } } /** * @brief Parse an XML PI * @param encoder The WBXML Encoder * @param node The PI to parse * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_pi(WBXMLEncoder *encoder, WBXMLTreeNode *node) { /** @todo parse_pi() */ return WBXML_ERROR_NOT_IMPLEMENTED; } /** * @brief Parse a WBXML Tree * @param encoder The WBXML Encoder * @param node The WBXML Tree to parse * @return WBXML_OK if parsing is OK, an error code otherwise */ static WBXMLError parse_tree(WBXMLEncoder *encoder, WBXMLTreeNode *node) { switch (encoder->output_type) { case WBXML_ENCODER_OUTPUT_WBXML: return wbxml_encode_tree(encoder, node->tree); case WBXML_ENCODER_OUTPUT_XML: return xml_encode_tree(encoder, node->tree); default: return WBXML_ERROR_INTERNAL; } } /***************************************** * WBXML Output Functions */ /**************************** * Build WBXML Result */ /** * @brief Build WBXML Result * @param encoder [in] The WBXML Encoder * @param wbxml [out] Resulting WBXML document * @param wbxml_len [out] The resulting WBXML document length * @return WBXML_OK if built is OK, an error code otherwise * @note WBXML Header = version publicid charset length */ static WBXMLError wbxml_build_result(WBXMLEncoder *encoder, WB_UTINY **wbxml, WB_ULONG *wbxml_len) { WBXMLBuffer *header = NULL; WBXMLError ret = WBXML_OK; if (encoder->flow_mode == TRUE) { /* Header already built */ header = encoder->output_header; } else { /* Create WBXML Header buffer */ if ((header = wbxml_buffer_create("", 0, WBXML_ENCODER_WBXML_HEADER_MALLOC_BLOCK)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Fill Header Buffer */ if ((ret = wbxml_fill_header(encoder, header)) != WBXML_OK) { wbxml_buffer_destroy(header); return ret; } } /* Result Buffer Length */ *wbxml_len = wbxml_buffer_len(header) + wbxml_buffer_len(encoder->output); /* Create Result Buffer */ *wbxml = (WB_UTINY *) wbxml_malloc(*wbxml_len * sizeof(WB_UTINY)); if (*wbxml == NULL) { if (encoder->flow_mode == FALSE) wbxml_buffer_destroy(header); *wbxml_len = 0; return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Copy WBXML Header */ memcpy(*wbxml, wbxml_buffer_get_cstr(header), wbxml_buffer_len(header)); /* Copy WBXML Buffer */ memcpy(*wbxml + wbxml_buffer_len(header), wbxml_buffer_get_cstr(encoder->output), wbxml_buffer_len(encoder->output)); if (encoder->flow_mode == FALSE) wbxml_buffer_destroy(header); return WBXML_OK; } static WBXMLError wbxml_fill_header(WBXMLEncoder *encoder, WBXMLBuffer *header) { WBXMLBuffer *pid = NULL; WB_ULONG public_id = 0, public_id_index = 0, strstbl_len = 0; WB_BOOL pi_in_strtbl = FALSE; WBXMLError ret = WBXML_OK; #if defined( WBXML_ENCODER_USE_STRTBL ) WBXMLStringTableElement *elt = NULL; WB_BOOL added = FALSE; strstbl_len = encoder->strstbl_len; #endif /* WBXML_ENCODER_USE_STRTBL */ if ((encoder == NULL) || (encoder->lang == NULL) || (encoder->lang->publicID == NULL) || (header == NULL)) return WBXML_ERROR_BAD_PARAMETER; /* WBXML Public ID */ public_id = encoder->lang->publicID->wbxmlPublicID; /* Encode WBXML Version */ if (!wbxml_buffer_append_char(header, (WB_UTINY) encoder->wbxml_version)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Encode Public ID */ /* If WBXML Public Id is '0x01' (unknown), or we forced it, add the XML Public ID in the String Table */ if (encoder->textual_publicid || (public_id == WBXML_PUBLIC_ID_UNKNOWN)) { if (encoder->lang->publicID->xmlPublicID != NULL) { if ((pid = wbxml_buffer_create(encoder->lang->publicID->xmlPublicID, WBXML_STRLEN(encoder->lang->publicID->xmlPublicID), WBXML_STRLEN(encoder->lang->publicID->xmlPublicID))) == NULL) { return WBXML_ERROR_NOT_ENOUGH_MEMORY; } #if defined( WBXML_ENCODER_USE_STRTBL ) if (encoder->use_strtbl) { if ((elt = wbxml_strtbl_element_create(pid, FALSE)) == NULL) { wbxml_buffer_destroy(pid); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } if (!wbxml_strtbl_add_element(encoder, elt, &public_id_index, &added)) { wbxml_strtbl_element_destroy(elt); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } if (!added) wbxml_strtbl_element_destroy(elt); strstbl_len = encoder->strstbl_len; } else { #endif /* WBXML_ENCODER_USE_STRTBL */ /* Length of String Table is length of XML Public ID */ strstbl_len = wbxml_buffer_len(pid); /* There is only the XML Public ID in String Table */ public_id_index = 0; #if defined( WBXML_ENCODER_USE_STRTBL ) } #endif /* WBXML_ENCODER_USE_STRTBL */ pi_in_strtbl = TRUE; } } /* publicid = mb_u_int32 | ( zero index ) */ if (pi_in_strtbl) { /* Encode XML Public ID String Table index */ if (!wbxml_buffer_append_char(header, 0x00) || !wbxml_buffer_append_mb_uint_32(header, public_id_index)) { return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } else { /* Encode WBXML Public ID */ if (!wbxml_buffer_append_mb_uint_32(header, public_id)) return WBXML_ERROR_ENCODER_APPEND_DATA; } /* Encode Charset (default: UTF-8) and String Table Length */ /** @todo Handle correctly the charset */ if (!wbxml_buffer_append_mb_uint_32(header, WBXML_ENCODER_DEFAULT_CHARSET) || !wbxml_buffer_append_mb_uint_32(header, strstbl_len)) { return WBXML_ERROR_ENCODER_APPEND_DATA; } /* Copy WBXML String Table */ #if defined( WBXML_ENCODER_USE_STRTBL ) if (encoder->use_strtbl) { if ((ret = wbxml_strtbl_construct(header, encoder->strstbl)) != WBXML_OK) return ret; } else { #endif /* WBXML_ENCODER_USE_STRTBL */ if (pid != NULL) { /* The append includes terminating NULL char */ if (!wbxml_buffer_append(header, pid)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Clean up */ wbxml_buffer_destroy(pid); } #if defined( WBXML_ENCODER_USE_STRTBL ) } #endif /* WBXML_ENCODER_USE_STRTBL */ return WBXML_OK; } /**************************** * WBXML Encoding Functions */ /** * @brief Encode a WBXML End Token * @param encoder The WBXML Encoder * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError wbxml_encode_end(WBXMLEncoder *encoder) { /* Append END */ if (!wbxml_buffer_append_char(encoder->output, WBXML_END)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode a WBXML Tag * @param encoder The WBXML Encoder * @param node The element to encode * @param has_content Does the element has content ? * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError wbxml_encode_tag(WBXMLEncoder *encoder, WBXMLTreeNode *node, WB_BOOL has_content) { const WBXMLTagEntry *tag = NULL; WB_UTINY token = 0x00, page = 0x00; if (node->name->type == WBXML_VALUE_TOKEN) { token = node->name->u.token->wbxmlToken; page = node->name->u.token->wbxmlCodePage; /* Current Tag */ encoder->current_tag = node->name->u.token; } else { /* Search tag in Tags Table */ if ((tag = wbxml_tables_get_tag_from_xml(encoder->lang, wbxml_tag_get_xml_name(node->name))) != NULL) { token = tag->wbxmlToken; page = tag->wbxmlCodePage; /* Current Tag */ encoder->current_tag = tag; } else encoder->current_tag = NULL; } /* Check if this element has content */ if (has_content) token |= WBXML_TOKEN_WITH_CONTENT; /* Check if this element has attributes */ /** @todo Check if the attributes will really be tokenized. There can be ignored attributes, and so NO attribute * tokenized at all. */ if ((node->attrs != NULL) && (encoder->lang->attrTable != NULL) /** @todo Fast patch for SyncML (change this) */) token |= WBXML_TOKEN_WITH_ATTRS; /* Encode Token */ if ((token & WBXML_TOKEN_MASK) == 0x00) return wbxml_encode_tag_literal(encoder, (WB_UTINY *) wbxml_tag_get_xml_name(node->name), token); else return wbxml_encode_tag_token(encoder, token, page); } /** * @brief Encode a WBXML Literal Token * @param encoder The WBXML Encoder * @param tag The literal tag to encode * @param mask The WBXML Mask for this tag * @return WBXML_OK if encoding is OK, an error code otherwise * @note stag = (literalTag index) * literalTag = LITERAL | LITERAL_A | LITERAL_C | LITERAL_AC * index = mb_u_int32 */ static WBXMLError wbxml_encode_tag_literal(WBXMLEncoder *encoder, WB_UTINY *tag, WB_UTINY mask) { #if defined( WBXML_ENCODER_USE_STRTBL ) WBXMLStringTableElement *elt = NULL; WBXMLBuffer *buff = NULL; WB_ULONG index = 0; WB_BOOL added = FALSE; /* If String Table generation is disabled, we can't generate this Literal Tag */ if (!encoder->use_strtbl) return WBXML_ERROR_STRTBL_DISABLED; /* Add tag in String Table */ if (((buff = wbxml_buffer_create(tag, WBXML_STRLEN(tag), WBXML_STRLEN(tag))) == NULL) || ((elt = wbxml_strtbl_element_create(buff, FALSE)) == NULL) || (!wbxml_strtbl_add_element(encoder, elt, &index, &added))) { wbxml_strtbl_element_destroy(elt); wbxml_buffer_destroy(buff); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* If already exists in String Table: clean-up */ if (!added) wbxml_strtbl_element_destroy(elt); /* Encode literalTag index */ if ((!wbxml_buffer_append_char(encoder->output, (WB_UTINY) (WBXML_LITERAL | mask))) || (!wbxml_buffer_append_mb_uint_32(encoder->output, index))) { return WBXML_ERROR_ENCODER_APPEND_DATA; } return WBXML_OK; #else /* No String Table Support */ return WBXML_ERROR_STRTBL_DISABLED; #endif /* WBXML_ENCODER_USE_STRTBL */ } /** * @brief Encode a WBXML Tag Token * @param encoder The WBXML Encoder * @param token The WBXML Tag Token to encode * @param page The WBXML CodePage for this Token * @return WBXML_OK if encoding is OK, an error code otherwise * @note element = ([switchPage] stag) * switchPage = SWITCH_PAGE pageindex * stag = TAG * pageindex = u_int8 */ static WBXMLError wbxml_encode_tag_token(WBXMLEncoder *encoder, WB_UTINY token, WB_UTINY page) { /* Switch Page if needed */ if (encoder->tagCodePage != page) { if ((!wbxml_buffer_append_char(encoder->output, WBXML_SWITCH_PAGE)) || (!wbxml_buffer_append_char(encoder->output, page))) { return WBXML_ERROR_ENCODER_APPEND_DATA; } encoder->tagCodePage = page; } /* Add Token */ if (!wbxml_buffer_append_char(encoder->output, token)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode a WBXML Attribute * @param encoder [in] The WBXML Encoder * @param attribute [in] The Attribute to encode * @return WBXML_OK if encoding is OK, an error code otherwise * @note attribute = attrStart *attrValue */ static WBXMLError wbxml_encode_attr(WBXMLEncoder *encoder, WBXMLAttribute *attribute) { WB_UTINY *value = NULL; WBXMLError ret = WBXML_OK; /* Encode Attribute Start */ if ((ret = wbxml_encode_attr_start(encoder, attribute, &value)) != WBXML_OK) return ret; /* Encode Attribute Value */ if (value != NULL) { if ((ret = wbxml_encode_value_element_buffer(encoder, value, WBXML_VALUE_ELEMENT_CTX_ATTR)) != WBXML_OK) return ret; } /* Reset Current Attribute */ encoder->current_attr = NULL; return WBXML_OK; } /** * @brief Encode a WBXML Attribute Start * @param encoder [in] The WBXML Encoder * @param attribute [in] The Attribute * @param value [out] Pointer to the value to encode * @return WBXML_OK if encoding is OK, an error code otherwise * @note The 'value' result correspond to the value there is still to encode * For example, in Wireless-Village, to encode: * xmlns="http://www.wireless-village.org/CSP1.1" * We first encode: * xmlns="http://www.wireless-village.org/CSP (start token: 0x05) * Then: * "1.1" as an inline string */ static WBXMLError wbxml_encode_attr_start(WBXMLEncoder *encoder, WBXMLAttribute *attribute, WB_UTINY **value) { const WBXMLAttrEntry *attr = NULL; WB_UTINY *value_left = NULL; WB_UTINY token = 0x00, page = 0x00; *value = wbxml_buffer_get_cstr(attribute->value); if (attribute->name->type == WBXML_VALUE_TOKEN) { /* We already have Token / Page pair for this Attribute Start */ token = attribute->name->u.token->wbxmlToken; page = attribute->name->u.token->wbxmlCodePage; /* Current Attribute */ encoder->current_attr = attribute->name->u.token; /* If there is a Start Value associated to the Attribute Name token... */ if (attribute->name->u.token->xmlValue != NULL) { /* ... Check that we have it at start of full Attribute Value */ if (WBXML_STRNCMP(wbxml_buffer_get_cstr(attribute->value), attribute->name->u.token->xmlValue, WBXML_STRLEN(attribute->name->u.token->xmlValue)) == 0) { /* Check if you have still a part in the Attribute Value to encode */ if (wbxml_buffer_len(attribute->value) > WBXML_STRLEN(attribute->name->u.token->xmlValue)) { /* There is still a part in the Value to encode */ *value = wbxml_buffer_get_cstr(attribute->value) + WBXML_STRLEN(attribute->name->u.token->xmlValue); } else *value = NULL; } else { /** @todo Should we stop everything and generate an error ? */ WBXML_WARNING((WBXML_ENCODER, "wbxml_encode_attr_start() => Attribute Value doesn't match Attribute Token")); /* Current Attribute */ encoder->current_attr = NULL; /* Encode Attribute Literal */ return wbxml_encode_attr_start_literal(encoder, wbxml_attribute_get_xml_name(attribute)); } } /* Encode Attribute Token */ return wbxml_encode_attr_token(encoder, token, page); } else { /* Search in Attribute table */ if ((attr = wbxml_tables_get_attr_from_xml(encoder->lang, (WB_UTINY *)attribute->name->u.token->xmlName, wbxml_buffer_get_cstr(attribute->value), &value_left)) != NULL) { token = attr->wbxmlToken; page = attr->wbxmlCodePage; /* Current Attribute */ encoder->current_attr = attr; /* If there is still a part in Attribute Value to encode */ *value = value_left; /* Encode Attribute Token */ return wbxml_encode_attr_token(encoder, token, page); } else { /* Current Attribute */ encoder->current_attr = NULL; /* Encode Attribute Literal */ return wbxml_encode_attr_start_literal(encoder, wbxml_attribute_get_xml_name(attribute)); } } } /** * @brief Encode a WBXML Attribute Value * @param encoder The WBXML Encoder * @param buffer The Value Element Buffer to encode * @param ctx Value Element Context (Attribute Value or Content) * @return WBXML_OK if encoding is OK, an error code otherwise * @note attrStart = *attrValue * attrValue = string | extension | entity | opaque * * AND: element = *content * content = string | extension | entity | opaque */ static WBXMLError wbxml_encode_value_element_buffer(WBXMLEncoder *encoder, WB_UTINY *buffer, WBXMLValueElementCtx ctx) { WBXMLList *lresult = NULL; WBXMLBuffer *buff = NULL; WBXMLValueElement *elt = NULL, *new_elt = NULL; WB_ULONG i = 0, j = 0, index = 0; WB_UTINY *the_buffer = buffer; WBXMLError ret = WBXML_OK; #if defined( WBXML_ENCODER_USE_STRTBL ) WBXMLStringTableElement *strtbl_elt = NULL; #endif /* WBXML_ENCODER_USE_STRTBL */ if ((buffer == NULL) || (*buffer == '\0')) return WBXML_OK; /********************************************************* * Encoder Language Specific Attribute Values */ if (ctx == WBXML_VALUE_ELEMENT_CTX_ATTR) { switch (encoder->lang->langID) { #if defined( WBXML_SUPPORT_SI ) case WBXML_LANG_SI10: /* SI 1.0: Encode date for 'created' and 'si-expires' attributes */ if ((encoder->current_attr->wbxmlCodePage == 0x00) && ((encoder->current_attr->wbxmlToken == 0x0a) || (encoder->current_attr->wbxmlToken == 0x10))) { return wbxml_encode_datetime(encoder, buffer); } break; #endif /* WBXML_SUPPORT_SI */ #if defined( WBXML_SUPPORT_EMN ) case WBXML_LANG_EMN10: /* EMN 1.0: Encode date for 'timestamp' attribute */ if ((encoder->current_attr->wbxmlCodePage == 0x00) && (encoder->current_attr->wbxmlToken == 0x05)) { return wbxml_encode_datetime(encoder, buffer); } break; #endif /* WBXML_SUPPORT_EMN */ default: break; } } /********************************************************* * Encoder Language Specific Content Text Values */ /* If this is a Text Content (not in a CDATA section) */ if ((ctx == WBXML_VALUE_ELEMENT_CTX_CONTENT) && (!encoder->in_cdata)) { #if defined( WBXML_SUPPORT_WV ) /* If this is a Wireless-Village 1.1 / 1.2 document */ if ((encoder->lang->langID == WBXML_LANG_WV_CSP11) || (encoder->lang->langID == WBXML_LANG_WV_CSP12)) { /* Here we try to encode Specific WV Content. If this buffer is not a WV Data Type buffer, or * if it can't be FULLY encoded as an Extension Token, then this function returns WBXML_NOT_ENCODED. * If so, the buffer will be encoded as String latter. */ if ((ret = wbxml_encode_wv_content(encoder, buffer)) != WBXML_NOT_ENCODED) return ret; } #endif /* WBXML_SUPPORT_WV */ #if defined( WBXML_SUPPORT_DRMREL ) /* If this is a DRMREL 1.0 document */ if (encoder->lang->langID == WBXML_LANG_DRMREL10) { /* Here we try to encode Specific DRMREL Content. If this buffer is not a DRMREL Data Type buffer * this function returns WBXML_NOT_ENCODED. * If so, the buffer will be encoded as String latter. */ if ((ret = wbxml_encode_drmrel_content(encoder, buffer)) != WBXML_NOT_ENCODED) return ret; } #endif /* WBXML_SUPPORT_DRMREL */ #if defined( WBXML_SUPPORT_SYNCML ) /* If this is a SyncML document ? */ if ((encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML10) || (encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML11)) { /** @todo We must check too if we are in a */ /* Change text in from "application/vnd.syncml-devinf+xml" to "application/vnd.syncml-devinf+wbxml" */ if (WBXML_STRCASECMP(buffer, "application/vnd.syncml-devinf+xml") == 0) { the_buffer = (WB_UTINY*) "application/vnd.syncml-devinf+wbxml"; } } #endif /* WBXML_SUPPORT_SYNCML */ } /********************************************************* * @todo Search first for simple cases ! */ /********************************************************* * We search the list of Value Elements that represents * this Value buffer */ /* Create Result List */ if ((lresult = wbxml_list_create()) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Create primary Buffer */ if ((buff = wbxml_buffer_create_from_cstr(the_buffer)) == NULL) { wbxml_list_destroy(lresult, NULL); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Create Value Element for this buffer */ if ((elt = wbxml_value_element_create()) == NULL) { wbxml_buffer_destroy(buff); wbxml_list_destroy(lresult, NULL); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } elt->type = WBXML_VALUE_ELEMENT_STRING; elt->u.str = buff; /* Append this Buffer to Result List */ if (!wbxml_list_append(lresult, elt)) { wbxml_value_element_destroy(elt); wbxml_list_destroy(lresult, NULL); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* If this is an Attribute Value */ if (ctx == WBXML_VALUE_ELEMENT_CTX_ATTR) { /********************************************************* * Search for Attribute Value Tokens */ if (encoder->lang->attrValueTable != NULL) { /* For each Attribute Value Token */ j = 0; while (encoder->lang->attrValueTable[j].xmlName != NULL) { /* For each Value Element */ for (i = 0; i < wbxml_list_len(lresult); i++) { if ((elt = (WBXMLValueElement *) wbxml_list_get(lresult, i)) == NULL) continue; if (elt->type != WBXML_VALUE_ELEMENT_STRING) continue; /* Is this Attribute Value contained in this Buffer ? */ if (wbxml_buffer_search_cstr(elt->u.str, (WB_UTINY *)encoder->lang->attrValueTable[j].xmlName, 0, &index)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_ATTR_TOKEN; new_elt->u.attr = &(encoder->lang->attrValueTable[j]); /* Insert new Value Element in List */ if (!wbxml_list_insert(lresult, (void *) new_elt, i + 1)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Check if there is still the end of the String to encode */ if (index + WBXML_STRLEN(encoder->lang->attrValueTable[j].xmlName) < wbxml_buffer_len(elt->u.str)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_STRING; new_elt->u.str = wbxml_buffer_create_from_cstr(wbxml_buffer_get_cstr(elt->u.str) + index + WBXML_STRLEN(encoder->lang->attrValueTable[j].xmlName)); /* Insert new Value Element in List */ if ((new_elt->u.str == NULL) || !wbxml_list_insert(lresult, (void *) new_elt, i + 2)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } /* Remove the Attribute Value found in Value Element */ wbxml_buffer_delete(elt->u.str, index, wbxml_buffer_len(elt->u.str) - index); } /* if */ } /* for */ j++; } /* while */ } /* if */ } /* If this is a Text Content (not in a CDATA section) */ if ((ctx == WBXML_VALUE_ELEMENT_CTX_CONTENT) && (!encoder->in_cdata)) { /********************************************************* * Search for Extension Tokens */ /** @todo Finish Extension Tokens Search */ if (encoder->lang->extValueTable != NULL) { /* For each Extension Token */ j = 0; while (encoder->lang->extValueTable[j].xmlName != NULL) { /* For each Value Element */ for (i = 0; i < wbxml_list_len(lresult); i++) { if ((elt = (WBXMLValueElement *) wbxml_list_get(lresult, i)) == NULL) continue; if (elt->type != WBXML_VALUE_ELEMENT_STRING) continue; /* Ignores the "1 char Extension Tokens" */ if (WBXML_STRLEN(encoder->lang->extValueTable[j].xmlName) < 2) continue; /* Is this Extension Token contained in this Buffer ? */ if (wbxml_buffer_search_cstr(elt->u.str, (WB_UTINY *) encoder->lang->extValueTable[j].xmlName, 0, &index)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_EXTENSION; new_elt->u.ext = &(encoder->lang->extValueTable[j]); /* Insert new Value Element in List */ if (!wbxml_list_insert(lresult, (void *) new_elt, i + 1)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Check if there is still the end of the String to encode */ if (index + WBXML_STRLEN(encoder->lang->extValueTable[j].xmlName) < wbxml_buffer_len(elt->u.str)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_STRING; new_elt->u.str = wbxml_buffer_create_from_cstr(wbxml_buffer_get_cstr(elt->u.str) + index + WBXML_STRLEN(encoder->lang->extValueTable[j].xmlName)); /* Insert new Value Element in List */ if ((new_elt->u.str == NULL) || !wbxml_list_insert(lresult, (void *) new_elt, i + 2)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } /* Remove the Attribute Value found in Value Element */ wbxml_buffer_delete(elt->u.str, index, wbxml_buffer_len(elt->u.str) - index); } /* if */ } /* for */ j++; } /* while */ } /* if */ } #if defined( WBXML_ENCODER_USE_STRTBL ) /*********************************************************************** * Search for String Table References * (except if this is a Content string and we are in a CDATA section) */ if (encoder->use_strtbl && !(encoder->in_cdata && (ctx == WBXML_VALUE_ELEMENT_CTX_CONTENT))) { /* For each String Table Element */ for (j = 0; j < wbxml_list_len(encoder->strstbl); j++) { if ((strtbl_elt = (WBXMLStringTableElement *) wbxml_list_get(encoder->strstbl, j)) == NULL) continue; /* For each Value Element */ for (i = 0; i < wbxml_list_len(lresult); i++) { if ((elt = (WBXMLValueElement *) wbxml_list_get(lresult, i)) == NULL) continue; if (elt->type != WBXML_VALUE_ELEMENT_STRING) continue; /* Is the String Table Element contained in this Buffer ? */ if (wbxml_buffer_search(elt->u.str, strtbl_elt->string, 0, &index)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_TABLEREF; new_elt->u.index = strtbl_elt->offset; /* Insert new Value Element in List */ if (!wbxml_list_insert(lresult, (void *) new_elt, i + 1)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Check if there is still the end of the String to encode */ if (index + wbxml_buffer_len(strtbl_elt->string) < wbxml_buffer_len(elt->u.str)) { /* Create new Value Element */ if ((new_elt = wbxml_value_element_create()) == NULL) { wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } new_elt->type = WBXML_VALUE_ELEMENT_STRING; new_elt->u.str = wbxml_buffer_create_from_cstr(wbxml_buffer_get_cstr(elt->u.str) + index + wbxml_buffer_len(strtbl_elt->string)); /* Insert new Value Element in List */ if ((new_elt->u.str == NULL) || !wbxml_list_insert(lresult, (void *) new_elt, i + 2)) { wbxml_value_element_destroy(new_elt); wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } /* Remove the Attribute Value found in Value Element */ wbxml_buffer_delete(elt->u.str, index, wbxml_buffer_len(elt->u.str) - index); } /* if */ } /* for */ } /* for */ } /* if */ #endif /* WBXML_ENCODER_USE_STRTBL */ /********************************************************* * Encode Value Element Buffer */ ret = wbxml_encode_value_element_list(encoder, lresult); /* Clean-up */ wbxml_list_destroy(lresult, wbxml_value_element_destroy_item); return ret; } /** * @brief Encode a WBXML Value Element List * @param encoder The WBXML Encoder * @param list The Value Element list * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError wbxml_encode_value_element_list(WBXMLEncoder *encoder, WBXMLList *list) { WBXMLValueElement *elt = NULL; WB_ULONG i = 0; WBXMLError ret = WBXML_OK; if (encoder == NULL) return WBXML_ERROR_INTERNAL; if (list == NULL) return WBXML_OK; for (i = 0; i < wbxml_list_len(list); i++) { if ((elt = (WBXMLValueElement *) wbxml_list_get(list, i)) == NULL) continue; switch (elt->type) { case WBXML_VALUE_ELEMENT_STRING: /* Inline String */ if (wbxml_buffer_len(elt->u.str) > 0) { if ((ret = wbxml_encode_inline_string(encoder, elt->u.str)) != WBXML_OK) return ret; } break; #if defined( WBXML_ENCODER_USE_STRTBL ) case WBXML_VALUE_ELEMENT_TABLEREF: /* String Table Reference */ if ((ret = wbxml_encode_tableref(encoder, elt->u.index)) != WBXML_OK) return ret; break; #endif /* WBXML_ENCODER_USE_STRTBL */ case WBXML_VALUE_ELEMENT_EXTENSION: /* Encode Extension Token */ if ((ret = wbxml_encode_inline_integer_extension_token(encoder, WBXML_EXT_T_0, elt->u.ext->wbxmlToken)) != WBXML_OK) return ret; break; case WBXML_VALUE_ELEMENT_OPAQUE: /** @todo Opaque */ break; case WBXML_VALUE_ELEMENT_ATTR_TOKEN: /* Attribute Value Token */ if ((ret = wbxml_encode_attr_token(encoder, elt->u.attr->wbxmlToken, elt->u.attr->wbxmlCodePage)) != WBXML_OK) return ret; break; default: return WBXML_ERROR_INTERNAL; } } return WBXML_OK; } /** * @brief Encode a WBXML Literal Attribute Start * @param encoder The WBXML Encoder * @param attr The literal attr name to encode * @return WBXML_OK if encoding is OK, an error code otherwise * @note attrStart = (LITERAL index) * index = mb_u_int32 */ static WBXMLError wbxml_encode_attr_start_literal(WBXMLEncoder *encoder, const WB_UTINY *attr) { #if defined( WBXML_ENCODER_USE_STRTBL ) WBXMLStringTableElement *elt = NULL; WBXMLBuffer *buff = NULL; WB_ULONG index = 0; WB_BOOL added = FALSE; /* If String Table generation is disabled, we can't generate this Literal */ if (!encoder->use_strtbl) return WBXML_ERROR_STRTBL_DISABLED; /* Add tag in String Table */ if (((buff = wbxml_buffer_create(attr, WBXML_STRLEN(attr), WBXML_STRLEN(attr))) == NULL) || ((elt = wbxml_strtbl_element_create(buff, FALSE)) == NULL) || (!wbxml_strtbl_add_element(encoder, elt, &index, &added))) { wbxml_strtbl_element_destroy(elt); wbxml_buffer_destroy(buff); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* If already exists in String Table: clean-up */ if (!added) wbxml_strtbl_element_destroy(elt); /* Encode LITERAL index */ if ((!wbxml_buffer_append_char(encoder->output, WBXML_LITERAL)) || (!wbxml_buffer_append_mb_uint_32(encoder->output, index))) { return WBXML_ERROR_ENCODER_APPEND_DATA; } return WBXML_OK; #else /* No String Table Support */ return WBXML_ERROR_STRTBL_DISABLED; #endif /* WBXML_ENCODER_USE_STRTBL */ } /** * @brief Encode a WBXML Attribute Token * @param encoder The WBXML Encoder * @param token The WBXML Attribute Token to encode * @param page The WBXML CodePage for this Token * @return WBXML_OK if encoding is OK, an error code otherwise * @note attrStart = ([switchPage] ATTRSTART) * switchPage = SWITCH_PAGE pageindex * pageindex = u_int8 * * And: attrValue = ([switchPage] ATTRVALUE) */ static WBXMLError wbxml_encode_attr_token(WBXMLEncoder *encoder, WB_UTINY token, WB_UTINY page) { /* Switch Page if needed */ if (encoder->attrCodePage != page) { if ((!wbxml_buffer_append_char(encoder->output, WBXML_SWITCH_PAGE)) || (!wbxml_buffer_append_char(encoder->output, page))) { return WBXML_ERROR_ENCODER_APPEND_DATA; } encoder->attrCodePage = page; } /* Add Token */ if (!wbxml_buffer_append_char(encoder->output, token)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode a WBXML Inline String * @param encoder The WBXML Encoder * @param str The String to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError wbxml_encode_inline_string(WBXMLEncoder *encoder, WBXMLBuffer *str) { /* Add STR_I */ if (!wbxml_buffer_append_char(encoder->output, WBXML_STR_I)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add String */ if (!wbxml_buffer_append(encoder->output, str)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Null Termination */ if (!wbxml_buffer_append_char(encoder->output, WBXML_STR_END)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode a WBXML Inline Integer Extension Token * @param encoder The WBXML Encoder * @param ext Extension Type (WBXML_EXT_T_0, WBXML_EXT_T_1 or WBXML_EXT_T_2) * @param value The Extension Token Value * @return WBXML_OK if encoding is OK, an error code otherwise * @note (WBXML 1.3 - 5.8.4.2) Inline integrer extension token = EXT_T* mb_u_int32 */ static WBXMLError wbxml_encode_inline_integer_extension_token(WBXMLEncoder *encoder, WB_UTINY ext, WB_UTINY value) { /* Add EXT_T* */ if (!wbxml_buffer_append_char(encoder->output, ext)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Value */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, (WB_ULONG) value)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } #if 0 /** * @brief Encode a WBXML Entity * @param encoder The WBXML Encoder * @param value The Entity to encode * @return WBXML_OK if encoding is OK, an error code otherwise * @note (WBXML 1.3 - 5.8.4.3) entity = ENTITY entcode * entcode = mb_u_int32 */ static WBXMLError wbxml_encode_entity(WBXMLEncoder *encoder, WB_ULONG value) { /* Add ENTITY */ if (!wbxml_buffer_append_char(encoder->output, WBXML_ENTITY)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add entcode */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, value)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } #endif /* 0 */ /** * @brief Encode a WBXML Opaque * @param encoder The WBXML Encoder * @param buff The Buffer to encode * @return WBXML_OK if encoding is OK, an error code otherwise * @note opaque = OPAQUE length *byte * length = mb_u_int32 */ static WBXMLError wbxml_encode_opaque(WBXMLEncoder *encoder, WBXMLBuffer *buff) { /* Add WBXML_OPAQUE */ if (!wbxml_buffer_append_char(encoder->output, WBXML_OPAQUE)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Length */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, wbxml_buffer_len(buff))) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Buffer */ if (!wbxml_buffer_append(encoder->output, buff)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } #if defined( WBXML_ENCODER_USE_STRTBL ) /** * @brief Encode a WBXML String Table Reference * @param encoder The WBXML Encoder * @param offset The String Table offset * @return WBXML_OK if encoding is OK, an error code otherwise * @note tableref = STR_T index */ static WBXMLError wbxml_encode_tableref(WBXMLEncoder *encoder, WB_ULONG offset) { /* Add WBXML_STR_T */ if (!wbxml_buffer_append_char(encoder->output, WBXML_STR_T)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add String */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, offset)) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } #endif /* WBXML_ENCODER_USE_STRTBL */ /** * @brief Create a WBXMLValueElement structure * @return The newly created WBXMLValueElement structure, or NULL if not enough memory */ static WBXMLValueElement *wbxml_value_element_create(void) { WBXMLValueElement *elt = NULL; if ((elt = (WBXMLValueElement*) wbxml_malloc(sizeof(WBXMLValueElement))) == NULL) return NULL; elt->type = WBXML_VALUE_ELEMENT_STRING; elt->u.str = NULL; return elt; } /** * @brief Destroy a WBXMLValueElement structure * @param elt The WBXMLValueElement structure to destroy */ static void wbxml_value_element_destroy(WBXMLValueElement *elt) { if (elt == NULL) return; switch (elt->type) { case WBXML_VALUE_ELEMENT_STRING: wbxml_buffer_destroy(elt->u.str); break; case WBXML_VALUE_ELEMENT_OPAQUE: wbxml_buffer_destroy(elt->u.buff); break; default: /* Nothing to destroy */ break; } wbxml_free((void*) elt); } /** * @brief Destroy a WBXMLValueElement structure (for wbxml_list_destroy() function) * @param elt The WBXMLValueElement structure to destroy */ static void wbxml_value_element_destroy_item(void *elt) { wbxml_value_element_destroy((WBXMLValueElement *) elt); } /** * @brief Encode an encapsulated WBXML Tree to WBXML * @param encoder [in] The WBXML Encoder * @param tree [in] The WBXML Tree to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError wbxml_encode_tree(WBXMLEncoder *encoder, WBXMLTree *tree) { WBXMLEncoder *new_encoder = NULL; WB_UTINY *wbxml = NULL; WB_ULONG wbxml_len = 0; WBXMLError ret = WBXML_OK; if ((new_encoder = encoder_duplicate(encoder)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Set Tree */ new_encoder->tree = tree; /* Encode to WBXML */ if ((ret = wbxml_encoder_encode_tree_to_wbxml(new_encoder, &wbxml, &wbxml_len)) != WBXML_OK) { wbxml_encoder_destroy(new_encoder); return ret; } /* Clean-up */ wbxml_encoder_destroy(new_encoder); /* Add WBXML_OPAQUE */ if (!wbxml_buffer_append_char(encoder->output, WBXML_OPAQUE)) { wbxml_free(wbxml); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Add Length */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, wbxml_len)) { wbxml_free(wbxml); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Append wbxml to output */ if (!wbxml_buffer_append_data(encoder->output, wbxml, wbxml_len)) { wbxml_free(wbxml); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Clean-up */ wbxml_free(wbxml); return WBXML_OK; } /**************************************** * Language Specific Encoding Functions */ #if ( defined( WBXML_SUPPORT_SI ) || defined( WBXML_SUPPORT_EMN ) ) /******************* * SI 1.0 / EMN 1.0 */ /** * @brief Encode SI %Datetime attribute value * @param encoder The WBXML Encoder * @param buffer The %Datetime value to encode * @return WBXML_OK if encoded, another error code otherwise * @note [SI] - 8.2.2. Encoding of %Datetime */ static WBXMLError wbxml_encode_datetime(WBXMLEncoder *encoder, WB_UTINY *buffer) { WBXMLBuffer *tmp = NULL; WB_ULONG i = 0; WB_UTINY ch = 0; WBXMLError ret = WBXML_OK; if ((tmp = wbxml_buffer_create_from_cstr(buffer)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Remove non-digit characters */ while (i < wbxml_buffer_len(tmp)) { /* Get char */ if (!wbxml_buffer_get_char(tmp, i, &ch)) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_INTERNAL; } if (!WBXML_ISDIGIT(ch)) { if ((ch != 'T') && (ch != 'Z') && (ch != '-') && (ch != ':')) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_BAD_DATETIME; } /* Remove it */ wbxml_buffer_delete(tmp, i, 1); } else i++; } /* Convert Ascii to Binary buffer */ wbxml_buffer_hex_to_binary(tmp); /* Remove trailing zero */ wbxml_buffer_remove_trailing_zeros(&tmp); /* Encode it to Opaque */ ret = wbxml_encode_opaque(encoder, tmp); wbxml_buffer_destroy(tmp); return ret; } #endif /* WBXML_SUPPORT_SI || WBXML_SUPPORT_EMN */ #if defined( WBXML_SUPPORT_WV ) /******************* * WV 1.1 / WV 1.2 */ /** * @brief Encode a Wireless-Village Content buffer * @param encoder The WBXML Encoder * @param buffer The buffer to encode * @return WBXML_OK if encoded, WBXML_NOT_ENCODED if not encoded, another error code otherwise * @note This function encodes a Specific WV Data Type content, or an exact Extension Token. * If not found, this is not encoded... and it will be encoded latter as an Inline String * in wbxml_encode_value_element_buffer(). We don't deal here if this buffer CONTAINS * Extension Tokens. */ static WBXMLError wbxml_encode_wv_content(WBXMLEncoder *encoder, WB_UTINY *buffer) { const WBXMLExtValueEntry *ext = NULL; WBXMLWVDataType data_type = WBXML_WV_DATA_TYPE_STRING; /* WB_ULONG ucs4_ch = 0; */ /* * Specific Data Type Elements: * * Boolean: * Acceptance (0x00 / 0x05) * InUse (0x00 / 0x18) * Poll (0x00 / 0x21) * AllFunctionsRequest (0x01 / 0x06) * CapabilityRequest (0x01 / 0x0B) * CompletionFlag (0x01 / 0x34) * ReceiveList (0x01 / 0x36) [WV 1.2] * AnyContent (0x03 / 0x09) * DefaultList (0x04 / 0x0B) * Auto-Subscribe (0x04 / 0x1E) [WV 1.2] * DeliveryReport (0x06 / 0x08) * JoinGroup (0x07 / 0x21) * JoinedRequest (0x07 / 0x10) * SubscribeNotification (0x07 / 0x22) * CIR (0x09 / 0x05) [WV 1.2] * * Integer: * Code (0x00 / 0x0B) * ContentSize (0x00 / 0x0F) * MessageCount (0x00 / 0x1A) * Validity (0x00 / 0x3C) * KeepAliveTime (0x01 / 0x1C) * SearchFindings (0x01 / 0x25) * SearchID (0x01 / 0x26) * SearchIndex (0x01 / 0x27) * SearchLimit (0x01 / 0x28) * TimeToLive (0x01 / 0x32) * AcceptedCharSet (0x03 / 0x05) * AcceptedContentLength (0x03 / 0x06) * MultiTrans (0x03 / 0x0C) * ParserSize (0x03 / 0x0D) * ServerPollMin (0x03 / 0x0E) * TCPPort (0x03 / 0x12) * UDPPort (0x03 / 0x13) * HistoryPeriod (0x09 / 0x08) [WV 1.2] * MaxWatcherList (0x09 / 0x0A) [WV 1.2] * * Date and Time: * DateTime (0x00 / 0x11) * DeliveryTime (0x06 / 0x1A) * * Binary: * ContentData (0x00 / 0x0D) (only if we have a: "BASE64" associated) */ /**************************************** * Get the Data Type of Current Element */ if (encoder->current_tag != NULL) { switch (encoder->current_tag->wbxmlCodePage) { case 0x00: /* Code Page: 0x00 */ switch (encoder->current_tag->wbxmlToken) { case 0x05: /* Acceptance */ case 0x18: /* InUse */ case 0x21: /* Poll */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; case 0x0B: /* Code */ case 0x0F: /* ContentSize */ case 0x1A: /* MessageCount */ case 0x3C: /* Validity */ /* INTEGER */ data_type = WBXML_WV_DATA_TYPE_INTEGER; break; case 0x11: /* DateTime */ /* DATE_AND_TIME */ data_type = WBXML_WV_DATA_TYPE_DATE_AND_TIME; break; case 0x0D: /* ContentData */ /* BINARY */ /** @todo Check if we have a: "BASE64" associated */ /* if (base64_encoded) data_type = WBXML_WV_DATA_TYPE_BINARY; else */ data_type = WBXML_WV_DATA_TYPE_STRING; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x01: /* Code Page: 0x01 */ switch (encoder->current_tag->wbxmlToken) { case 0x06: /* AllFunctionsRequest */ case 0x0B: /* CapabilityRequest */ case 0x34: /* CompletionFlag */ case 0x36: /* ReceiveList */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; case 0x1C: /* KeepAliveTime */ case 0x25: /* SearchFindings */ case 0x26: /* SearchID */ case 0x27: /* SearchIndex */ case 0x28: /* SearchLimit */ case 0x32: /* TimeToLive */ /* INTEGER */ data_type = WBXML_WV_DATA_TYPE_INTEGER; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x03: /* Code Page: 0x03 */ switch (encoder->current_tag->wbxmlToken) { case 0x09: /* AnyContent */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; case 0x05: /* AcceptedCharSet */ case 0x06: /* AcceptedContentLength */ case 0x0C: /* MultiTrans */ case 0x0D: /* ParserSize */ case 0x0E: /* ServerPollMin */ case 0x12: /* TCPPort */ case 0x13: /* UDPPort */ /* INTEGER */ data_type = WBXML_WV_DATA_TYPE_INTEGER; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x04: /* Code Page: 0x04 */ switch (encoder->current_tag->wbxmlToken) { case 0x0B: /* DefaultList */ case 0x1E: /* Auto-Subscribe */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x06: /* Code Page: 0x06 */ switch (encoder->current_tag->wbxmlToken) { case 0x08: /* DeliveryReport */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; case 0x1A: /* DeliveryTime */ /* DATE AND TIME */ data_type = WBXML_WV_DATA_TYPE_DATE_AND_TIME; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x07: /* Code Page: 0x07 */ switch (encoder->current_tag->wbxmlToken) { case 0x21: /* JoinGroup */ case 0x10: /* JoinedRequest */ case 0x22: /* SubscribeNotification */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; case 0x09: /* Code Page: 0x09 */ switch (encoder->current_tag->wbxmlToken) { case 0x05: /* CIR */ /* BOOLEAN */ data_type = WBXML_WV_DATA_TYPE_BOOLEAN; break; case 0x08: /* HistoryPeriod */ case 0x0A: /* MaxWatcherList */ /* INTEGER */ data_type = WBXML_WV_DATA_TYPE_INTEGER; break; default: /* STRING */ data_type = WBXML_WV_DATA_TYPE_STRING; break; } break; default: data_type = WBXML_WV_DATA_TYPE_STRING; break; } } /**************************************** * Encode, given the Data Type */ switch (data_type) { case WBXML_WV_DATA_TYPE_INTEGER: /* Integer: Encode it */ return wbxml_encode_wv_integer(encoder, buffer); break; case WBXML_WV_DATA_TYPE_DATE_AND_TIME: /* Date and time can be encoded as OPAQUE data or as a string as specified in [ISO8601]. For now we * keep the string... but if someone wants to code the Date and time encoding function :-) */ /* return wbxml_encode_wv_datetime(encoder, buffer); */ break; case WBXML_WV_DATA_TYPE_BINARY: /** @todo Binary Encoding !! */ break; case WBXML_WV_DATA_TYPE_BOOLEAN: /* Booleans are handled by the "T" and "F" extension tokens */ case WBXML_WV_DATA_TYPE_STRING: /* Check if this buffer is an EXACT Extension Token */ if ((ext = wbxml_tables_get_ext_from_xml(encoder->lang, buffer)) != NULL) return wbxml_encode_inline_integer_extension_token(encoder, WBXML_EXT_T_0, ext->wbxmlToken); else { if (WBXML_STRLEN(buffer) == 1) { /** * @todo [OMA WV 1.1] - 6.1 : A single character can be encoded as ENTITY (0x02) followed * by a mb_u_int32 containing the entity number. */ /* if (convert_char_to_ucs4(*buffer, &ucs4_ch)) return wbxml_encode_entity(encoder, ucs4_ch); */ } /* Else: noting encoded... this will be latter as an inline string */ } break; default: /* Hu ? */ break; } return WBXML_NOT_ENCODED; } /** * @brief Encode a Wireless-Village Integer * @param encoder The WBXML Encoder * @param buffer The buffer that contains the string representation of the integer to encode * @return WBXML_OK if OK, another error code otherwise */ static WBXMLError wbxml_encode_wv_integer(WBXMLEncoder *encoder, WB_UTINY *buffer) { WB_UTINY octets[4]; WB_ULONG the_int = 0, i = 0, start = 0; if ((encoder == NULL) || (buffer == NULL)) return WBXML_ERROR_INTERNAL; the_int = (WB_ULONG) atol((const WB_TINY *) buffer); for (i = 3; the_int > 0 && i >= 0; i--) { octets[i] = (WB_UTINY)(the_int & 0xff); the_int >>= 8; } start = i + 1; /* Add WBXML_OPAQUE */ if (!wbxml_buffer_append_char(encoder->output, WBXML_OPAQUE)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Integer Length */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, 4 - start)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Integer */ if (!wbxml_buffer_append_data(encoder->output, octets + start, (WB_UTINY)(4 - start))) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode WV Date and Time content value * @param encoder The WBXML Encoder * @param buffer The Date and Time value to encode * @return WBXML_OK if encoded, another error code otherwise * @note [WV] - 6.6 Date and Time * @note * Encoded Format: (6 octets) * - The first 2 bits are reserved, and both must be 0. * - Year is encoded by 12 bits (0 to 4095) * - Month is encoded by 4 bits (1 to 12) * - Day is encoded by 5 bits (1 to 31) * - Hour is encoded by 5 bits (0 to 23) * - Minute is encoded by 6 bits (0 to 59) * - Second is encoded by 6 bits (0 to 59) * - Time zone is encoded in 1 byte [ISO8601]. * * eg: * Binary: 00 011111010001 1010 10011 01001 110010 011111 01011010 * Octets: (-------)(-------)(--------)(-------)(-------) (------) * * Decoded Format: * eg: 20011019T095031Z or 20011019T095031 */ static WBXMLError wbxml_encode_wv_datetime(WBXMLEncoder *encoder, WB_UTINY *buffer) { /** @todo Finish wbxml_encode_wv_datetime() */ return WBXML_ERROR_NOT_IMPLEMENTED; #if 0 WBXMLBuffer *tmp = NULL; WB_ULONG i = 0, len = 0; WB_UTINY ch = 0; WBXMLError ret = WBXML_OK; WB_BOOL is_utc = FALSE; len = WBXML_STRLEN(buffer); /* Check Length */ if ((len != 15) && (len != 16)) return WBXML_ERROR_WV_DATETIME_FORMAT; /* Check position of 'T' */ if ((*buffer)[8] != 'T') return WBXML_ERROR_WV_DATETIME_FORMAT; /* Check position of 'Z' */ if (len == 16) { if ((*buffer)[15] != 'Z') return WBXML_ERROR_WV_DATETIME_FORMAT; /* This is an UTC format */ is_utc = TRUE; } /* Create temp Buffer */ if ((tmp = wbxml_buffer_create_from_cstr(buffer)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Delete 'T' and 'Z' */ if (is_utc) wbxml_buffer_delete(tmp, 15, 1); wbxml_buffer_delete(tmp, 8, 1); /* Check if you have only digits characters */ while (i < wbxml_buffer_len(tmp)) { /* Get char */ if (!wbxml_buffer_get_char(tmp, i, &ch)) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_INTERNAL; } if (!WBXML_ISDIGIT(ch)) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_WV_DATETIME_FORMAT; } else i++; } /* Convert Ascii to Binary buffer */ wbxml_buffer_hex_to_binary(tmp); /* Set Year */ /* Set Month */ /* Set Day */ /* Set Hour */ /* Set Minute */ /* Set Second */ /* Set Time Zone */ /* Encode it to Opaque */ ret = wbxml_encode_opaque(encoder, tmp); wbxml_buffer_destroy(tmp); return ret; #endif /* 0 */ } #endif /* WBXML_SUPPORT_WV */ #if defined( WBXML_SUPPORT_DRMREL ) /******************* * DRMREL 1.0 */ /** * @brief Encode a DRMREL Content buffer * @param encoder The WBXML Encoder * @param buffer The buffer to encode * @return WBXML_OK if encoded, WBXML_NOT_ENCODED if not encoded, another error code otherwise * @note This function encodes a Specific DRMREL content. * If not found, this is not encoded... and it will be encoded latter as an Inline String * in wbxml_encode_value_element_buffer(). */ static WBXMLError wbxml_encode_drmrel_content(WBXMLEncoder *encoder, WB_UTINY *buffer) { WB_UTINY *data = NULL; WB_LONG data_len = 0; const WBXMLTagEntry *current_tag = NULL; if ((encoder->current_text_parent != NULL) && (encoder->current_text_parent->name != NULL) && (encoder->current_text_parent->name->type == WBXML_VALUE_TOKEN)) { current_tag = encoder->current_text_parent->name->u.token; } if (current_tag != NULL) { if ((current_tag->wbxmlCodePage == 0x00) && (current_tag->wbxmlToken == 0x0C)) { /* content: "Encoded in binary format, i.e., no base64 encoding" */ /* Decode Base64 */ if ((data_len = wbxml_base64_decode(buffer, &data)) < 0) return WBXML_NOT_ENCODED; /* Add WBXML_OPAQUE */ if (!wbxml_buffer_append_char(encoder->output, WBXML_OPAQUE)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Data Length */ if (!wbxml_buffer_append_mb_uint_32(encoder->output, (WB_ULONG) data_len)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Add Data */ if (!wbxml_buffer_append_data(encoder->output, data, data_len)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Free Data */ wbxml_free(data); return WBXML_OK; } } return WBXML_NOT_ENCODED; } #endif /* WBXML_SUPPORT_DRMREL */ #if defined( WBXML_ENCODER_USE_STRTBL ) /**************************** * String Table Functions */ /** * @brief Create a String Table element * @param string The WBXMLBuffer containing the String * @param is_stat If set to TRUE, this Buffer won't be destroyed in wbxml_strtbl_element_destroy() function * @return The newly created String Table Element, or NULL if not enought memory */ static WBXMLStringTableElement *wbxml_strtbl_element_create(WBXMLBuffer *string, WB_BOOL is_stat) { WBXMLStringTableElement *elt = NULL; if ((elt = (WBXMLStringTableElement *) wbxml_malloc(sizeof(WBXMLStringTableElement))) == NULL) return NULL; elt->string = string; elt->offset = 0; elt->count = 0; elt->stat = is_stat; return elt; } /** * @brief Destroy a String Table element * @param element The element to destroy */ static void wbxml_strtbl_element_destroy(WBXMLStringTableElement *element) { if (element == NULL) return; if (!element->stat) wbxml_buffer_destroy(element->string); wbxml_free(element); } /** * @brief Destroy a String Table element (for wbxml_list_destroy()) * @param element The element to destroy */ static void wbxml_strtbl_element_destroy_item(void *element) { wbxml_strtbl_element_destroy((WBXMLStringTableElement *) element); } /** * @brief Initialize the String Table * @param encoder The WBXML Encoder * @param root The root element of LibXML Tree */ static WBXMLError wbxml_strtbl_initialize(WBXMLEncoder *encoder, WBXMLTreeNode *root) { WBXMLList *strings = NULL, *one_ref = NULL; WBXMLError ret; if ((strings = wbxml_list_create()) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Collect all Strings: * [out] 'strings' is the list of pointers to WBXMLBuffer. This Buffers must not be freed. */ wbxml_strtbl_collect_strings(encoder, root, strings); /* Insert, in String Table, Strings that are referenced more than one time * [out] 'strings' is NULL * 'one_ref' is the list of strings referenced only ONE time (list of WBXMLStringTableElement*) * Strings referenced more than one time are added to String Table */ if ((ret = wbxml_strtbl_check_references(encoder, &strings, &one_ref, TRUE)) != WBXML_OK) { wbxml_list_destroy(strings, NULL); return ret; } /* 'strings' is destroyed after call of wbxml_strtbl_check_references() */ /* Split Strings refered only one time in Words * [out] 'strings' is the list of words. This words (WBXMLBuffer) must be freed */ if ((ret = wbxml_strtbl_collect_words(one_ref, &strings)) != WBXML_OK) { wbxml_list_destroy(one_ref, wbxml_strtbl_element_destroy_item); return ret; } /* Destroy References List */ wbxml_list_destroy(one_ref, wbxml_strtbl_element_destroy_item); one_ref = NULL; /* Keep Strings referenced more than one time */ if (strings != NULL) wbxml_strtbl_check_references(encoder, &strings, &one_ref, FALSE); /* 'strings' is destroyed after call of wbxml_strtbl_check_references() */ /* Cleanup */ wbxml_list_destroy(one_ref, wbxml_strtbl_element_destroy_item); return WBXML_OK; } /** * @brief Collect Strings in XML Document (in Text Content and Attribute Values) * @param encoder [in] The WBXML Encoder * @param node [in] The current element node of LibXML Tree * @param strings [out] List of WBXMLBuffer buffers corresponding to Collected Strings */ static void wbxml_strtbl_collect_strings(WBXMLEncoder *encoder, WBXMLTreeNode *node, WBXMLList *strings) { const WBXMLAttrEntry *attr_entry = NULL; WBXMLAttribute *attr = NULL; WB_ULONG i = 0; WB_UTINY *value_left = NULL; switch (node->type) { case WBXML_TREE_TEXT_NODE: /* Ignore blank nodes */ if (wbxml_buffer_contains_only_whitespaces(node->content)) break; /** @todo Shrink / Strip Blanks */ /* Only add this string if it is big enought */ if (wbxml_buffer_len(node->content) > WBXML_ENCODER_STRING_TABLE_MIN) { wbxml_list_append(strings, node->content); WBXML_DEBUG((WBXML_ENCODER, "Strtbl - Collecting String: %s", wbxml_buffer_get_cstr(node->content))); } break; case WBXML_TREE_ELEMENT_NODE: /* Collect strings in Attributes Values too */ if (node->attrs != NULL) { for (i = 0; i < wbxml_list_len(node->attrs); i++) { /* Get attribute */ attr = wbxml_list_get(node->attrs, i); /* Only add this string if it is big enought */ if (wbxml_buffer_len(attr->value) > WBXML_ENCODER_STRING_TABLE_MIN) { /* This mustn't be a tokenisable Attribute Start */ attr_entry = wbxml_tables_get_attr_from_xml(encoder->lang, (WB_UTINY *) wbxml_attribute_get_xml_name(attr), (WB_UTINY *) wbxml_attribute_get_xml_value(attr), &value_left); /* - If attr_entry is NULL: no Attribute Start found * - If attr_entry is not NULL: and Attribute Start is found, but it can be the one with * no Attribute Value associated. So just check that the 'value_left' is the same than * the attribute value we where searching for */ if ((attr_entry == NULL) || ((attr_entry != NULL) && (value_left == (WB_UTINY *) wbxml_attribute_get_xml_value(attr)))) { /* It mustn't contain a tokenisable Attribute Value */ if (!wbxml_tables_contains_attr_value_from_xml(encoder->lang, (WB_UTINY *) wbxml_attribute_get_xml_value(attr))) { wbxml_list_append(strings, attr->value); WBXML_DEBUG((WBXML_ENCODER, "Strtbl - Collecting String: %s", wbxml_buffer_get_cstr(attr->value))); } } } } } break; default: /* NOOP */ break; } if (node->children != NULL) wbxml_strtbl_collect_strings(encoder, node->children, strings); if (node->next != NULL) wbxml_strtbl_collect_strings(encoder, node->next, strings); } /** * @brief Split Strings into Words * @param elements [in] List of String Table Elements to split * @param result [out] Resulting list of Words * @return WBXML_OK is no error, another error code otherwise */ static WBXMLError wbxml_strtbl_collect_words(WBXMLList *elements, WBXMLList **result) { WBXMLStringTableElement *elt = NULL; WBXMLList *list = NULL, *temp_list = NULL; WBXMLBuffer *word = NULL; WB_ULONG i = 0; *result = NULL; for (i = 0; i < wbxml_list_len(elements); i++) { elt = (WBXMLStringTableElement *) wbxml_list_get(elements, i); if (list == NULL) { if ((list = wbxml_buffer_split_words(elt->string)) == NULL) { wbxml_list_destroy(list, wbxml_buffer_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } else { if ((temp_list = wbxml_buffer_split_words(elt->string)) == NULL) { wbxml_list_destroy(list, wbxml_buffer_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } while ((word = wbxml_list_extract_first(temp_list)) != NULL) { if (!wbxml_list_append(list, word)) { wbxml_buffer_destroy(word); wbxml_list_destroy(temp_list, wbxml_buffer_destroy_item); wbxml_list_destroy(list, wbxml_buffer_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } wbxml_list_destroy(temp_list, NULL); } } *result = list; return WBXML_OK; } /** * @brief Append the String Table to result Buffer * @param buff The Buffer to append the String Table to * @param strstbl The String Table to append */ static WBXMLError wbxml_strtbl_construct(WBXMLBuffer *buff, WBXMLList *strstbl) { WBXMLStringTableElement *elt = NULL; WB_ULONG i = 0; if ((buff == NULL) || (strstbl == NULL)) return WBXML_ERROR_BAD_PARAMETER; for (i = 0; i < wbxml_list_len(strstbl); i++) { if ((elt = wbxml_list_get(strstbl, i)) == NULL) continue; if (!wbxml_buffer_append(buff, elt->string)) return WBXML_ERROR_ENCODER_APPEND_DATA; if (!wbxml_buffer_append_char(buff, WBXML_STR_END)) return WBXML_ERROR_ENCODER_APPEND_DATA; } return WBXML_OK; } /** * @brief Check strings that have multiple references, add them to string table * and return strings that have only one reference * @param encoder The WBXML Encoder * @param strings The List of Strings to check (List of WBXMLBuffer) : This list is freed by this function * @param one_ref List of strings that have only one reference (List of WBXMLStringTableElement) * @param stat_buff If set to TRUE, Buffers referenced by 'strings' must NOT be destroyed. * @return WBXML_OK if no error, another error code otherwise * @warning All elements of 'strings' list are removed from this list */ static WBXMLError wbxml_strtbl_check_references(WBXMLEncoder *encoder, WBXMLList **strings, WBXMLList **one_ref, WB_BOOL stat_buff) { WBXMLList *referenced = NULL, *result = NULL; WBXMLBuffer *string = NULL; WBXMLStringTableElement *ref = NULL; WB_ULONG j = 0; WB_BOOL added = FALSE; if ((strings == NULL) || (one_ref == NULL)) return WBXML_ERROR_INTERNAL; *one_ref = NULL; /* Create list of String References */ if ((referenced = wbxml_list_create()) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /********************* * Count References */ while (wbxml_list_len(*strings) > 0) { string = (WBXMLBuffer *) wbxml_list_extract_first(*strings); /* Check if we have already found this String */ for (j = 0; j < wbxml_list_len(referenced); j++) { ref = (WBXMLStringTableElement *) wbxml_list_get(referenced, j); if (wbxml_buffer_compare(ref->string, string) == 0) { if (!stat_buff) wbxml_buffer_destroy(string); string = NULL; ref->count++; break; } } if (string != NULL) { /* New Reference Element */ if ((ref = wbxml_strtbl_element_create(string, stat_buff)) == NULL) { wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); if (!stat_buff) wbxml_list_destroy(*strings, wbxml_buffer_destroy_item); else wbxml_list_destroy(*strings, NULL); *strings = NULL; return WBXML_ERROR_NOT_ENOUGH_MEMORY; } ref->count++; if (!wbxml_list_append(referenced, (void *) ref)) { wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); if (!stat_buff) wbxml_list_destroy(*strings, wbxml_buffer_destroy_item); else wbxml_list_destroy(*strings, NULL); *strings = NULL; return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } } wbxml_list_destroy(*strings, NULL); *strings = NULL; /*********************************************** * Remove Strings that have only One reference */ if ((result = wbxml_list_create()) == NULL) { wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } while (wbxml_list_len(referenced) > 0) { ref = (WBXMLStringTableElement *) wbxml_list_extract_first(referenced); if ((ref->count > 1) && (wbxml_buffer_len(ref->string) > WBXML_ENCODER_STRING_TABLE_MIN)) { /* Add Element to String Table */ if (!wbxml_strtbl_add_element(encoder, ref, NULL, &added)) { wbxml_strtbl_element_destroy(ref); wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); wbxml_list_destroy(result, wbxml_strtbl_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } if (!added) { wbxml_strtbl_element_destroy(ref); } } else { /* Add Element in resulting 'not added in String Table' list */ if (!wbxml_list_append(result, (void *) ref)) { wbxml_strtbl_element_destroy(ref); wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); wbxml_list_destroy(result, wbxml_strtbl_element_destroy_item); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } } } wbxml_list_destroy(referenced, wbxml_strtbl_element_destroy_item); *one_ref = result; return WBXML_OK; } /** * @brief Add a String in String table * @param encoder [in] The WBXML Encoder * @param elt [in] The Element to add * @param index [out] The index in String Table * @param added [out] TRUE if really added, or FALSe if already in String Table * @return TRUE if no error, FALSE is Memory Error */ static WB_BOOL wbxml_strtbl_add_element(WBXMLEncoder *encoder, WBXMLStringTableElement *elt, WB_ULONG *index, WB_BOOL *added) { WBXMLStringTableElement *elt_tmp = NULL; WB_ULONG i = 0; if ((encoder == NULL) || (encoder->strstbl == NULL) || (elt == NULL) || (elt->string == NULL)) return FALSE; *added = FALSE; /* Check if this element already exists in String Table */ for (i = 0; i < wbxml_list_len(encoder->strstbl); i++) { if ((elt_tmp = wbxml_list_get(encoder->strstbl, i)) == NULL) continue; if ((wbxml_buffer_len(elt_tmp->string) == wbxml_buffer_len(elt->string)) && (wbxml_buffer_compare(elt_tmp->string, elt->string) == 0)) { /* The String already exists in the String Table */ if (index != NULL) *index = elt_tmp->offset; return TRUE; } } /* Add this string to String Table */ elt->offset = encoder->strstbl_len; if (!wbxml_list_append(encoder->strstbl, (void *) elt)) return FALSE; /* Index in String Table */ if (index != NULL) *index = encoder->strstbl_len; /* New String Table length */ encoder->strstbl_len += wbxml_buffer_len(elt->string) + 1; *added = TRUE; return TRUE; } #endif /* WBXML_ENCODER_USE_STRTBL */ /***************************************** * XML Output Functions */ /**************************** * Build XML Result */ /** * @brief Build XML Result * @param encoder [in] The WBXML Encoder * @param xml [out] Resulting XML document * @param xml_len [out] XML document length * @return WBXML_OK if built is OK, an error code otherwise */ static WBXMLError xml_build_result(WBXMLEncoder *encoder, WB_UTINY **xml, WB_ULONG *xml_len) { WBXMLBuffer *header = NULL; WB_ULONG len = 0; WBXMLError ret = WBXML_OK; /* Init */ *xml_len = 0; if (encoder->flow_mode == TRUE) { /* Header already built */ header = encoder->output_header; } else { /* Create Header Buffer */ if ((header = wbxml_buffer_create("", 0, WBXML_ENCODER_XML_HEADER_MALLOC_BLOCK)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Fill Header Buffer */ if (encoder->xml_encode_header) { if ((ret = xml_fill_header(encoder, header)) != WBXML_OK) { wbxml_buffer_destroy(header); return ret; } } } /* Result Buffer Length */ len = wbxml_buffer_len(header) + wbxml_buffer_len(encoder->output); /* Create Result Buffer */ *xml = (WB_UTINY *) wbxml_malloc((len + 1) * sizeof(WB_UTINY)); if (*xml == NULL) { if (encoder->flow_mode == FALSE) wbxml_buffer_destroy(header); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /** @todo Use the 'output_charset' field */ /* Copy Header to Result */ memcpy(*xml, wbxml_buffer_get_cstr(header), wbxml_buffer_len(header)); /* Copy XML Document to Result */ memcpy(*xml + wbxml_buffer_len(header), wbxml_buffer_get_cstr(encoder->output), wbxml_buffer_len(encoder->output)); /** @todo Remove this NULL char if not needed by charset */ /* NULL Terminated Buffer */ (*xml)[len] = '\0'; /* Set length */ if (xml_len != NULL) *xml_len = len; /* Clean-up */ if (encoder->flow_mode == FALSE) wbxml_buffer_destroy(header); return WBXML_OK; } /** * @brief Fill the XML Header * @param encoder The WBXML Encoder * @param header The Buffer to Fill * @return WBXML_OK if built is OK, an error code otherwise */ static WBXMLError xml_fill_header(WBXMLEncoder *encoder, WBXMLBuffer *header) { if ((encoder == NULL) || (header == NULL)) return WBXML_ERROR_BAD_PARAMETER; /** @todo Add 'encoding' info */ /* */ if (!wbxml_buffer_append_cstr(header, WBXML_ENCODER_XML_HEADER)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* New Line */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { if (!xml_encode_new_line(header)) return WBXML_ERROR_ENCODER_APPEND_DATA; } /* lang->publicID->xmlRootElt)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* PUBLIC " */ if (!wbxml_buffer_append_cstr(header, WBXML_ENCODER_XML_PUBLIC)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Public ID */ if (!wbxml_buffer_append_cstr(header, encoder->lang->publicID->xmlPublicID)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* DTD */ if (!wbxml_buffer_append_cstr(header, WBXML_ENCODER_XML_DTD)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* DTD */ if (!wbxml_buffer_append_cstr(header, encoder->lang->publicID->xmlDTD)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* "> */ if (!wbxml_buffer_append_cstr(header, WBXML_ENCODER_XML_END_DTD)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* New Line */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { if (!xml_encode_new_line(header)) return WBXML_ERROR_ENCODER_APPEND_DATA; } return WBXML_OK; } /**************************** * XML Encoding Functions */ /** * @brief Encode an XML Tag * @param encoder The WBXML Encoder * @param node The element to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_tag(WBXMLEncoder *encoder, WBXMLTreeNode *node) { const WB_TINY *ns = NULL; WB_UTINY i; /* Set as current Tag */ if (node->name->type == WBXML_VALUE_TOKEN) encoder->current_tag = node->name->u.token; else encoder->current_tag = NULL; /* Indent */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { for (i=0; i<(encoder->indent * encoder->indent_delta); i++) { if (!wbxml_buffer_append_char(encoder->output, ' ')) return WBXML_ERROR_ENCODER_APPEND_DATA; } } /* Append < */ if (!wbxml_buffer_append_char(encoder->output, '<')) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append Element Name */ if (!wbxml_buffer_append_cstr(encoder->output, wbxml_tag_get_xml_name(node->name))) return WBXML_ERROR_ENCODER_APPEND_DATA; /* NameSpace handling: Check if Current Node Code Page is different than Parent Node Code Page */ if ((encoder->lang->nsTable != NULL) && ((node->parent == NULL) || ((node->parent->type == WBXML_TREE_ELEMENT_NODE) && (node->parent->name->type == WBXML_VALUE_TOKEN) && (node->type == WBXML_TREE_ELEMENT_NODE) && (node->name->type == WBXML_VALUE_TOKEN) && (node->parent->name->u.token->wbxmlCodePage != node->name->u.token->wbxmlCodePage)))) { if ((ns = wbxml_tables_get_xmlns(encoder->lang->nsTable, node->name->u.token->wbxmlCodePage)) != NULL) { /* Append xmlns=" */ if (!wbxml_buffer_append_cstr(encoder->output, " xmlns=\"")) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append NameSpace */ if (!wbxml_buffer_append_cstr(encoder->output, ns)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append " */ if (!wbxml_buffer_append_char(encoder->output, '"')) return WBXML_ERROR_ENCODER_APPEND_DATA; } } return WBXML_OK; } /** * @brief Encode an XML End Tag * @param encoder The WBXML Encoder * @param node Tag * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_end_tag(WBXMLEncoder *encoder, WBXMLTreeNode *node) { WB_UTINY i; if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) if (wbxml_tree_node_have_child_elt(node)) { #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ /* Add a New Line if there were content in this element */ if (encoder->in_content) { if (!xml_encode_new_line(encoder->output)) return WBXML_ERROR_ENCODER_APPEND_DATA; } encoder->indent--; /* Indent End Element */ for (i=0; i<(encoder->indent * encoder->indent_delta); i++) { if (!wbxml_buffer_append_char(encoder->output, ' ')) return WBXML_ERROR_ENCODER_APPEND_DATA; } #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) } #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ } /* Append output, "output, wbxml_tag_get_xml_name(node->name))) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append > */ if (!wbxml_buffer_append_char(encoder->output, '>')) return WBXML_ERROR_ENCODER_APPEND_DATA; /* New Line */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { if (!xml_encode_new_line(encoder->output)) return WBXML_ERROR_ENCODER_APPEND_DATA; } /* No more in content */ encoder->in_content = FALSE; return WBXML_OK; } /** * @brief Encode a XML Attribute * @param encoder [in] The WBXML Encoder * @param attribute [in] The Attribute to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_attr(WBXMLEncoder *encoder, WBXMLAttribute *attribute) { /* Append a space */ if (!wbxml_buffer_append_char(encoder->output, ' ')) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append Attribute Name */ if (!wbxml_buffer_append_cstr(encoder->output, wbxml_attribute_get_xml_name(attribute))) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Append =" */ if (!wbxml_buffer_append_cstr(encoder->output, "=\"")) return WBXML_ERROR_ENCODER_APPEND_DATA; if (wbxml_attribute_get_xml_value(attribute) != NULL) { /* Fix Attribute Value text */ WBXMLBuffer *tmp = NULL; /* Work with a temporary copy */ if ((tmp = wbxml_buffer_create_from_cstr(wbxml_attribute_get_xml_value(attribute))) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Fix text */ xml_fix_text(tmp, (WB_BOOL) (encoder->xml_gen_type == WBXML_GEN_XML_CANONICAL)); /* Append Attribute Value */ if (!wbxml_buffer_append(encoder->output, tmp)) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_ENCODER_APPEND_DATA; } wbxml_buffer_destroy(tmp); } /* Append " */ if (!wbxml_buffer_append_char(encoder->output, '"')) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode a End of XML Attributes List * @param encoder [in] The WBXML Encoder * @param node [in] Current node * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_end_attrs(WBXMLEncoder *encoder, WBXMLTreeNode *node) { #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) if (node->children == NULL) { /* Append " />" */ if (!wbxml_buffer_append_cstr(encoder->output, "/>")) return WBXML_ERROR_ENCODER_APPEND_DATA; /* New Line */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { if (!xml_encode_new_line(encoder->output)) return WBXML_ERROR_ENCODER_APPEND_DATA; } } else { #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ /* Append > */ if (!wbxml_buffer_append_char(encoder->output, '>')) return WBXML_ERROR_ENCODER_APPEND_DATA; /* New Line */ if (encoder->xml_gen_type == WBXML_GEN_XML_INDENT) { #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) if (wbxml_tree_node_have_child_elt(node)) { #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ if (!xml_encode_new_line(encoder->output)) return WBXML_ERROR_ENCODER_APPEND_DATA; /* Increment indentation */ encoder->indent++; #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) } #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ } #if defined( WBXML_ENCODER_XML_GEN_EMPTY_ELT ) } #endif /* WBXML_ENCODER_XML_GEN_EMPTY_ELT */ return WBXML_OK; } /** * @brief Encode an XML Text * @param encoder The WBXML Encoder * @param node The node containing XML Text to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_text(WBXMLEncoder *encoder, WBXMLTreeNode *node) { WBXMLBuffer *str = node->content; WBXMLBuffer *tmp = NULL; WB_UTINY i = 0; if (encoder->in_cdata) { /* If we are in a CDATA section, do not modify the text to encode */ if (!wbxml_buffer_append(encoder->output, str)) return WBXML_ERROR_ENCODER_APPEND_DATA; } else { /* Work with a temporary copy */ if ((tmp = wbxml_buffer_duplicate(str)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Indent */ if ((encoder->xml_gen_type == WBXML_GEN_XML_INDENT) && (!encoder->in_content)) { #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) if (wbxml_tree_node_have_child_elt(node)) { #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ /* Indent Content (only indent in first call to xml_encode_text()) */ for (i=0; i<(encoder->indent * encoder->indent_delta); i++) { if (!wbxml_buffer_append_char(encoder->output, ' ')) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_ENCODER_APPEND_DATA; } } #if defined( WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT ) } #endif /* WBXML_ENCODER_XML_NO_EMPTY_ELT_INDENT */ } #if defined( WBXML_SUPPORT_SYNCML ) /* Change text in from "application/vnd.syncml-devinf+wbxml" to "application/vnd.syncml-devinf+xml" */ if (((encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML10) || (encoder->lang->langID == WBXML_LANG_SYNCML_SYNCML11)) && (encoder->current_tag != NULL) && (encoder->current_tag->wbxmlCodePage == 0x01 ) && (encoder->current_tag->wbxmlToken == 0x13 ) && (wbxml_buffer_compare_cstr(tmp, "application/vnd.syncml-devinf+wbxml") == 0)) { wbxml_buffer_destroy(tmp); /* Change Content */ if ((tmp = wbxml_buffer_create_from_cstr("application/vnd.syncml-devinf+xml")) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; } #endif /* WBXML_SUPPORT_SYNCML */ /* Fix text */ xml_fix_text(tmp, (WB_BOOL) (encoder->xml_gen_type == WBXML_GEN_XML_CANONICAL)); /* Append Text */ if (!wbxml_buffer_append(encoder->output, tmp)) { wbxml_buffer_destroy(tmp); return WBXML_ERROR_ENCODER_APPEND_DATA; } /* Clean-up */ wbxml_buffer_destroy(tmp); } encoder->in_content = TRUE; return WBXML_OK; } /** * @brief Append a New Line to a Buffer * @param buff The Buffer * @return TRUE if added, FALSE otherwise */ static WB_BOOL xml_encode_new_line(WBXMLBuffer *buff) { if (buff == NULL) return FALSE; return wbxml_buffer_append_data(buff, WBXML_ENCODER_XML_NEW_LINE, WBXML_STRLEN(WBXML_ENCODER_XML_NEW_LINE)); } /** * @brief Fix an XML text buffer (content text or attribute value) * @param buff The Buffer to fix * @param normalize Normalize text ? * @return WBXML_OK if ok, an Error Code otherwise * @note Reference: http://www.w3.org/TR/2004/REC-xml-20040204/#syntax */ static WB_BOOL xml_fix_text(WBXMLBuffer *buff, WB_BOOL normalize) { WB_ULONG i = 0; WB_UTINY ch; for (i = 0; i < wbxml_buffer_len(buff); i++) { if (!wbxml_buffer_get_char(buff, i, &ch)) continue; switch (ch) { case '<': /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write "<" */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_lt, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; break; case '>': /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write ">" */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_gt, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; break; case '&': /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write "&" */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_amp, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; break; case '"': /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write """ */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_quot, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; break; case '\'': /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write "'" */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_apos, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; break; case '\r': if (normalize) { /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write " " */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_slashr, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; } break; case '\n': if (normalize) { /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write " " */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_slashn, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; } break; case '\t': if (normalize) { /* Remove it */ wbxml_buffer_delete(buff, i, 1); /* Write " " */ if (!wbxml_buffer_insert_cstr(buff, (WB_UTINY *) xml_tab, i)) return WBXML_ERROR_NOT_ENOUGH_MEMORY; } break; default: /* Do Nothing */ break; } } return WBXML_OK; } /** * @brief Encode a begin of CDATA section * @param encoder The WBXML Encoder * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_cdata(WBXMLEncoder *encoder) { /* Append output, " */ if (!wbxml_buffer_append_cstr(encoder->output, "]]>")) return WBXML_ERROR_ENCODER_APPEND_DATA; return WBXML_OK; } /** * @brief Encode an encapsulated WBXML Tree to XML * @param encoder [in] The WBXML Encoder * @param tree [in] The WBXML Tree to encode * @return WBXML_OK if encoding is OK, an error code otherwise */ static WBXMLError xml_encode_tree(WBXMLEncoder *encoder, WBXMLTree *tree) { WBXMLEncoder *new_encoder = NULL; WB_UTINY *xml = NULL; WB_ULONG xml_len = 0; WBXMLError ret = WBXML_OK; if ((new_encoder = encoder_duplicate(encoder)) == NULL) return WBXML_ERROR_NOT_ENOUGH_MEMORY; /* Set Tree */ new_encoder->tree = tree; /* Encode to XML */ if ((ret = wbxml_encoder_encode_tree_to_xml(new_encoder, &xml, &xml_len)) != WBXML_OK) { wbxml_encoder_destroy(new_encoder); return ret; } /* Clean-up */ wbxml_encoder_destroy(new_encoder); /** @bug Handle output_charset ! */ /* Append xml to output */ if (!wbxml_buffer_append_cstr(encoder->output, xml)) { wbxml_free(xml); return WBXML_ERROR_NOT_ENOUGH_MEMORY; } /* Clean-up */ wbxml_free(xml); return WBXML_OK; } .