/* * This file is part of EAS-4-TbSync. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; var wbxmltools = { // Convert a WBXML (WAP Binary XML) to plain XML - returns save xml with all special chars in the user data encoded by encodeURIComponent convert2xml: function (wbxml) { let num = 4; //skip the 4 first bytes which are mostly 0x03 (WBXML Version 1.3), 0x01 (unknown public identifier), 0x6A (utf-8), 0x00 (Length of string table) //the main code page will be set to the the first codepage used let mainCodePage = null; let tagStack = []; let xml = ""; let codepage = 0; while (num < wbxml.length) { let data = wbxml.substr(num, 1).charCodeAt(0); let token = data & 0x3F; //removes content bit(6) and attribute bit(7) let tokenHasContent = ((data & 0x40) != 0); //checks if content bit is set let tokenHasAttributes = ((data & 0x80) != 0); //checks if attribute bit is set switch(token) { case 0x00: // switch of codepage (new codepage is next byte) num = num + 1; codepage = (wbxml.substr(num, 1)).charCodeAt(0) & 0xFF; break; case 0x01: // Indicates the end of an attribute list or the end of an element // tagStack contains a list of opened tags, which await to be closed xml = xml + tagStack.pop(); break; case 0x02: // A character entity. Followed by a mb_u_int32 encoding the character entity number. TbSync.dump("wbxml", "Encoded character entity has not yet been implemented. Sorry."); return false; break; case 0x03: // Inline string followed by a termstr. (0x00) let termpos = wbxml.indexOf(String.fromCharCode(0x00), num); //encode all special chars in the user data by encodeURIComponent which does not encode the apostrophe, so we need to do that by hand xml = xml + encodeURIComponent(wbxml.substring(num + 1, termpos)).replace(/'/g, "%27"); num = termpos; break; case 0x04: // An unknown tag or attribute name. Followed by an mb_u_int32 that encodes an offset into the string table. case 0x40: // Inline string document-type-specific extension token. Token is followed by a termstr. case 0x41: // Inline string document-type-specific extension token. Token is followed by a termstr. case 0x42: // Inline string document-type-specific extension token. Token is followed by a termstr. case 0x43: // Processing instruction. case 0x44: // Unknown tag, with content. case 0x80: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32. case 0x81: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32. case 0x82: // Inline integer document-type-specific extension token. Token is followed by a mb_uint_32. case 0x83: // String table reference. Followed by a mb_u_int32 encoding a byte offset from the beginning of the string table. case 0x84: // Unknown tag, with attributes. case 0xC0: // Single-byte document-type-specific extension token. case 0xC1: // Single-byte document-type-specific extension token. case 0xC2: // Single-byte document-type-specific extension token. case 0xC3: // Opaque document-type-specific data. case 0xC4: // Unknown tag, with content and attributes. TbSync.dump("wbxml", "Global token <" + token + "> has not yet been implemented. Sorry."); return false; break; default: // if this code page is not the mainCodePage (or mainCodePage is not yet set = very first tag), add codePageTag with current codepage let codePageTag = (codepage != mainCodePage) ? " xmlns='" + this.getNamespace(codepage) + "'" : ""; // if no mainCodePage has been defined yet, use the current codepage, which is either the initialized/default value of codepage or a value set by SWITCH_PAGE if (mainCodePage === null) mainCodePage = codepage; if (!tokenHasContent) { xml = xml + "<" + this. getCodepageToken(codepage, token) + codePageTag + "/>"; } else { xml = xml + "<" +this. getCodepageToken(codepage, token) + codePageTag +">"; //add the closing tag to the stack, so it can get properly closed later tagStack.push(""); } if (this.isUnknownToken(codepage, token)) { TbSync.eventlog.add("warning", null, "WBXML: Unknown token <" + token + "> for codepage <"+codepage+">."); } } num = num + 1; } return (xml == "") ? "" : '' + xml; }, isUnknownToken: function (codepage, token) { if (this.codepages[codepage] && token in this.codepages[codepage]) return false; else return true; }, getNamespace: function (codepage) { return (this.namespaces[codepage]) ? this.namespaces[codepage] : "UnknownCodePage" + codepage ; }, getCodepageToken: function (codepage, token) { return this.isUnknownToken(codepage, token) ? "Unknown." + codepage + "." + token : this.codepages[codepage][token]; }, // This returns a wbxml object, which allows to add tags (using names), switch codepages, or open and close tags, it is also possible to append pure (binary) wbxml // If no wbxmlstring is present, default to the "init" string ( WBXML Version 1.3, unknown public identifier, UTF-8, Length of string table) createWBXML: function (wbxmlstring = String.fromCharCode(0x03, 0x01, 0x6A, 0x00), initialCodepage = "") { let wbxml = { _codepage : 0, _wbxml : wbxmlstring, append : function (wbxmlstring) { this._wbxml = this._wbxml + wbxmlstring; }, // adding a string content tag as contentstring atag : function (tokenname, content = "") { //check if tokenname is in current codepage if ((this._codepage in wbxmltools.codepages2) == false) throw "[wbxmltools] Unknown codepage <"+this._codepage+">"; if ((tokenname in wbxmltools.codepages2[this._codepage]) == false) throw "[wbxmltools] Unknown tokenname <"+tokenname+"> for codepage <"+wbxmltools.namespaces[this._codepage]+">"; if (content == "") { //empty, just add token this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname]); } else { //not empty,add token with enabled content bit and also add inlinestringidentifier this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname] | 0x40, 0x03); //add content for (let i=0; i< content.length; i++) this._wbxml += String.fromCharCode(content.charCodeAt(i)); //add string termination and tag close this._wbxml += String.fromCharCode(0x00, 0x01); } }, switchpage : function (name) { let codepage = wbxmltools.namespaces.indexOf(name); if (codepage == -1) throw "[wbxmltools] Unknown codepage <"+ name +">"; this._codepage = codepage; this._wbxml += String.fromCharCode(0x00, codepage); }, ctag : function () { this._wbxml += String.fromCharCode(0x01); }, //opentag is assumed to add a token with content, otherwise use addtag otag : function (tokenname) { this._wbxml += String.fromCharCode(wbxmltools.codepages2[this._codepage][tokenname] | 0x40); }, getCharCodes : function () { let value = ""; for (let i=0; i