/*
* 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("" +this. getCodepageToken(codepage, token) + ">");
}
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