1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
10 Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
15 * @see WAP-192-WBXML-20010725-A, clause 5.8.2
17 const TAG_TOKEN_ATTR_MASK = 0x80;
18 const TAG_TOKEN_CONTENT_MASK = 0x40;
19 const TAG_TOKEN_VALUE_MASK = 0x3F;
24 * @see WAP-192-WBXML-20010725-A, clause 7.1
26 const CODE_PAGE_SWITCH_TOKEN = 0x00;
27 const TAG_END_TOKEN = 0x01;
28 const INLINE_STRING_TOKEN = 0x03;
29 const STRING_TABLE_TOKEN = 0x83;
30 const OPAQUE_TOKEN = 0xC3;
32 // Set to true to enable debug message on all WBXML decoders.
33 this.DEBUG_ALL = false;
35 // Enable debug message for WBXML decoder core.
36 this.DEBUG = DEBUG_ALL | false;
39 * Handle WBXML code page switch.
42 * A wrapped object containing raw PDU data.
44 * Internal information for decode process.
46 * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.2 and 5.8.1
48 this.WbxmlCodePageSwitch = {
49 decode: function decode_wbxml_code_page_switch(data, decodeInfo) {
50 let codePage = WSP.Octet.decode(data);
52 if (decodeInfo.currentState === "tag") {
53 decodeInfo.currentTokenList.tag = decodeInfo.tokenList.tag[codePage];
55 if (!decodeInfo.currentTokenList.tag) {
56 throw new Error("Invalid tag code page: " + codePage + ".");
62 if (decodeInfo.currentState === "attr") {
63 decodeInfo.currentTokenList.attr = decodeInfo.tokenList.attr[codePage];
64 decodeInfo.currentTokenList.value = decodeInfo.tokenList.value[codePage];
66 if (!decodeInfo.currentTokenList.attr ||
67 !decodeInfo.currentTokenList.value) {
68 throw new Error("Invalid attr code page: " + codePage + ".");
74 throw new Error("Invalid decoder state: " + decodeInfo.currentState + ".");
79 * Parse end WBXML encoded message.
82 * A wrapped object containing raw PDU data.
84 * Internal information for decode process.
86 * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.1
90 decode: function decode_wbxml_end(data, decodeInfo) {
91 let tagInfo = decodeInfo.tagStack.pop();
92 return "</" + tagInfo.name + ">";
97 * Escape XML reserved characters &, <, >, " and ' which may appear in the
98 * WBXML-encoded strings in their original form.
101 * A string with potentially unescaped characters
103 * @return A string with the &, <, >, " and ' characters turned into XML
104 * character entitites
106 * @see WAP-192-WBXML-20010725-A, clause 6.1
108 this.escapeReservedCharacters = function escape_reserved_characters(str) {
111 for (var i = 0; i < str.length; i++) {
113 case '&' : dst += "&" ; break;
114 case '<' : dst += "<" ; break;
115 case '>' : dst += ">" ; break;
116 case '"' : dst += """; break;
117 case '\'': dst += "'"; break;
118 default : dst += str[i];
126 * Handle string table in WBXML message.
128 * @see WAP-192-WBXML-20010725-A, clause 5.7
130 this.readStringTable = function decode_wbxml_read_string_table(start, stringTable, charset) {
133 // Find end of string
134 let stringTableLength = stringTable.length;
135 while (end < stringTableLength) {
136 if (stringTable[end] === 0) {
143 return WSP.PduHelper.decodeStringContent(stringTable.subarray(start, end),
147 this.WbxmlStringTable = {
148 decode: function decode_wbxml_string_table(data, decodeInfo) {
149 let start = WSP.Octet.decode(data);
150 let str = readStringTable(start, decodeInfo.stringTable, decodeInfo.charset);
152 return escapeReservedCharacters(str);
157 * Parse inline string in WBXML encoded message.
160 * A wrapped object containing raw PDU data.
162 * Internal information for decode process.
164 * @see WAP-192-WBXML-20010725-A, clause 5.8.4.1
167 this.WbxmlInlineString = {
168 decode: function decode_wbxml_inline_string(data, decodeInfo) {
169 let charCode = WSP.Octet.decode(data);
172 stringData.push(charCode);
173 charCode = WSP.Octet.decode(data);
176 let str = WSP.PduHelper.decodeStringContent(stringData, decodeInfo.charset);
178 return escapeReservedCharacters(str);
183 * Parse inline Opaque data in WBXML encoded message.
186 * A wrapped object containing raw PDU data.
188 * Internal information for decode process.
190 * @see WAP-192-WBXML-20010725-A, clause 5.8.4.6
194 decode: function decode_wbxml_inline_opaque(data) {
195 // Inline OPAQUE must be decoded based on application definition,
196 // so it's illegal to run into OPAQUE type in general decoder.
197 throw new Error("OPQAUE decoder is not defined.");
204 * Parse WBXML encoded message into plain text.
207 * A wrapped object containing raw PDU data.
209 * Information for decoding, now requires charset and string table.
211 * Application-specific token difinition.
213 * @return Decoded WBXML message string.
215 parseWbxml: function parseWbxml_wbxml(data, decodeInfo, appToken) {
217 // Parse token definition to my structure.
218 decodeInfo.tokenList = {
219 tag: appToken.tagTokenList,
220 attr: appToken.attrTokenList,
221 value: appToken.valueTokenList
223 decodeInfo.tagStack = []; // tag decode stack
224 decodeInfo.currentTokenList = {
225 tag: decodeInfo.tokenList.tag[0],
226 attr: decodeInfo.tokenList.attr[0],
227 value: decodeInfo.tokenList.value[0]
229 decodeInfo.currentState = "tag"; // Current decoding state, "tag" or "attr"
230 // Used to read corresponding code page
231 // initial state : "tag"
233 // Merge global tag tokens into single list, so we don't have
234 // to search two lists every time.
235 let globalTagTokenList = Object.create(WBXML_GLOBAL_TOKENS);
236 if (appToken.globalTokenOverride) {
237 let globalTokenOverrideList = appToken.globalTokenOverride;
238 for (let token in globalTokenOverrideList) {
239 globalTagTokenList[token] = globalTokenOverrideList[token];
244 while (data.offset < data.array.length) {
245 // Decode content, might be a new tag token, an end of tag token, or an
248 // Switch to tag domain
249 decodeInfo.currentState = "tag";
251 let tagToken = WSP.Octet.decode(data);
252 let tagTokenValue = tagToken & TAG_TOKEN_VALUE_MASK;
254 // Try global tag first, tagToken of string table is 0x83, and will be 0x03
255 // in tagTokenValue, which is collision with inline string.
256 // So tagToken need to be searched before tagTokenValue.
257 let tagInfo = globalTagTokenList[tagToken] ||
258 globalTagTokenList[tagTokenValue];
260 content += tagInfo.coder.decode(data, decodeInfo);
264 // Check if application tag token is valid
265 tagInfo = decodeInfo.currentTokenList.tag[tagTokenValue];
267 throw new Error("Unsupported WBXML token: " + tagTokenValue + ".");
270 content += "<" + tagInfo.name;
272 if (tagToken & TAG_TOKEN_ATTR_MASK) {
273 // Decode attributes, might be a new attribute token, a value token,
274 // or an inline string
276 // Switch to attr/value domain
277 decodeInfo.currentState = "attr";
279 let attrSeperator = "";
280 while (data.offset < data.array.length) {
281 let attrToken = WSP.Octet.decode(data);
282 if (attrToken === TAG_END_TOKEN) {
286 let attrInfo = globalTagTokenList[attrToken];
288 content += attrInfo.coder.decode(data, decodeInfo);
292 // Check if attribute token is valid
293 attrInfo = decodeInfo.currentTokenList.attr[attrToken];
295 content += attrSeperator + " " + attrInfo.name + "=\"" + attrInfo.value;
296 attrSeperator = "\"";
300 attrInfo = decodeInfo.currentTokenList.value[attrToken];
302 content += attrInfo.value;
306 throw new Error("Unsupported WBXML token: " + attrToken + ".");
309 content += attrSeperator;
312 if (tagToken & TAG_TOKEN_CONTENT_MASK) {
314 decodeInfo.tagStack.push(tagInfo);
326 * A wrapped object containing raw PDU data.
328 * contains application-specific token info, including
330 * publicId : Public identifier of application.
331 * tagToken : Ojbect defines application tag tokens.
333 * Object[TAG_NAME] = Object[TOKEN_NUMBER] =
335 * name: "TOKEN_NAME",
336 * number: TOKEN_NUMBER
338 * attrToken : Object defines application attribute tokens.
339 * Object[ATTR_NAME] = Object[TOKEN_NUMBER] =
342 * value: "ATTR_VALUE",
343 * number: TOKEN_NUMBER
345 * For attribute value tokens, assign name as ""
346 * globalTokenOverride : Object overrides decoding behavior of global tokens.
348 * Object[GLOBAL_TOKEN_NUMBER] =
350 * decode: function(data),
351 * encode: function(data)
353 * decode() returns decoded text, encode() returns
357 * @return A WBXML message object or null in case of errors found.
359 parse: function parse_wbxml(data, appToken) {
365 * @see WAP-192-WBXML-20010725-A, Clause 5.3
368 headerRaw.version = WSP.Octet.decode(data);
369 headerRaw.publicId = WSP.UintVar.decode(data);
370 if (headerRaw.publicId === 0) {
371 headerRaw.publicIdStr = WSP.UintVar.decode(data);
373 headerRaw.charset = WSP.UintVar.decode(data);
375 let stringTableLen = WSP.UintVar.decode(data);
377 WSP.Octet.decodeMultiple(data, data.offset + stringTableLen);
379 // Transform raw header into user-friendly form
380 let entry = WSP.WSP_WELL_KNOWN_CHARSETS[headerRaw.charset];
382 throw new Error("Charset is not supported.");
384 msg.charset = entry.name;
386 if (headerRaw.publicId !== 0) {
387 msg.publicId = WBXML_PUBLIC_ID[headerRaw.publicId];
389 msg.publicId = readStringTable(headerRaw.publicIdStr, msg.stringTable,
390 WSP.WSP_WELL_KNOWN_CHARSETS[msg.charset].converter);
392 if (msg.publicId != appToken.publicId) {
393 throw new Error("Public ID does not match.");
396 msg.version = ((headerRaw.version >> 4) + 1) + "." + (headerRaw.version & 0x0F);
399 charset: WSP.WSP_WELL_KNOWN_CHARSETS[msg.charset].converter, // document character set
400 stringTable: msg.stringTable // document string table
402 msg.content = this.parseWbxml(data, decodeInfo, appToken);
411 * @see WAP-192-WBXML-20010725-A, clause 7.1
413 const WBXML_GLOBAL_TOKENS = (function () {
415 function add(number, coder) {
420 names[number] = entry;
423 add(CODE_PAGE_SWITCH_TOKEN, WbxmlCodePageSwitch);
424 add(TAG_END_TOKEN, WbxmlEnd);
425 add(INLINE_STRING_TOKEN, WbxmlInlineString);
426 add(STRING_TABLE_TOKEN, WbxmlStringTable);
427 add(OPAQUE_TOKEN, WbxmlOpaque);
433 * Pre-defined public IDs
435 * @see http://technical.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx
437 const WBXML_PUBLIC_ID = (function () {
439 function add(id, text) {
444 add(0x01, "UNKNOWN");
445 add(0x02, "-//WAPFORUM//DTD WML 1.0//EN");
446 add(0x03, "-//WAPFORUM//DTD WTA 1.0//EN");
447 add(0x04, "-//WAPFORUM//DTD WML 1.1//EN");
448 add(0x05, "-//WAPFORUM//DTD SI 1.0//EN");
449 add(0x06, "-//WAPFORUM//DTD SL 1.0//EN");
450 add(0x07, "-//WAPFORUM//DTD CO 1.0//EN");
451 add(0x08, "-//WAPFORUM//DTD CHANNEL 1.1//EN");
452 add(0x09, "-//WAPFORUM//DTD WML 1.2//EN");
453 add(0x0A, "-//WAPFORUM//DTD WML 1.3//EN");
454 add(0x0B, "-//WAPFORUM//DTD PROV 1.0//EN");
455 add(0x0C, "-//WAPFORUM//DTD WTA-WML 1.2//EN");
456 add(0x0D, "-//WAPFORUM//DTD EMN 1.0//EN");
457 add(0x0E, "-//OMA//DTD DRMREL 1.0//EN");
458 add(0x0F, "-//WIRELESSVILLAGE//DTD CSP 1.0//EN");
459 add(0x10, "-//WIRELESSVILLAGE//DTD CSP 1.1//EN");
460 add(0x11, "-//OMA//DTD WV-CSP 1.2//EN");
461 add(0x12, "-//OMA//DTD IMPS-CSP 1.3//EN");
462 add(0x13, "-//OMA//DRM 2.1//EN");
463 add(0x14, "-//OMA//SRM 1.0//EN");
464 add(0x15, "-//OMA//DCD 1.0//EN");
465 add(0x16, "-//OMA//DTD DS-DataObjectEmail 1.2//EN");
466 add(0x17, "-//OMA//DTD DS-DataObjectFolder 1.2//EN");
467 add(0x18, "-//OMA//DTD DS-DataObjectFile 1.2//EN");
470 add(0x0FD1, "-//SYNCML//DTD SyncML 1.0//EN");
471 add(0x0FD2, "-//SYNCML//DTD DevInf 1.0//EN");
472 add(0x0FD3, "-//SYNCML//DTD SyncML 1.1//EN");
473 add(0x0FD4, "-//SYNCML//DTD DevInf 1.1//EN");
474 add(0x1100, "-//PHONE.COM//DTD ALERT 1.0//EN");
475 add(0x1101, "-//PHONE.COM//DTD CACHE-OPERATION 1.0//EN");
476 add(0x1102, "-//PHONE.COM//DTD SIGNAL 1.0//EN");
477 add(0x1103, "-//PHONE.COM//DTD LIST 1.0//EN");
478 add(0x1104, "-//PHONE.COM//DTD LISTCMD 1.0//EN");
479 add(0x1105, "-//PHONE.COM//DTD CHANNEL 1.0//EN");
480 add(0x1106, "-//PHONE.COM//DTD MMC 1.0//EN");
481 add(0x1107, "-//PHONE.COM//DTD BEARER-CHOICE 1.0//EN");
482 add(0x1108, "-//PHONE.COM//DTD WML 1.1//EN");
483 add(0x1109, "-//PHONE.COM//DTD CHANNEL 1.1//EN");
484 add(0x110A, "-//PHONE.COM//DTD LIST 1.1//EN");
485 add(0x110B, "-//PHONE.COM//DTD LISTCMD 1.1//EN");
486 add(0x110C, "-//PHONE.COM//DTD MMC 1.1//EN");
487 add(0x110D, "-//PHONE.COM//DTD WML 1.3//EN");
488 add(0x110E, "-//PHONE.COM//DTD MMC 2.0//EN");
489 add(0x1200, "-//3GPP2.COM//DTD IOTA 1.0//EN");
490 add(0x1201, "-//SYNCML//DTD SyncML 1.2//EN");
491 add(0x1202, "-//SYNCML//DTD MetaInf 1.2//EN");
492 add(0x1203, "-//SYNCML//DTD DevInf 1.2//EN");
493 add(0x1204, "-//NOKIA//DTD LANDMARKS 1.0//EN");
494 add(0x1205, "-//SyncML//Schema SyncML 2.0//EN");
495 add(0x1206, "-//SyncML//Schema DevInf 2.0//EN");
496 add(0x1207, "-//OMA//DTD DRMREL 1.0//EN");
501 this.EXPORTED_SYMBOLS = [