From 189dc45fd76b61ab7442c8d3180308c463c52aec Mon Sep 17 00:00:00 2001 From: Stephane Lejeune Date: Tue, 13 Oct 2015 10:34:37 +0200 Subject: [PATCH 01/10] Adding oic/cbor support --- chrome/content/Handlers.js | 49 +++- chrome/content/Helpers.js | 67 ++++- chrome/content/Renderers.js | 32 ++- chrome/content/ResourceViews.js | 11 +- chrome/content/cbor.js | 395 +++++++++++++++++++++++++++++ chrome/content/coap/CoapRFC7252.js | 12 +- chrome/content/copper.xul | 3 + defaults/preferences/prefs.js | 1 + 8 files changed, 555 insertions(+), 15 deletions(-) create mode 100644 chrome/content/cbor.js diff --git a/chrome/content/Handlers.js b/chrome/content/Handlers.js index 0f1705f..9aa2773 100644 --- a/chrome/content/Handlers.js +++ b/chrome/content/Handlers.js @@ -54,9 +54,16 @@ Copper.defaultHandler = function(message) { if (message.getRTT) Copper.updateLabel('info_code', ' (RTT ' + message.getRTT() + ' ms)', true); - if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + + + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { Copper.updateResourceLinks( Copper.parseLinkFormat( document.getElementById('packet_payload').value ) ); - } + } else if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR && + (Copper.WELL_KNOWN_RESOURCES.indexOf(message.getOption(Copper.OPTION_URI_PATH)) > -1)) { + // FIXME: investigate why the code below triggers a parsing error +// Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.parseCBORFormat(document.getElementById('packet_payload').value ) ) ); + } + }; //Handle ping responses @@ -182,14 +189,15 @@ Copper.observingHandler = function(message) { }; // Handle messages with link format payload -Copper.discoverCache = new String(); +Copper.discoverCache; Copper.discoverHandler = function(message) { Copper.logEvent('INFO: discoverHandler()'); if (message.getCode()!=Copper.CODE_2_05_CONTENT) return; - if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT + || message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR) { Copper.updateLabel('info_code', 'Discovering'); @@ -206,23 +214,48 @@ Copper.discoverHandler = function(message) { if (message.getBlock2Number()==0) { Copper.logEvent('INFO: Starting new discover cache'); - Copper.discoverCache = new String(); + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + Copper.discoverCache = new String(); + } else if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR) { + Copper.discoverCache = new Array(); + } + } - Copper.discoverCache += Copper.bytes2str( message.getPayload() ); + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + Copper.discoverCache += Copper.bytes2str( message.getPayload() ); + } else if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR) { + Copper.discoverCache.concat( message.getPayload() ); + } if (!message.getBlock2More()) { Copper.logEvent('INFO: Appending discover cache'); // link-format Copper.resourcesCached = false; - Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.discoverCache ) ); + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.discoverCache ) ); + } else if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR && + (Copper.WELL_KNOWN_RESOURCES.indexOf(message.getOption(Copper.OPTION_URI_PATH)) > -1)) { + Copper.discoverCache = Copper.parseCBORFormat(Copper.discoverCache); + Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.discoverCache ) ); + } + document.getElementById('toolbar_discover').image = 'chrome://copper/skin/tool_discover.png'; } } else { // link-format Copper.resourcesCached = false; - Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.bytes2str( message.getPayload() ) ) ); + + + if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { + Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.bytes2str( message.getPayload() ) ) ); + } else if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_CBOR && + (Copper.WELL_KNOWN_RESOURCES.indexOf(message.getOption(Copper.OPTION_URI_PATH)) > -1)) { + Copper.discoverCache = Copper.parseCBORFormat(message.getPayload()); + Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.discoverCache ) ); + } + document.getElementById('toolbar_discover').image = 'chrome://copper/skin/tool_discover.png'; } diff --git a/chrome/content/Helpers.js b/chrome/content/Helpers.js index 22eec77..afa5b0c 100644 --- a/chrome/content/Helpers.js +++ b/chrome/content/Helpers.js @@ -47,6 +47,7 @@ Copper.getRequestType = function() { Copper.loadBehavior = function() { Copper.behavior.requests = Copper.prefManager.getCharPref('extensions.copper.behavior.requests'); Copper.behavior.retransmissions = Copper.prefManager.getBoolPref('extensions.copper.behavior.retransmissions'); + Copper.behavior.oic = Copper.prefManager.getBoolPref('extensions.copper.behavior.oic'); Copper.behavior.sendDuplicates = Copper.prefManager.getBoolPref('extensions.copper.behavior.send-duplicates'); Copper.behavior.showUnknown = Copper.prefManager.getBoolPref('extensions.copper.behavior.show-unknown'); Copper.behavior.rejectUnknown = Copper.prefManager.getBoolPref('extensions.copper.behavior.reject-unknown'); @@ -62,6 +63,7 @@ Copper.loadBehavior = function() { // sync XUL menu with behavior object Copper.updateBehavior = function() { document.getElementById('menu_behavior_requests_' + Copper.behavior.requests).setAttribute('checked', 'true'); + document.getElementById('menu_behavior_oic').setAttribute('checked', Copper.behavior.oic); document.getElementById('menu_behavior_retransmissions').setAttribute('checked', Copper.behavior.retransmissions); document.getElementById('menu_behavior_send_duplicates').setAttribute('checked', Copper.behavior.sendDuplicates); document.getElementById('menu_behavior_show_unknown').setAttribute('checked', Copper.behavior.showUnknown); @@ -78,6 +80,10 @@ Copper.updateBehavior = function() { Copper.behaviorUpdate = function(target) { if (target.id.substr(0,22)=='menu_behavior_requests') { Copper.behavior.requests = target.value; + } else if (target.id=='menu_behavior_oic') { + Copper.behavior.oic = target.getAttribute('checked')=='true'; + Copper.resources = new Object(); + Copper.updateResourceLinks(); } else if (target.id=='menu_behavior_retransmissions') { Copper.behavior.retransmissions = target.getAttribute('checked')=='true'; Copper.endpoint.setRetransmissions(Copper.behavior.retransmissions); @@ -331,10 +337,47 @@ Copper.checkUri = function(uri, caller) { } }; +Copper.parseCBORFormat = function(data) { + let abs = data.length; + let ab = new ArrayBuffer(abs); + let abv = new DataView(ab); + for(var i=0; i < abs; i++) + abv.setUint8(i, data[i]); + return Copper.decode(ab); +} + Copper.parseLinkFormat = function(data) { - + // This should really take another parameter, maybe the wk url + if ( (data instanceof Array) && + (data.length != 0) && + ((data[0].links) instanceof Array) ) + { + return Copper.parseOICLinkFormat( data ); + } + return Copper.parseRawLinkFormat (data); + +}; + +Copper.parseOICLinkFormat = function(data) { var links = new Object(); + // add all links + elm = data[0].links; + for (var i = 0; i]+>\s*(;\s*\w+\s*(=\s*(\w+|"([^"\\]*(\\.[^"\\]*)*)")\s*)?)*)/g); Copper.logEvent('-parsing link-format----------------------------'); @@ -391,6 +434,21 @@ Copper.parseLinkFormat = function(data) { return links; }; +Copper.stringifyReplacer = function(key, value) { + var ua = this[key]; + if(value instanceof Uint8Array) { + // Copper.logEvent('replacestringify ' + (typeof value) + ' ['+key+'] = ' + value); + var h=''; + for (var i = 0; i + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +Copper.POW_2_24 = Math.pow(2, -24); +Copper.POW_2_32 = Math.pow(2, 32); +Copper.POW_2_53 = Math.pow(2, 53); + +Copper.encode = function (value) { + var data = new ArrayBuffer(256); + var dataView = new DataView(data); + var lastLength; + var offset = 0; + + function ensureSpace(length) { + var newByteLength = data.byteLength; + var requiredLength = offset + length; + while (newByteLength < requiredLength) + newByteLength *= 2; + if (newByteLength !== data.byteLength) { + var oldDataView = dataView; + data = new ArrayBuffer(newByteLength); + dataView = new DataView(data); + var uint32count = (offset + 3) >> 2; + for (var i = 0; i < uint32count; ++i) + dataView.setUint32(i * 4, oldDataView.getUint32(i * 4)); + } + + lastLength = length; + return dataView; + } + function write() { + offset += lastLength; + } + function writeFloat64(value) { + write(ensureSpace(8).setFloat64(offset, value)); + } + function writeUint8(value) { + write(ensureSpace(1).setUint8(offset, value)); + } + function writeUint8Array(value) { + var dataView = ensureSpace(value.length); + for (var i = 0; i < value.length; ++i) + dataView.setUint8(offset + i, value[i]); + write(); + } + function writeUint16(value) { + write(ensureSpace(2).setUint16(offset, value)); + } + function writeUint32(value) { + write(ensureSpace(4).setUint32(offset, value)); + } + function writeUint64(value) { + var low = value % POW_2_32; + var high = (value - low) / POW_2_32; + var dataView = ensureSpace(8); + dataView.setUint32(offset, high); + dataView.setUint32(offset + 4, low); + write(); + } + function writeTypeAndLength(type, length) { + if (length < 24) { + writeUint8(type << 5 | length); + } else if (length < 0x100) { + writeUint8(type << 5 | 24); + writeUint8(length); + } else if (length < 0x10000) { + writeUint8(type << 5 | 25); + writeUint16(length); + } else if (length < 0x100000000) { + writeUint8(type << 5 | 26); + writeUint32(length); + } else { + writeUint8(type << 5 | 27); + writeUint64(length); + } + } + + function encodeItem(value) { + var i; + + if (value === false) + return writeUint8(0xf4); + if (value === true) + return writeUint8(0xf5); + if (value === null) + return writeUint8(0xf6); + if (value === undefined) + return writeUint8(0xf7); + + switch (typeof value) { + case "number": + if (Math.floor(value) === value) { + if (0 <= value && value <= POW_2_53) + return writeTypeAndLength(0, value); + if (-POW_2_53 <= value && value < 0) + return writeTypeAndLength(1, -(value + 1)); + } + writeUint8(0xfb); + return writeFloat64(value); + + case "string": + var utf8data = []; + for (i = 0; i < value.length; ++i) { + var charCode = value.charCodeAt(i); + if (charCode < 0x80) { + utf8data.push(charCode); + } else if (charCode < 0x800) { + utf8data.push(0xc0 | charCode >> 6); + utf8data.push(0x80 | charCode & 0x3f); + } else if (charCode < 0xd800) { + utf8data.push(0xe0 | charCode >> 12); + utf8data.push(0x80 | (charCode >> 6) & 0x3f); + utf8data.push(0x80 | charCode & 0x3f); + } else { + charCode = (charCode & 0x3ff) << 10; + charCode |= value.charCodeAt(++i) & 0x3ff; + charCode += 0x10000; + + utf8data.push(0xf0 | charCode >> 18); + utf8data.push(0x80 | (charCode >> 12) & 0x3f); + utf8data.push(0x80 | (charCode >> 6) & 0x3f); + utf8data.push(0x80 | charCode & 0x3f); + } + } + + writeTypeAndLength(3, utf8data.length); + return writeUint8Array(utf8data); + + default: + var length; + if (Array.isArray(value)) { + length = value.length; + writeTypeAndLength(4, length); + for (i = 0; i < length; ++i) + encodeItem(value[i]); + } else if (value instanceof Uint8Array) { + writeTypeAndLength(2, value.length); + writeUint8Array(value); + } else { + var keys = Object.keys(value); + length = keys.length; + writeTypeAndLength(5, length); + for (i = 0; i < length; ++i) { + var key = keys[i]; + encodeItem(key); + encodeItem(value[key]); + } + } + } + } + + encodeItem(value); + + if ("slice" in data) + return data.slice(0, offset); + + var ret = new ArrayBuffer(offset); + var retView = new DataView(ret); + for (var i = 0; i < offset; ++i) + retView.setUint8(i, dataView.getUint8(i)); + return ret; +} + +Copper.decode = function (data, tagger, simpleValue) { + var dataView = new DataView(data); + var offset = 0; + + if (typeof tagger !== "function") + tagger = function(value) { return value; }; + if (typeof simpleValue !== "function") + simpleValue = function() { return undefined; }; + + function read(value, length) { + offset += length; + return value; + } + function readArrayBuffer(length) { + return read(new Uint8Array(data, offset, length), length); + } + function readFloat16() { + var tempArrayBuffer = new ArrayBuffer(4); + var tempDataView = new DataView(tempArrayBuffer); + var value = readUint16(); + + var sign = value & 0x8000; + var exponent = value & 0x7c00; + var fraction = value & 0x03ff; + + if (exponent === 0x7c00) + exponent = 0xff << 10; + else if (exponent !== 0) + exponent += (127 - 15) << 10; + else if (fraction !== 0) + return fraction * POW_2_24; + + tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13); + return tempDataView.getFloat32(0); + } + function readFloat32() { + return read(dataView.getFloat32(offset), 4); + } + function readFloat64() { + return read(dataView.getFloat64(offset), 8); + } + function readUint8() { + return read(dataView.getUint8(offset), 1); + } + function readUint16() { + return read(dataView.getUint16(offset), 2); + } + function readUint32() { + return read(dataView.getUint32(offset), 4); + } + function readUint64() { + return readUint32() * POW_2_32 + readUint32(); + } + function readBreak() { + if (dataView.getUint8(offset) !== 0xff) + return false; + offset += 1; + return true; + } + function readLength(additionalInformation) { + if (additionalInformation < 24) + return additionalInformation; + if (additionalInformation === 24) + return readUint8(); + if (additionalInformation === 25) + return readUint16(); + if (additionalInformation === 26) + return readUint32(); + if (additionalInformation === 27) + return readUint64(); + if (additionalInformation === 31) + return -1; + throw "Invalid length encoding"; + } + function readIndefiniteStringLength(majorType) { + var initialByte = readUint8(); + if (initialByte === 0xff) + return -1; + var length = readLength(initialByte & 0x1f); + if (length < 0 || (initialByte >> 5) !== majorType) + throw "Invalid indefinite length element"; + return length; + } + + function appendUtf16data(utf16data, length) { + for (var i = 0; i < length; ++i) { + var value = readUint8(); + if (value & 0x80) { + if (value < 0xe0) { + value = (value & 0x1f) << 6 + | (readUint8() & 0x3f); + length -= 1; + } else if (value < 0xf0) { + value = (value & 0x0f) << 12 + | (readUint8() & 0x3f) << 6 + | (readUint8() & 0x3f); + length -= 2; + } else { + value = (value & 0x0f) << 18 + | (readUint8() & 0x3f) << 12 + | (readUint8() & 0x3f) << 6 + | (readUint8() & 0x3f); + length -= 3; + } + } + + if (value < 0x10000) { + utf16data.push(value); + } else { + value -= 0x10000; + utf16data.push(0xd800 | (value >> 10)); + utf16data.push(0xdc00 | (value & 0x3ff)); + } + } + } + + function decodeItem() { + var initialByte = readUint8(); + var majorType = initialByte >> 5; + var additionalInformation = initialByte & 0x1f; + var i; + var length; + + if (majorType === 7) { + switch (additionalInformation) { + case 25: + return readFloat16(); + case 26: + return readFloat32(); + case 27: + return readFloat64(); + } + } + + length = readLength(additionalInformation); + if (length < 0 && (majorType < 2 || 6 < majorType)) + throw "Invalid length"; + + switch (majorType) { + case 0: + return length; + case 1: + return -1 - length; + case 2: + if (length < 0) { + var elements = []; + var fullArrayLength = 0; + while ((length = readIndefiniteStringLength(majorType)) >= 0) { + fullArrayLength += length; + elements.push(readArrayBuffer(length)); + } + var fullArray = new Uint8Array(fullArrayLength); + var fullArrayOffset = 0; + for (i = 0; i < elements.length; ++i) { + fullArray.set(elements[i], fullArrayOffset); + fullArrayOffset += elements[i].length; + } + return fullArray; + } + return readArrayBuffer(length); + case 3: + var utf16data = []; + if (length < 0) { + while ((length = readIndefiniteStringLength(majorType)) >= 0) + appendUtf16data(utf16data, length); + } else + appendUtf16data(utf16data, length); + return String.fromCharCode.apply(null, utf16data); + case 4: + var retArray; + if (length < 0) { + retArray = []; + while (!readBreak()) + retArray.push(decodeItem()); + } else { + retArray = new Array(length); + for (i = 0; i < length; ++i) + retArray[i] = decodeItem(); + } + return retArray; + case 5: + var retObject = {}; + for (i = 0; i < length || length < 0 && !readBreak(); ++i) { + var key = decodeItem(); + retObject[key] = decodeItem(); + } + return retObject; + case 6: + return tagger(decodeItem(), length); + case 7: + switch (length) { + case 20: + return false; + case 21: + return true; + case 22: + return null; + case 23: + return undefined; + default: + return simpleValue(length); + } + } + } + + var ret = decodeItem(); + if (offset !== data.byteLength) + throw "Remaining bytes"; + return ret; +} + diff --git a/chrome/content/coap/CoapRFC7252.js b/chrome/content/coap/CoapRFC7252.js index 8331ed0..32f992a 100644 --- a/chrome/content/coap/CoapRFC7252.js +++ b/chrome/content/coap/CoapRFC7252.js @@ -133,7 +133,17 @@ Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TLV", function() Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_JSON", function() { return 1543; }); Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_OPAQUE", function() { return 1544; }); -Copper.__defineGetter__("WELL_KNOWN_RESOURCES", function() { return '/.well-known/core'; }); +Copper.__defineGetter__("WELL_KNOWN_RESOURCES", function() { + if(Copper.behavior.oic) return '/oic/res'; + return '/.well-known/core'; +}); + +Copper.__defineGetter__("WELL_KNOWN_PATH", function() { + if(Copper.behavior.oic) return '/oic'; + return '/.well-known'; +}); + + //Registries //////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/content/copper.xul b/chrome/content/copper.xul index 0025342..1df5dbb 100644 --- a/chrome/content/copper.xul +++ b/chrome/content/copper.xul @@ -57,6 +57,7 @@