├── LICENSE ├── README.md ├── chrome.manifest ├── chrome ├── locale │ └── en-US │ │ └── translations.dtd └── skin │ ├── Cu_16.png │ ├── Cu_24.png │ ├── Cu_32.png │ ├── Cu_48.png │ ├── Cu_64.png │ ├── Cu_box_256.png │ ├── classic.css │ ├── payload_in.png │ ├── payload_out.png │ ├── payload_rendered.png │ ├── renderJSON.css │ ├── renderLinkFormat.css │ ├── resource.png │ ├── resource_circle.png │ ├── resource_discovery.png │ ├── resource_home.png │ ├── resource_key.png │ ├── resource_link.png │ ├── resource_observable.png │ ├── resource_tag.png │ ├── resource_world.png │ ├── spinner.gif │ ├── text_clear.png │ ├── text_clear_6.png │ ├── tool_delete.png │ ├── tool_discover.png │ ├── tool_get.png │ ├── tool_observe.png │ ├── tool_ping.png │ ├── tool_post.png │ ├── tool_put.png │ ├── tool_test.png │ └── tool_unobserve.png ├── components └── CoapProtocolHandler.js ├── content ├── DebugOptions.js ├── Handlers.js ├── Helpers.js ├── Launcher.js ├── Logger.js ├── Main.js ├── Observing.js ├── Options.js ├── Plugtest.js ├── Renderers.js ├── ResourceViews.js ├── cbor-js │ └── cbor.js ├── coap │ ├── CoapMessage.js │ ├── CoapRFC7252.js │ ├── TransactionHandler.js │ └── UdpClient.js ├── copper.xul ├── launcher.xul ├── namespace.js ├── options.xul └── overlay.xul ├── defaults └── preferences │ └── prefs.js └── install.rdf /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016, Institute for Pervasive Computing, ETH Zurich. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copper (Cu) CoAP user-agent 2 | =========================== 3 | 4 | Implements [RFC7252](http://tools.ietf.org/html/rfc7252) 5 | 6 | Also see [Copper (Cu4Cr)](https://github.com/mkovatsc/Copper4Cr) 7 | 8 | ## Not supported by Firefox 56+ / WebExtensions API 9 | 10 | Mozilla changed their add-on model to the [WebExtensions API](https://developer.mozilla.org/Add-ons/WebExtensions), which does not allow implementing protocol handlers. 11 | 12 | **Copper cannot be fixed** to work again unless Mozilla changes the internals of Firefox again -- not to the old one, but a safe one that allows for protocol handler add-ons. 13 | 14 | ### Alternatives 15 | 16 | * [Copper4Cr](https://github.com/mkovatsc/Copper4Cr): Chrome also uses the WebExtensions API, but has a loophole through [discontinued Apps](https://blog.chromium.org/2016/08/from-chrome-apps-to-web.html) installed in developer mode 17 | * [Californium (Cf) Browser](https://github.com/eclipse/californium.tools/tree/master/cf-browser): Java-based tool, yet without the debug options support 18 | * [Firefox 55 Portable](https://sourceforge.net/projects/portableapps/files/Mozilla%20Firefox%2C%20Portable%20Ed./Mozilla%20Firefox%2C%20Portable%20Edition%2055.0.3/): Downgrade, but only use for add-ons, not browsing... 19 | 1. Ensure to start offline (no Internet connection!) and disable auto updates 20 | 2. Open `about:config` 21 | 3. Search `browser.tabs.remote.autostart` and `browser.tabs.remote.autostart.2` 22 | 4. Set both to `false` 23 | 24 | ## A Firefox add-on to browse the Internet of Things 25 | 26 | ### How to integrate the Copper sources into Firefox: 27 | 28 | 1. Get the sources from Github: `clone git://github.com/mkovatsc/Copper.git` 29 | 2. Add a text file named `copper@vs.inf.ethz.ch` to your extension directory of your profile: 30 | - Windows: `C:\Users\\AppData\Roaming\Mozilla\Firefox\Profiles\xxxxxxxx.default\extensions\` 31 | - Linux: `~/.mozilla/firefox/xxxxxxxx.default/extensions/` 32 | - MacOS: `~/Library/Application Support/Firefox/Profiles/xxxxxxxx.default/extensions/` 33 | 3. Write the path to the Copper sources (i.e., the directory that contains `install.rdf` and `chrome.manifest`) with (back)slash at the end to the file (e.g., `D:\Projects\Git\Copper\`). 34 | 4. Since Firefox 43 you need to **allow unsigned add-ons** by opening [about:config](about:config) and setting `xpinstall.signatures.required` to false. 35 | 5. Restart Firefox. 36 | 6. Optionally use [about:config](about:config) with the filter "copper" to clean up old configurations. 37 | 38 | Copper should show up in the add-ons list. You can now enter CoAP URIs (e.g., [coap://californium.eclipse.org/](coap://californium.eclipse.org/)) into the address bar and will have a user interface to interact with the CoAP resources on a server. The detailed log output is available through the rectangular button on the top right, next to the preferences button. 39 | 40 | ### See also: 41 | 42 | - [Setting up Extension Development Environment](https://developer.mozilla.org/en/setting_up_extension_development_environment) 43 | - [Firefox Profile Manager](https://support.mozilla.org/en-US/kb/profile-manager-create-and-remove-firefox-profiles) 44 | 45 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | component {6ffeeb10-91cc-0854-a554-81cf891ced50} components/CoapProtocolHandler.js 2 | contract @mozilla.org/network/protocol;1?name=coap {6ffeeb10-91cc-0854-a554-81cf891ced50} 3 | category profile-after-change CoapProtocolHandler @mozilla.org/network/protocol;1?name=coap 4 | 5 | content copper content/ contentaccessible=yes 6 | 7 | overlay chrome://browser/content/browser.xul content/overlay.xul 8 | overlay chrome://navigator/content/navigator.xul content/overlay.xul 9 | 10 | locale copper en-US chrome/locale/en-US/ 11 | 12 | skin copper classic chrome/skin/ 13 | -------------------------------------------------------------------------------- /chrome/locale/en-US/translations.dtd: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chrome/skin/Cu_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_16.png -------------------------------------------------------------------------------- /chrome/skin/Cu_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_24.png -------------------------------------------------------------------------------- /chrome/skin/Cu_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_32.png -------------------------------------------------------------------------------- /chrome/skin/Cu_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_48.png -------------------------------------------------------------------------------- /chrome/skin/Cu_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_64.png -------------------------------------------------------------------------------- /chrome/skin/Cu_box_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/Cu_box_256.png -------------------------------------------------------------------------------- /chrome/skin/classic.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper CoAP browser. 30 | ******************************************************************************/ 31 | 32 | toolbar { 33 | margin-bottom: 5px; 34 | border-bottom: 1px solid ThreeDShadow; 35 | -moz-appearance: none; 36 | } 37 | 38 | /* removes lines next to checkboxes (border of empty labels) */ 39 | checkbox:focus:not([label]) .checkbox-label-box { 40 | -moz-appearance: none; 41 | border: none; 42 | } 43 | 44 | /* the X button to clear textboxes */ 45 | .copper-clear-button { 46 | cursor: default; 47 | opacity: 0.8; 48 | 49 | list-style-image: url('chrome://copper/skin/text_clear_6.png'); 50 | padding: 5px 2px 4px 1px; 51 | /* 52 | list-style-image: url('chrome://browser/skin/Toolbar.png'); 53 | -moz-image-region: rect(3px 51px 15px 39px); 54 | padding: 2px 0 1px 0; 55 | 56 | list-style-image: url('chrome://browser/skin/reload-stop-go.png'); 57 | -moz-image-region: rect(0px 40px 14px 29px); 58 | padding-top:1px; 59 | */ 60 | } 61 | 62 | /* default resource icon */ 63 | treechildren { 64 | list-style-image: url('chrome://copper/skin/resource_circle.png'); 65 | } 66 | 67 | /* special resource icon */ 68 | treechildren::-moz-tree-image(host) { 69 | list-style-image: url("chrome://copper/skin/resource_home.png"); 70 | } 71 | treechildren::-moz-tree-image(link) { 72 | list-style-image: url("chrome://copper/skin/resource_link.png"); 73 | } 74 | treechildren::-moz-tree-image(wellknown) { 75 | list-style-image: url("chrome://copper/skin/resource_world.png"); 76 | } 77 | treechildren::-moz-tree-image(discovery) { 78 | list-style-image: url("chrome://copper/skin/resource_discovery.png"); 79 | } 80 | treechildren::-moz-tree-image(observable) { 81 | list-style-image: url("chrome://copper/skin/resource_observable.png"); 82 | } 83 | 84 | /* resource node appearance */ 85 | treechildren::-moz-tree-row() { 86 | height: 24px; 87 | } 88 | treechildren::-moz-tree-cell() { 89 | cursor: pointer; 90 | } 91 | treechildren::-moz-tree-image() { 92 | cursor: pointer; 93 | margin: 0px 4px 0px 2px; 94 | width: 16px; 95 | } 96 | treechildren::-moz-tree-cell-text() { 97 | color: darkgreen; 98 | } 99 | treechildren::-moz-tree-cell-text(hover) { 100 | cursor: pointer; 101 | text-decoration: underline; 102 | } 103 | 104 | treechildren::-moz-tree-indentation() { 105 | width: 20px; 106 | } 107 | 108 | /* highlight current resource */ 109 | treechildren::-moz-tree-cell(active) { 110 | background-color: #99CCFF; 111 | } 112 | treechildren::-moz-tree-cell-text(active) { 113 | font-weight: bold; 114 | color: black; 115 | } 116 | treechildren::-moz-tree-cell-text(cached) { 117 | color: black; 118 | } 119 | -------------------------------------------------------------------------------- /chrome/skin/payload_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/payload_in.png -------------------------------------------------------------------------------- /chrome/skin/payload_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/payload_out.png -------------------------------------------------------------------------------- /chrome/skin/payload_rendered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/payload_rendered.png -------------------------------------------------------------------------------- /chrome/skin/renderJSON.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper CoAP browser. 30 | ******************************************************************************/ 31 | 32 | .json-content li { 33 | list-style-type: none; 34 | } 35 | 36 | /* Containers */ 37 | 38 | .json-content .label { 39 | margin: 0px; 40 | font-weight: bold; 41 | } 42 | 43 | .json-content .object { 44 | padding-left: 20px; 45 | margin: 5px 0px 5px 0px; 46 | } 47 | .json-content .object:before { 48 | font-weight: bold; 49 | margin-left: -20px; 50 | content: '{'; 51 | } 52 | .json-content .object:after { 53 | font-weight: bold; 54 | margin-left: -20px; 55 | content: '}'; 56 | } 57 | 58 | .json-content .array { 59 | padding-left: 20px; 60 | margin: 5px 0px 5px 0px; 61 | } 62 | .json-content .array:before { 63 | font-weight: bold; 64 | margin-left: -20px; 65 | content: '['; 66 | } 67 | .json-content .array:after { 68 | font-weight: bold; 69 | margin-left: -20px; 70 | content: ']'; 71 | } 72 | 73 | /* Types */ 74 | .json-content .value { 75 | } 76 | 77 | .json-content .string { 78 | color: green; 79 | white-space: pre; 80 | } 81 | 82 | .json-content .null { 83 | color: black; 84 | font-weight: bold; 85 | } 86 | 87 | .json-content .int { 88 | color: red; 89 | } 90 | 91 | .json-content .float { 92 | color: purple; 93 | } 94 | 95 | .json-content .bool { 96 | color: blue; 97 | } 98 | -------------------------------------------------------------------------------- /chrome/skin/renderLinkFormat.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper CoAP browser. 30 | ******************************************************************************/ 31 | 32 | .link-content ul { 33 | list-style-type: none; 34 | } 35 | 36 | .link-content .uri { 37 | font-size: 16px; 38 | font-weight: bold; 39 | margin-left: -20px; 40 | } 41 | 42 | /* Containers */ 43 | 44 | .link-content .object { 45 | list-style-type: square; 46 | padding-left: 20px; 47 | margin: 5px 0px 5px 0px; 48 | } 49 | 50 | .link-content .array { 51 | list-style-type: circle; 52 | padding-left: 20px; 53 | margin: 5px 0px 5px 0px; 54 | color: black; 55 | } 56 | .link-content .array:before { 57 | font-weight: bold; 58 | margin-top: -20px; 59 | } 60 | 61 | /* Types */ 62 | .link-content .value { 63 | } 64 | 65 | .link-content .string { 66 | color: green; 67 | white-space: pre; 68 | } 69 | 70 | .link-content .null { 71 | color: black; 72 | font-weight: bold; 73 | } 74 | 75 | .link-content .int { 76 | color: red; 77 | } 78 | 79 | .link-content .float { 80 | color: purple; 81 | } 82 | 83 | .link-content .bool { 84 | color: blue; 85 | } 86 | 87 | 88 | 89 | .link-content .label { 90 | margin: 0px; 91 | font-weight: bold; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /chrome/skin/resource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource.png -------------------------------------------------------------------------------- /chrome/skin/resource_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_circle.png -------------------------------------------------------------------------------- /chrome/skin/resource_discovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_discovery.png -------------------------------------------------------------------------------- /chrome/skin/resource_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_home.png -------------------------------------------------------------------------------- /chrome/skin/resource_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_key.png -------------------------------------------------------------------------------- /chrome/skin/resource_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_link.png -------------------------------------------------------------------------------- /chrome/skin/resource_observable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_observable.png -------------------------------------------------------------------------------- /chrome/skin/resource_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_tag.png -------------------------------------------------------------------------------- /chrome/skin/resource_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/resource_world.png -------------------------------------------------------------------------------- /chrome/skin/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/spinner.gif -------------------------------------------------------------------------------- /chrome/skin/text_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/text_clear.png -------------------------------------------------------------------------------- /chrome/skin/text_clear_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/text_clear_6.png -------------------------------------------------------------------------------- /chrome/skin/tool_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_delete.png -------------------------------------------------------------------------------- /chrome/skin/tool_discover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_discover.png -------------------------------------------------------------------------------- /chrome/skin/tool_get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_get.png -------------------------------------------------------------------------------- /chrome/skin/tool_observe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_observe.png -------------------------------------------------------------------------------- /chrome/skin/tool_ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_ping.png -------------------------------------------------------------------------------- /chrome/skin/tool_post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_post.png -------------------------------------------------------------------------------- /chrome/skin/tool_put.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_put.png -------------------------------------------------------------------------------- /chrome/skin/tool_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_test.png -------------------------------------------------------------------------------- /chrome/skin/tool_unobserve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkovatsc/Copper/f3cd8d5a22aadaefcd8e316470f3d837ea7a66ab/chrome/skin/tool_unobserve.png -------------------------------------------------------------------------------- /components/CoapProtocolHandler.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Firefox protocol handler for 'coap:' 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | /** 39 | * CopperProtocolHandler namespace. 40 | */ 41 | if ("undefined" == typeof(CopperProtocol)) { 42 | var CopperProtocol = {}; 43 | }; 44 | 45 | // Array allows for multiple schemes (e.g., coap and coaps for coap with DTLS) 46 | CopperProtocol.__defineGetter__("CLASS_ID", function() { return [Components.ID('{6ffeeb10-91cc-0854-a554-81cf891ced50}')]; }); 47 | CopperProtocol.__defineGetter__("CLASS_SCHEME", function() { return ['coap']; }); 48 | CopperProtocol.__defineGetter__("CLASS_DEFAULT_PORT", function() { return [-1]; }); 49 | CopperProtocol.__defineGetter__("CLASS_FLAGS", function() { return [Components.interfaces.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE]; }); 50 | CopperProtocol.__defineGetter__("CLASS_NAME", function() { return ['CoAP (RFC 7252)']; }); 51 | 52 | CopperProtocol.__defineGetter__("SECURITY", function() { return Components.interfaces.nsILoadInfo.SEC_SANDBOXED | Components.interfaces.nsILoadInfo.SEC_ALLOW_CHROME; }); 53 | 54 | /** 55 | * Handles the CoAP protocol 56 | */ 57 | CopperProtocol.Handler = function() {}; 58 | 59 | CopperProtocol.Handler.prototype = { 60 | 61 | defaultPort : CopperProtocol.CLASS_DEFAULT_PORT[0], 62 | protocolFlags : CopperProtocol.CLASS_FLAGS[0], 63 | scheme: CopperProtocol.CLASS_SCHEME[0], 64 | 65 | allowPort: function(port, scheme) { 66 | return false; 67 | }, 68 | 69 | newURI : function(aSpec, aCharset, aBaseURI) { 70 | 71 | /* 72 | * Standard URL with URLTYPE_AUTHORITY = 2 73 | * 74 | * blah:foo/bar => blah://foo/bar 75 | * blah:/foo/bar => blah://foo/bar 76 | * blah://foo/bar => blah://foo/bar 77 | * blah:///foo/bar => blah://foo/bar 78 | */ 79 | 80 | try { 81 | var uri = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIStandardURL); 82 | uri.init(Components.interfaces.nsIStandardURL.URLTYPE_AUTHORITY, this.defaultPort, aSpec, aCharset, aBaseURI); 83 | 84 | return uri; 85 | 86 | } catch (ex) { 87 | // will cause an alert 88 | return uri = Components.classes["@mozilla.org/network/simple-uri;1"].createInstance(Components.interfaces.nsIURI); 89 | } 90 | }, 91 | 92 | newChannel : function(aURI) { 93 | var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); 94 | return ioService.newChannel2("chrome://copper/content/copper.xul", null, null, null, null, null, CopperProtocol.SECURITY, null); 95 | }, 96 | 97 | /** 98 | * The QueryInterface method provides runtime type discovery. 99 | * More: http://developer.mozilla.org/en/docs/nsISupports 100 | * @param aIID the IID of the requested interface. 101 | * @return the resulting interface pointer. 102 | */ 103 | QueryInterface : function(aIID) { 104 | if (!aIID.equals(Components.interfaces.nsIProtocolHandler) && !aIID.equals(Components.interfaces.nsISupports)) { 105 | throw Components.results.NS_ERROR_NO_INTERFACE; 106 | } 107 | return this; 108 | } 109 | 110 | }; 111 | 112 | /** 113 | * The nsIFactory interface allows for the creation of nsISupports derived 114 | * classes without specifying a concrete class type. 115 | * More: http://developer.mozilla.org/en/docs/nsIFactory 116 | */ 117 | CopperProtocol.HandlerFactory = function() {}; 118 | 119 | CopperProtocol.HandlerFactory.prototype = { 120 | createInstance: function (aOuter, aIID) { 121 | if (null != aOuter) { 122 | throw Components.results.NS_ERROR_NO_AGGREGATION; 123 | } 124 | return (new CopperProtocol.Handler()).QueryInterface(aIID); 125 | } 126 | }; 127 | 128 | /** 129 | * Firefox 3.x 130 | * The nsIModule interface must be implemented by each XPCOM component. It is 131 | * the main entry point by which the system accesses an XPCOM component. 132 | * More: http://developer.mozilla.org/en/docs/nsIModule 133 | */ 134 | CopperProtocol.HandlerModule = { 135 | /** 136 | * When the nsIModule is discovered, this method will be called so that any 137 | * setup registration can be preformed. 138 | * @param aCompMgr the global component manager. 139 | * @param aLocation the location of the nsIModule on disk. 140 | * @param aLoaderStr opaque loader specific string. 141 | * @param aType loader type being used to load this module. 142 | */ 143 | registerSelf : function(aCompMgr, aLocation, aLoaderStr, aType) { 144 | aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); 145 | for (var i in CopperProtocol.CLASS_SCHEME) { 146 | aCompMgr.registerFactoryLocation(CopperProtocol.CLASS_ID[i], CopperProtocol.CLASS_NAME[i], "@mozilla.org/network/protocol;1?name="+CopperProtocol.CLASS_SCHEME[i], aLocation, aLoaderStr, aType); 147 | } 148 | }, 149 | 150 | /** 151 | * When the nsIModule is being unregistered, this method will be called so 152 | * that any cleanup can be preformed. 153 | * @param aCompMgr the global component manager. 154 | * @param aLocation the location of the nsIModule on disk. 155 | * @param aLoaderStr opaque loader specific string. 156 | */ 157 | unregisterSelf : function (aCompMgr, aLocation, aLoaderStr) { 158 | aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); 159 | for (var i in CopperProtocol.CLASS_SCHEME) { 160 | aCompMgr.unregisterFactoryLocation(CopperProtocol.CLASS_ID[i], aLocation); 161 | } 162 | }, 163 | 164 | /** 165 | * This method returns a class object for a given ClassID and IID. 166 | * @param aCompMgr the global component manager. 167 | * @param aClass the ClassID of the object instance requested. 168 | * @param aIID the IID of the object instance requested. 169 | * @return the resulting interface pointer. 170 | * @throws NS_ERROR_NOT_IMPLEMENTED if aIID is inadequate. 171 | * @throws NS_ERROR_NO_INTERFACE if the interface is not found. 172 | */ 173 | getClassObject : function(aCompMgr, aClass, aIID) { 174 | if (!aIID.equals(Components.interfaces.nsIFactory)) { 175 | throw Components.results.NS_ERROR_NOT_IMPLEMENTED; 176 | } 177 | 178 | if (aClass.equals(CopperProtocol.CLASS_ID[0])) { 179 | return new CopperProtocol.HandlerFactory(); 180 | } 181 | 182 | throw Components.results.NS_ERROR_NO_INTERFACE; 183 | }, 184 | 185 | /** 186 | * This method may be queried to determine whether or not the component 187 | * module can be unloaded by XPCOM. 188 | * @param aCompMgr the global component manager. 189 | * @return true if the module can be unloaded by XPCOM. false otherwise. 190 | */ 191 | canUnload: function(aCompMgr) { 192 | return true; 193 | } 194 | }; 195 | 196 | /** 197 | * Initial entry point Firefox 3.x 198 | * @param aCompMgr the global component manager. 199 | * @param aFileSpec component file. 200 | * @return the module for the service. 201 | */ 202 | function NSGetModule(aCompMgr, aFileSpec) { 203 | return CopperProtocol.HandlerModule; 204 | } 205 | 206 | /** 207 | * Initial entry point Firefox 4 208 | * @param cid the class ID from the manifest. 209 | * @return the module for the service. 210 | */ 211 | function NSGetFactory (cid) { 212 | for(var i in CopperProtocol.CLASS_ID) { 213 | if (cid.equals(CopperProtocol.CLASS_ID[0])) { 214 | return new CopperProtocol.HandlerFactory(); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /content/DebugOptions.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Debug options sidebar functions 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | // Debug options functions 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | Copper.initDebugContentFormats = function() { 42 | for (var i = 0; i<100; ++i) { 43 | var name = ''; 44 | if ( (name = Copper.getContentFormatName(i))!='unknown/unknown') { 45 | 46 | var menuitem1 = document.createElement('menuitem'); 47 | var menuitem2 = document.createElement('menuitem'); 48 | 49 | menuitem1.setAttribute('label', Copper.getContentFormatName(i)); 50 | menuitem1.setAttribute('value', i); 51 | menuitem2.setAttribute('label', Copper.getContentFormatName(i)); 52 | menuitem2.setAttribute('value', i); 53 | 54 | document.getElementById('popup_content_types').appendChild(menuitem1); 55 | document.getElementById('popup_accepts').appendChild(menuitem2); 56 | } 57 | } 58 | }; 59 | 60 | Copper.loadDebugOptions = function() { 61 | document.getElementById('chk_debug_options').checked = Copper.prefManager.getBoolPref('extensions.copper.debug.options-enabled'); 62 | document.getElementById('debug_option_content_format').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.content-format'); 63 | document.getElementById('debug_option_max_age').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.max-age'); 64 | document.getElementById('debug_option_proxy_uri').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.proxy-uri'); 65 | document.getElementById('debug_option_proxy_scheme').checked = Copper.prefManager.getBoolPref('extensions.copper.debug.options.proxy-scheme'); 66 | document.getElementById('debug_option_etag').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.etag'); 67 | document.getElementById('debug_option_uri_host').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.uri-host'); 68 | document.getElementById('debug_option_location_path').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.location-path'); 69 | document.getElementById('debug_option_uri_port').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.uri-port'); 70 | document.getElementById('debug_option_location_query').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.location-query'); 71 | document.getElementById('debug_option_observe').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.observe'); 72 | document.getElementById('debug_option_token').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.token'); 73 | document.getElementById('debug_option_accept').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.accept'); 74 | document.getElementById('debug_option_if_match').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.if-match'); 75 | document.getElementById('debug_option_block2').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.block2'); 76 | document.getElementById('debug_option_block1').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.block1'); 77 | document.getElementById('debug_option_size2').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.size2'); 78 | document.getElementById('debug_option_size1').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.size1'); 79 | document.getElementById('chk_debug_option_block_auto').checked = Copper.prefManager.getBoolPref('extensions.copper.debug.options.block-auto'); 80 | document.getElementById('debug_option_if_none_match').checked = Copper.prefManager.getBoolPref('extensions.copper.debug.options.if-none-match'); 81 | 82 | document.getElementById('debug_option_custom_number').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.custom-number'); 83 | document.getElementById('debug_option_custom_value').value = Copper.prefManager.getCharPref('extensions.copper.debug.options.custom-value'); 84 | }; 85 | 86 | Copper.saveDebugOptions = function() { 87 | Copper.prefManager.setBoolPref('extensions.copper.debug.options-enabled', document.getElementById('chk_debug_options').checked); 88 | Copper.prefManager.setCharPref('extensions.copper.debug.options.content-format', document.getElementById('debug_option_content_format').value); 89 | Copper.prefManager.setCharPref('extensions.copper.debug.options.max-age', document.getElementById('debug_option_max_age').value); 90 | Copper.prefManager.setCharPref('extensions.copper.debug.options.proxy-uri', document.getElementById('debug_option_proxy_uri').value); 91 | Copper.prefManager.setBoolPref('extensions.copper.debug.options.proxy-scheme', document.getElementById('debug_option_proxy_scheme').checked); 92 | Copper.prefManager.setCharPref('extensions.copper.debug.options.etag', document.getElementById('debug_option_etag').value); 93 | Copper.prefManager.setCharPref('extensions.copper.debug.options.uri-host', document.getElementById('debug_option_uri_host').value); 94 | Copper.prefManager.setCharPref('extensions.copper.debug.options.location-path', document.getElementById('debug_option_location_path').value); 95 | Copper.prefManager.setCharPref('extensions.copper.debug.options.uri-port', document.getElementById('debug_option_uri_port').value); 96 | Copper.prefManager.setCharPref('extensions.copper.debug.options.location-query', document.getElementById('debug_option_location_query').value); 97 | Copper.prefManager.setCharPref('extensions.copper.debug.options.observe', document.getElementById('debug_option_observe').value); 98 | Copper.prefManager.setCharPref('extensions.copper.debug.options.token', document.getElementById('debug_option_token').value); 99 | Copper.prefManager.setCharPref('extensions.copper.debug.options.accept', document.getElementById('debug_option_accept').value); 100 | Copper.prefManager.setCharPref('extensions.copper.debug.options.if-match', document.getElementById('debug_option_if_match').value); 101 | Copper.prefManager.setCharPref('extensions.copper.debug.options.block2', document.getElementById('debug_option_block2').value); 102 | Copper.prefManager.setCharPref('extensions.copper.debug.options.block1', document.getElementById('debug_option_block1').value); 103 | Copper.prefManager.setCharPref('extensions.copper.debug.options.size2', document.getElementById('debug_option_size2').value); 104 | Copper.prefManager.setCharPref('extensions.copper.debug.options.size1', document.getElementById('debug_option_size1').value); 105 | Copper.prefManager.setBoolPref('extensions.copper.debug.options.block-auto', document.getElementById('chk_debug_option_block_auto').checked); 106 | Copper.prefManager.setBoolPref('extensions.copper.debug.options.if-none-match', document.getElementById('debug_option_if_none_match').checked); 107 | 108 | Copper.prefManager.setCharPref('extensions.copper.debug.options.custom-number', document.getElementById('debug_option_custom_number').value); 109 | Copper.prefManager.setCharPref('extensions.copper.debug.options.custom-value', document.getElementById('debug_option_custom_value').value); 110 | }; 111 | 112 | Copper.resetDebugOptions = function() { 113 | 114 | var list = document.getElementById('sidebar').getElementsByTagName('image'); 115 | for (var i=0; i "http:" 156 | // parser.hostname; // => "example.com" 157 | // parser.port; // => "3000" 158 | // parser.pathname; // => "/pathname/" 159 | // parser.search; // => "?search=test" 160 | // parser.hash; // => "#hash" 161 | // parser.host; // => "example.com:3000" 162 | 163 | message.setProxyScheme(uri.protocol.slice(0, -1)); 164 | message.setUriHost(uri.hostname); 165 | if (uri.port != '') message.setUriPort(uri.port); 166 | message.setUri(uri.pathname.substr(1) + uri.search); 167 | 168 | } else { 169 | message.setProxyUri(document.getElementById('debug_option_proxy_uri').value); 170 | } 171 | } 172 | 173 | if (document.getElementById('debug_option_etag').value != '') { 174 | message.setETag(document.getElementById('debug_option_etag').value); 175 | } 176 | if (document.getElementById('debug_option_uri_host').value != '') { 177 | message.setUriHost(document.getElementById('debug_option_uri_host').value); 178 | } 179 | if (document.getElementById('debug_option_location_path').value != '') { 180 | message.setLocationPath(document.getElementById('debug_option_location_path').value); 181 | } 182 | 183 | if (document.getElementById('debug_option_uri_port').value != '') { 184 | message.setUriPort(parseInt(document.getElementById('debug_option_uri_port').value)); 185 | } 186 | 187 | if (document.getElementById('debug_option_location_query').value != '') { 188 | message.setLocationQuery(document.getElementById('debug_option_location_query').value); 189 | } 190 | 191 | if (document.getElementById('debug_option_observe').value != '') { 192 | message.setObserve(parseInt(document.getElementById('debug_option_observe').value)); 193 | } 194 | if (document.getElementById('debug_option_token').value != '') { 195 | message.setToken(document.getElementById('debug_option_token').value); 196 | } 197 | 198 | if (document.getElementById('debug_option_accept').value != '') { 199 | if (document.getElementById('debug_option_accept').selectedItem) { 200 | message.setAccept(parseInt(document.getElementById('debug_option_accept').selectedItem.value)); 201 | } else { 202 | message.setAccept(parseInt(document.getElementById('debug_option_accept').value)); 203 | } 204 | } 205 | 206 | if (document.getElementById('debug_option_if_match').value != '') { 207 | message.setIfMatch(document.getElementById('debug_option_if_match').value); 208 | } 209 | 210 | if (Copper.behavior.blockSize != 0 && document.getElementById('debug_option_block2').value != '') { 211 | message.setBlock2(parseInt(document.getElementById('debug_option_block2').value), Copper.behavior.blockSize); 212 | } 213 | 214 | if (Copper.behavior.blockSize != 0 && document.getElementById('debug_option_block1').value != '') { 215 | message.setBlock1(parseInt(document.getElementById('debug_option_block1').value), Copper.behavior.blockSize, document 216 | .getElementById('debug_option_block1').value.match(/\+/)); 217 | } 218 | 219 | if (document.getElementById('debug_option_size2').value != '') { 220 | message.setSize2(parseInt(document.getElementById('debug_option_size2').value)); 221 | } 222 | 223 | if (document.getElementById('debug_option_size1').value != '' && !Copper.behavior.sendSize1) { 224 | message.setSize1(parseInt(document.getElementById('debug_option_size1').value)); 225 | } 226 | 227 | if (Copper.OPTION_IF_NONE_MATCH && document.getElementById('debug_option_if_none_match').checked) { 228 | message.setIfNoneMatch(); 229 | } 230 | 231 | if (document.getElementById('debug_option_custom_number').value != '') { 232 | message.setCustom(document.getElementById('debug_option_custom_number').value, document.getElementById('debug_option_custom_value').value); 233 | } 234 | } 235 | } catch (ex) { 236 | Copper.logError(ex); 237 | } 238 | }; 239 | -------------------------------------------------------------------------------- /content/Handlers.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Message handler functions 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | // CoAP message handlers 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | // Handle normal incoming messages, registered as default at TransactionHandler 42 | Copper.defaultHandler = function(message) { 43 | 44 | Copper.logEvent('INFO: defaultHandler()'); 45 | 46 | // late blocksize negotiation 47 | if (message.isOption(Copper.OPTION_BLOCK2) && Copper.downloadMethod!=0 48 | || message.isOption(Copper.OPTION_BLOCK1) && Copper.uploadMethod!=0) { 49 | return Copper.blockwiseHandler(message); 50 | } 51 | 52 | Copper.displayMessageInfo(message); 53 | Copper.displayPayload(message); 54 | 55 | if (message.getRTT) Copper.updateLabel('info_code', ' (RTT ' + message.getRTT() + ' ms)', true); 56 | 57 | if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { 58 | Copper.updateResourceLinks( Copper.parseLinkFormat( document.getElementById('packet_payload').value ) ); 59 | } 60 | }; 61 | 62 | //Handle ping responses 63 | Copper.pingHandler = function(message) { 64 | Copper.logEvent('INFO: pingHandler()'); 65 | 66 | Copper.displayMessageInfo(message); 67 | Copper.updateLabel('info_code', 'Pong: Remote responds to CoAP'); 68 | if (message.getRTT) Copper.updateLabel('info_code', ' (RTT ' + message.getRTT() + ' ms)', true); 69 | }; 70 | 71 | // Handle messages with block-wise transfer 72 | Copper.blockwiseHandler = function(message) { 73 | Copper.logEvent('INFO: blockwiseHandler()'); 74 | 75 | Copper.displayMessageInfo(message); 76 | Copper.updateLabel('info_code', ' (Blockwise)', true); // call after displayMessageInfo() 77 | Copper.displayPayload(message); 78 | 79 | if (message.isOption(Copper.OPTION_BLOCK1)) { 80 | 81 | // block size negotiation 82 | let size = message.getBlock1Size(); 83 | if (Copper.behavior.blockSize!=0 && Copper.behavior.blockSize < size) { 84 | size = Copper.behavior.blockSize; 85 | } 86 | // calculate offset first to continue with correct num (if block size differs) 87 | let offset = message.getBlock1Size() * (message.getBlock1Number() + 1); 88 | let num = offset/size; 89 | 90 | if (message.isSuccess() && Copper.uploadBlocks!=null && offset < Copper.uploadBlocks.length) { 91 | 92 | Copper.updateLabel('info_code', ' (Uploading...)', true); 93 | 94 | // automatically count up 95 | document.getElementById('debug_option_block1').value = num; 96 | if (offset+size < Copper.uploadBlocks.length) document.getElementById('debug_option_block1').value += '+'; 97 | 98 | if ( !document.getElementById('chk_debug_options').checked || document.getElementById('chk_debug_option_block_auto').checked ) { 99 | Copper.sendBlockwise1(Copper.mainWindow.document.getElementById('urlbar').value, num, size); 100 | return; 101 | } 102 | } else { 103 | // finished 104 | Copper.uploadMethod = 0; 105 | Copper.uploadBlocks = null; 106 | document.getElementById('debug_option_block1').value = ''; // important when continuing with Block1 107 | 108 | Copper.updateLabel('info_code', ' (Upload finished)', true); // call after displayMessageInfo() 109 | 110 | // call custom callback 111 | if (Copper.uploadHandler) { 112 | Copper.uploadHandler(message); 113 | Copper.uploadHandler = null; 114 | } 115 | } 116 | } 117 | 118 | if (message.isOption(Copper.OPTION_BLOCK2)) { 119 | if (message.getBlock2More()) { 120 | 121 | // block size negotiation 122 | let size = Copper.negotiateBlockSize(message); 123 | let offset = message.getBlock2Offset(); 124 | let num = offset/size; 125 | 126 | // automatically count up 127 | document.getElementById('debug_option_block2').value = num; 128 | 129 | if ( !document.getElementById('chk_debug_options').checked || document.getElementById('chk_debug_option_block_auto').checked) { 130 | Copper.sendBlockwise2(Copper.mainWindow.document.getElementById('urlbar').value, num, size); 131 | } 132 | } else { 133 | // finished 134 | Copper.downloadMethod = 0; 135 | document.getElementById('debug_option_block2').value = ''; 136 | 137 | Copper.updateLabel('info_code', ' (Download finished)', true); // call after displayMessageInfo() 138 | 139 | if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { 140 | Copper.updateResourceLinks( Copper.parseLinkFormat( document.getElementById('packet_payload').value ) ); 141 | } 142 | 143 | // call custom callback 144 | if (Copper.downloadHandler) { 145 | Copper.downloadHandler(message); 146 | Copper.downloadHandler = null; 147 | } 148 | } 149 | } 150 | 151 | }; 152 | 153 | //Handle messages with block-wise transfer 154 | Copper.observingHandler = function(message) { 155 | Copper.logEvent('INFO: observingHandler()'); 156 | 157 | Copper.displayMessageInfo(message); 158 | if (message.isOption(Copper.OPTION_OBSERVE)) { 159 | Copper.updateLabel('info_code', ' (Observing)', true); // call after displayMessageInfo() 160 | } else { 161 | Copper.updateLabel('info_code', ' (Observing stopped)', true); // call after displayMessageInfo() 162 | } 163 | Copper.displayPayload(message); 164 | 165 | //TODO duplicated code from blockwise handler 166 | if (message.isOption(Copper.OPTION_BLOCK2)) { 167 | 168 | Copper.updateLabel('info_code', ' (Blockwise)', true); // call after displayMessageInfo() 169 | 170 | if (message.getBlock2More()) { 171 | 172 | Copper.downloadMethod = Copper.GET; 173 | 174 | // block size negotiation 175 | let size = Copper.negotiateBlockSize(message); 176 | let offset = message.getBlock2Offset(); 177 | let num = offset/size; 178 | 179 | Copper.sendBlockwise2(Copper.mainWindow.document.getElementById('urlbar').value, num, size); 180 | } 181 | } 182 | }; 183 | 184 | // Handle messages with link format payload 185 | Copper.discoverCache = new String(); 186 | Copper.discoverHandler = function(message) { 187 | 188 | Copper.logEvent('INFO: discoverHandler()'); 189 | 190 | if (message.getCode()!=Copper.CODE_2_05_CONTENT) return; 191 | 192 | if (message.getContentFormat()==Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT) { 193 | 194 | Copper.updateLabel('info_code', 'Discovering'); 195 | 196 | if (message.isOption(Copper.OPTION_BLOCK2)) { 197 | 198 | if (message.getBlock2More()) { 199 | 200 | // block size negotiation 201 | let size = Copper.negotiateBlockSize(message); 202 | let offset = message.getBlock2Offset(); 203 | let num = offset/size; 204 | Copper.discover(num, size); 205 | } 206 | 207 | if (message.getBlock2Number()==0) { 208 | Copper.logEvent('INFO: Starting new discover cache'); 209 | Copper.discoverCache = new String(); 210 | } 211 | 212 | Copper.discoverCache += Copper.bytes2str( message.getPayload() ); 213 | 214 | if (!message.getBlock2More()) { 215 | Copper.logEvent('INFO: Appending discover cache'); 216 | // link-format 217 | Copper.resourcesCached = false; 218 | Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.discoverCache ) ); 219 | 220 | document.getElementById('toolbar_discover').image = 'chrome://copper/skin/tool_discover.png'; 221 | } 222 | } else { 223 | // link-format 224 | Copper.resourcesCached = false; 225 | Copper.updateResourceLinks( Copper.parseLinkFormat( Copper.bytes2str( message.getPayload() ) ) ); 226 | 227 | document.getElementById('toolbar_discover').image = 'chrome://copper/skin/tool_discover.png'; 228 | } 229 | } else { 230 | Copper.logWarning(new Error("Discovery requires 'application/link-format', but received "+message.getContentFormat())); 231 | } 232 | }; 233 | 234 | Copper.errorHandler = function(message) { 235 | Copper.logEvent('INFO: errorHandler()'); 236 | 237 | document.getElementById('group_head').setAttribute('style', 'display: none;'); 238 | document.getElementById('group_payload').setAttribute('style', 'display: none;'); 239 | 240 | Copper.updateLabel('info_code', message.getCopperCode()); 241 | 242 | Copper.endpoint.cancelTransactions(); 243 | }; 244 | -------------------------------------------------------------------------------- /content/Launcher.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Main program code for the Copper CoAP Browser 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | Copper.check = function() { 39 | 40 | var firstRun = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch).getBoolPref('extensions.copper.first-run'); 41 | 42 | if (firstRun) { 43 | 44 | let id = 'copper-toolbar-button'; 45 | 46 | // install the Cu toolbar button for the launcher 47 | if (!document.getElementById(id)) { 48 | var toolbar = document.getElementById('addon-bar'); 49 | 50 | toolbar.insertItem(id); 51 | toolbar.setAttribute("currentset", toolbar.currentSet); 52 | document.persist(toolbar.id, "currentset"); 53 | 54 | toolbar.collapsed = false; 55 | } 56 | 57 | // set first-run to false 58 | Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch).setBoolPref('extensions.copper.first-run', false); 59 | } 60 | }; 61 | 62 | Copper.hideLauncher = function() { 63 | 64 | let id = 'copper-toolbar-button'; 65 | 66 | if (document.getElementById(id)) { 67 | var toolbar = document.getElementById('addon-bar'); 68 | 69 | let newSet = toolbar.currentSet.replace(/(,copper-toolbar-button|copper-toolbar-button,)/, ''); 70 | toolbar.setAttribute("currentset", newSet); 71 | document.persist(toolbar.id, "currentset"); 72 | } 73 | 74 | Copper.firstRun = false; 75 | }; 76 | 77 | Copper.launch = function() { 78 | window.openDialog('chrome://copper/content/launcher.xul', 'Launcher', 'chrome,titlebar,toolbar,centerscreen,modal'); 79 | Copper.hideLauncher(); 80 | document.getElementById('urlbar').focus(); 81 | }; 82 | 83 | addEventListener("load", Copper.check, false); 84 | -------------------------------------------------------------------------------- /content/Logger.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | *******************************************************************************/ 31 | /** 32 | * \file Main script file for the LWM2M DevKit. 33 | * 34 | * \author Matthias Kovatsch 35 | */ 36 | 37 | Copper.operationLog = []; 38 | Copper.operationReportingLog = []; 39 | 40 | Copper.resetLogger = function() { 41 | let rows = document.getElementById('log_messages'); 42 | let toRemove = rows.getElementsByTagName('listitem'); 43 | for (let r in toRemove) { 44 | try {rows.removeChild(toRemove[r]);} catch (ex) {}; 45 | } 46 | }; 47 | 48 | Copper.logMessage = function(message, out) { 49 | 50 | let rows = document.getElementById('log_messages'); 51 | 52 | var item = document.createElement('listitem'); 53 | item.setAttribute('allowevents', "true"); 54 | item.style.backgroundColor = out ? '#AACCFF' : '#CCFFCC'; 55 | 56 | var cell = document.createElement('listcell'); 57 | cell.setAttribute('label', new Date().toLocaleTimeString()); // timestamp 58 | item.appendChild(cell); 59 | 60 | cell = document.createElement('listcell'); 61 | cell.setAttribute('label', message.getType(true)+'-'+message.getCode(true)); // type 62 | item.appendChild(cell); 63 | 64 | cell = document.createElement('listcell'); 65 | cell.setAttribute('label', message.getMID() + (message.isConfirmable() ? (' (' + message.getRetries() + ')') : '')); 66 | item.appendChild(cell); 67 | 68 | cell = document.createElement('listcell'); 69 | cell.setAttribute('label', message.getToken()); 70 | cell.tooltipText = message.getToken(); 71 | item.appendChild(cell); 72 | 73 | cell = document.createElement('listcell'); 74 | cell.setAttribute('label', message.getOptions(true)); 75 | cell.tooltipText = message.getOptions(true); 76 | item.appendChild(cell); 77 | 78 | cell = document.createElement('listcell'); 79 | cell.setAttribute('label', message.getPayloadText()); 80 | cell.tooltipText = message.getPayloadText(); 81 | item.appendChild(cell); 82 | 83 | rows.appendChild( item ); 84 | 85 | rows.ensureElementIsVisible(item); 86 | }; 87 | 88 | Copper.bytes2hexedit = function(bytes) { 89 | 90 | if (bytes==null) return ''; 91 | 92 | let str =''; 93 | let show = ''; 94 | for (let b in bytes) { 95 | str += Copper.Copper.leadingZero(bytes[b].toString(16)); 96 | show += bytes[b]<32 ? '·' : String.fromCharCode(bytes[b]); 97 | if (b % 16 == 15) { 98 | str += " | "; 99 | str += show; 100 | str += '\n'; 101 | show = ''; 102 | } else { 103 | str += ' '; 104 | } 105 | } 106 | return str; 107 | } 108 | 109 | Copper.logEvent = function(text) { 110 | document.getElementById('log_event_log').value += text + '\n'; 111 | }; 112 | 113 | Copper.logWarning = function(text) { 114 | Copper.logEvent('WARNING: ' + text); 115 | window.setTimeout( 116 | function() { alert('WARNING: '+ text); }, 117 | 0); 118 | }; 119 | 120 | Copper.logError = function(error, skip) { 121 | var message = 'ERROR: ' + error.message; 122 | if (!skip && error.stack) { 123 | message += '\n\t' + error.stack.replace(/\n/, '\n\t'); 124 | } 125 | Copper.logEvent(message); 126 | if (Copper.endpoint) { 127 | Copper.endpoint.cancelTransactions(); 128 | } 129 | window.setTimeout( 130 | function(msg) { alert(msg); }, 131 | 0, message); 132 | }; 133 | 134 | Copper.debug = function(object) { 135 | let str = ""; 136 | for (let x in object) str += x+": "+object[x]+"\n-----\n"; 137 | alert(str); 138 | }; 139 | -------------------------------------------------------------------------------- /content/Main.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Main program code for the Copper CoAP Browser 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | // file IO 39 | Components.utils.import("resource://gre/modules/NetUtil.jsm"); 40 | 41 | Copper.mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) 42 | .getInterface(Components.interfaces.nsIWebNavigation) 43 | .QueryInterface(Components.interfaces.nsIDocShellTreeItem) 44 | .rootTreeItem 45 | .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 46 | .getInterface(Components.interfaces.nsIDOMWindow); 47 | 48 | Copper.prefManager = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch); 49 | 50 | Copper.hostname = ''; 51 | Copper.port = -1; 52 | Copper.path = '/'; 53 | Copper.query = ''; 54 | 55 | Copper.endpoint = null; 56 | Copper.observer = null; 57 | 58 | Copper.resources = new Object(); 59 | Copper.resourcesCached = true; 60 | 61 | Copper.uploadMethod = 0; 62 | Copper.uploadBlocks = null; 63 | Copper.uploadHandler = null; 64 | 65 | Copper.downloadMethod = 0; 66 | Copper.downloadHandler = null; 67 | 68 | Copper.behavior = { 69 | requests: 'con', 70 | retransmission: true, 71 | sendDuplicates: false, 72 | showUnknown: false, 73 | rejectUnknown: true, 74 | sendUriHost: false, 75 | sendSize1: false, 76 | blockSize: 0, 77 | observeToken: true, 78 | observeCancellation: 'lazy' 79 | }; 80 | 81 | Copper.payload = { 82 | mode: 'text', 83 | file: '', 84 | loaded: false, 85 | data: null 86 | }; 87 | 88 | 89 | // Life cycle functions 90 | //////////////////////////////////////////////////////////////////////////////// 91 | 92 | Copper.main = function() { 93 | 94 | Copper.logEvent('=============================================================================='); 95 | Copper.logEvent('= INITIALIZING COPPER ========================================================'); 96 | Copper.logEvent('=============================================================================='); 97 | 98 | window.addEventListener('beforeunload', function(event) { Copper.beforeunload(event); }); 99 | window.addEventListener('unload', function(event) { Copper.unload(event); }); 100 | 101 | // get settings from preferences 102 | var onloadAction = null; 103 | try { 104 | 105 | document.getElementById('view_tree_split').setAttribute('state', Copper.prefManager.getBoolPref('extensions.copper.view-tree') ? 'open' : 'collapsed'); 106 | document.getElementById('view_debug_split').setAttribute('state', Copper.prefManager.getBoolPref('extensions.copper.view-debug') ? 'open' : 'collapsed'); 107 | document.getElementById('view_log_split').setAttribute('state', Copper.prefManager.getBoolPref('extensions.copper.view-log') ? 'open' : 'collapsed'); 108 | 109 | onloadAction = Copper.prefManager.getCharPref('extensions.copper.onload-action'); 110 | 111 | Copper.loadWindow(); 112 | Copper.loadBehavior(); 113 | Copper.loadDebugOptions(); 114 | Copper.initDebugContentFormats(); 115 | 116 | if (Copper.prefManager.getBoolPref('extensions.copper.plugtest.menu')) { 117 | document.getElementById('menu_plugtest').hidden = false; 118 | Copper.loadPlugtest(); 119 | } 120 | 121 | if (Copper.prefManager.getBoolPref('extensions.copper.enable-etch')) { 122 | document.getElementById('toolbar_fetch').hidden = false; 123 | document.getElementById('toolbar_patch').hidden = false; 124 | document.getElementById('toolbar_ipatch').hidden = false; 125 | } 126 | 127 | } catch (ex) { 128 | Copper.logError(ex); 129 | } 130 | 131 | // open location 132 | try { 133 | Copper.parseUri(document.location.href); 134 | 135 | // set up datagram and transaction layer 136 | Copper.endpoint = new Copper.TransactionHandler(new Copper.UdpClient(Copper.hostname, Copper.port), Copper.behavior.retransmissions); 137 | Copper.endpoint.registerCallback(Copper.defaultHandler); 138 | 139 | // enable observing 140 | Copper.observer = new Copper.Observing(); 141 | 142 | Copper.loadCachedResources(); 143 | Copper.updateResourceLinks(); 144 | 145 | Copper.loadPayload(); 146 | 147 | // handle auto-request after redirect 148 | if (onloadAction!='') { 149 | 150 | Copper.logEvent('INFO: onloadAction defined ('+onloadAction+')'); 151 | 152 | window.setTimeout( 153 | function(action) { 154 | switch (action) { 155 | case 'userObserve': Copper.userObserve(); break; 156 | default: Copper.userRequest(action); break; 157 | } 158 | }, 159 | 0, onloadAction); 160 | 161 | // reset onloadAction 162 | Copper.prefManager.setCharPref('extensions.copper.onload-action', ''); 163 | } 164 | 165 | Copper.updateLabel('info_code', "Opened " + decodeURI(document.location.href)); 166 | 167 | } catch (ex) { 168 | Copper.errorHandler({getCopperCode:function(){return ex.message;}, getPayload:function(){return ex.stacktrace;}}); 169 | Copper.logEvent('ERROR: ' + ex.message + '\n\t' + ex.stack.replace(/\n/, '\n\t')); 170 | } 171 | }; 172 | 173 | 174 | Copper.beforeunload = function(event) { 175 | 176 | if (Copper.observer.subscription!=null) { 177 | Copper.logEvent('WARNING: Leaving resource while observing'); 178 | Copper.updateLabel('info_code', "Copper: Still observing resource"); 179 | event.preventDefault(); 180 | } 181 | }; 182 | 183 | Copper.unload = function(event) { 184 | 185 | if (Copper.observer.subscription!=null) { 186 | Copper.logEvent('INFO: Canceling Observe in unload handler'); 187 | Copper.observer.unsubscribe(true); 188 | } 189 | 190 | // shut down socket required for refresh (F5), client might be null for parseUri() redirects 191 | if (Copper.endpoint!=null) { 192 | Copper.endpoint.shutdown(); 193 | } 194 | 195 | Copper.saveBehavior(); 196 | Copper.savePayload(); 197 | Copper.saveDebugOptions(); 198 | Copper.savePlugtest(); 199 | Copper.saveWindow(); 200 | }; 201 | 202 | 203 | // Toolbar commands 204 | //////////////////////////////////////////////////////////////////////////////// 205 | 206 | Copper.userRequest = function(method) { 207 | Copper.endpoint.cancelTransactions(); 208 | var uri = Copper.checkUri(null, method); 209 | 210 | Copper.sendRequest(method, uri); 211 | }; 212 | 213 | Copper.userObserve = function() { 214 | var uri = Copper.checkUri(null, 'userObserve'); 215 | 216 | Copper.observe(uri); 217 | }; 218 | 219 | Copper.userDiscover = function() { 220 | Copper.logEvent('INFO: resetting cached resources'); 221 | document.getElementById('toolbar_discover').image = 'chrome://copper/skin/spinner.gif'; 222 | Copper.prefManager.setCharPref('extensions.copper.resources.'+Copper.hostname+':'+Copper.port, '' ); 223 | Copper.resources = new Object(); 224 | 225 | Copper.discover(); 226 | }; 227 | 228 | 229 | // Request commands 230 | //////////////////////////////////////////////////////////////////////////////// 231 | 232 | Copper.sendRequest = function(method, uri, callback) { 233 | if (!uri) throw new Error('No URI specified'); 234 | 235 | Copper.downloadMethod = method; 236 | 237 | if (method == Copper.GET || method == Copper.DELETE) { 238 | 239 | if (Copper.behavior.blockSize!=0) { 240 | Copper.sendBlockwise2(uri, parseInt(document.getElementById('debug_option_block2').value), Copper.behavior.blockSize, callback); 241 | return; 242 | } 243 | 244 | try { 245 | var message = new Copper.CoapMessage(Copper.getRequestType(), method, uri); 246 | 247 | Copper.checkDebugOptions(message); 248 | 249 | Copper.clearLabels(); 250 | Copper.endpoint.send( message, callback ); 251 | } catch (ex) { 252 | Copper.logError(ex); 253 | } 254 | 255 | } else { 256 | 257 | var num = parseInt(document.getElementById('debug_option_block2').value); 258 | 259 | if (Copper.downloadMethod == method && num>0) { 260 | Copper.sendBlockwise2(uri, num, Copper.behavior.blockSize, callback); 261 | } else { 262 | Copper.doUpload(method, uri, callback); 263 | } 264 | } 265 | }; 266 | 267 | Copper.sendBlockwise2 = function(uri, num, size, callback) { 268 | try { 269 | if (!uri) throw new Error('No URI specified'); 270 | if (!num) num = 0; 271 | if (!size) size = Copper.behavior.blockSize; 272 | 273 | if (Copper.downloadMethod==0) { 274 | throw new Error('No download in progress'); 275 | } 276 | 277 | if (callback) Copper.downloadHandler = callback; 278 | 279 | var message = new Copper.CoapMessage(Copper.getRequestType(), Copper.downloadMethod, uri); 280 | 281 | Copper.checkDebugOptions(message); 282 | 283 | // (re)set to useful block option 284 | message.setBlock2(num, size); 285 | 286 | Copper.clearLabels(num==0); 287 | Copper.endpoint.send( message, Copper.blockwiseHandler ); 288 | } catch (ex) { 289 | Copper.logError(ex); 290 | } 291 | }; 292 | 293 | Copper.doUpload = function(method, uri, callback) { 294 | try { 295 | if (uri===undefined) throw new Error('No URI specified'); 296 | 297 | // load payload 298 | let pl = ''; 299 | if (Copper.payload.mode=='text') { 300 | pl = Copper.str2bytes(document.getElementById('payload_text').value); 301 | } else if (Copper.payload.file!='') { 302 | if (!Copper.payload.loaded) { 303 | // file loading as async, wait until done 304 | window.setTimeout(function() {Copper.doUpload(method, uri, callback);}, 50); 305 | return; 306 | } 307 | pl = Copper.data2bytes(Copper.payload.data); 308 | } else { 309 | Copper.logWarning('No payload data defined'); 310 | return; 311 | } 312 | 313 | // store payload in case server requests blockwise upload 314 | Copper.uploadMethod = method; // POST or PUT 315 | Copper.uploadBlocks = pl; 316 | Copper.downloadMethod = method; // in case of Block2 response 317 | 318 | // blockwise uploads 319 | if (Copper.behavior.blockSize!=0 && pl.length > Copper.behavior.blockSize) { 320 | Copper.sendBlockwise1(uri, parseInt(document.getElementById('debug_option_block1').value), Copper.behavior.blockSize, callback); 321 | return; 322 | } 323 | 324 | var message = new Copper.CoapMessage(Copper.getRequestType(), method, uri, pl); 325 | 326 | Copper.checkDebugOptions(message); 327 | 328 | if (Copper.behavior.sendSize1) { 329 | Copper.logEvent('INFO: Send auto Size1 option'); 330 | message.setSize1(pl.length); 331 | document.getElementById('debug_option_size1').value = pl.length; 332 | } 333 | 334 | Copper.clearLabels(); 335 | Copper.endpoint.send( message, callback ); 336 | } catch (ex) { 337 | Copper.logError(ex); 338 | } 339 | } 340 | 341 | Copper.sendBlockwise1 = function(uri, num, size, callback) { 342 | try { 343 | if (uri===undefined) throw new Error('No URI specified'); 344 | if (!num) num = 0; 345 | if (size===undefined) size = Copper.behavior.blockSize; 346 | 347 | if (Copper.uploadBlocks==null || Copper.uploadMethod==0) { 348 | throw new Error('No upload in progress, cancelling'); 349 | } 350 | if ( (num>0) && (size*(num-1) > Copper.uploadBlocks.length)) { // num-1, as we are called with the num to send, not was has been send 351 | throw new Error('Debug Block1 out of payload scope'); 352 | } 353 | 354 | let more = false; 355 | let pl = Copper.uploadBlocks.slice(size*num, size*(num+1)); 356 | 357 | // more blocks? 358 | if (Copper.uploadBlocks.length > (num+1) * size) { // num+1, as we start counting at 0... 359 | more = true; 360 | } 361 | 362 | if (callback) Copper.uploadHandler = callback; 363 | 364 | var message = new Copper.CoapMessage(Copper.getRequestType(), Copper.uploadMethod, uri, pl); 365 | 366 | Copper.checkDebugOptions(message); 367 | 368 | if (Copper.behavior.sendSize1) { 369 | Copper.logEvent('INFO: Send auto Size1 option'); 370 | message.setSize1(Copper.uploadBlocks.length); 371 | document.getElementById('debug_option_size1').value = Copper.uploadBlocks.length; 372 | } 373 | 374 | message.setBlock1(num, size, more); 375 | 376 | Copper.clearLabels(num==0); 377 | Copper.endpoint.send( message, Copper.blockwiseHandler ); 378 | } catch (ex) { 379 | Copper.logError(ex); 380 | } 381 | }; 382 | 383 | Copper.observe = function(uri) { 384 | try { 385 | Copper.observer.subscribe(uri, Copper.observingHandler); 386 | 387 | } catch (ex) { 388 | Copper.logError(ex); 389 | } 390 | }; 391 | 392 | Copper.discover = function(num, size) { 393 | try { 394 | let message = new Copper.CoapMessage(Copper.getRequestType(), Copper.GET, Copper.WELL_KNOWN_RESOURCES); 395 | 396 | if (num!==undefined) { 397 | Copper.logEvent('INFO: Continuing discovery with Block '+num+' size '+size); 398 | if (size===undefined) size = Copper.behavior.blockSize; 399 | message.setBlock2(num, size); 400 | } 401 | 402 | Copper.endpoint.send( message, Copper.discoverHandler ); 403 | } catch (ex) { 404 | Copper.logError(ex); 405 | } 406 | }; 407 | 408 | // Sends a CoAP ping which is an empty CON message 409 | Copper.ping = function() { 410 | try { 411 | Copper.endpoint.cancelTransactions(); 412 | 413 | var message = new Copper.CoapMessage(Copper.MSG_TYPE_CON); 414 | 415 | Copper.clearLabels(); 416 | Copper.endpoint.send( message, Copper.pingHandler ); 417 | } catch (ex) { 418 | Copper.logError(ex); 419 | } 420 | }; -------------------------------------------------------------------------------- /content/Observing.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Code handling Observing Resources 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | Copper.ObserveEntry = function(uri, cb, token) { 39 | this.uri = uri; 40 | this.callback = cb; 41 | if (token!=null) { 42 | this.token = token; 43 | } 44 | 45 | return this; 46 | }; 47 | Copper.ObserveEntry.prototype = { 48 | uri : null, 49 | callback: null, 50 | token : null, 51 | lastMID: -1 52 | }; 53 | 54 | Copper.Observing = function() { 55 | // maybe support multiple subscriptions via sidebar in the future 56 | //this.subscriptions = new Object(); 57 | 58 | return this; 59 | }; 60 | 61 | Copper.Observing.prototype = { 62 | 63 | pending : null, 64 | subscription : null, 65 | 66 | subscribe : function(uri, cb) { 67 | // check for existing subscriptions 68 | if (this.subscription) { 69 | this.unsubscribe(); 70 | return; 71 | } 72 | 73 | Copper.logEvent('INFO: Subscribing to ' + uri); 74 | 75 | var subscribe = new Copper.CoapMessage(Copper.getRequestType(), Copper.GET, uri); 76 | 77 | // add all debug options 78 | Copper.checkDebugOptions(subscribe); 79 | 80 | // set token depending on the behavior config 81 | if (Copper.behavior.observeToken && subscribe.getToken()) { 82 | subscribe.setToken( new Array(parseInt(Math.random()*0x100), parseInt(Math.random()*0x100)) ); 83 | // update debug options 84 | if (document.getElementById('chk_debug_options').checked) { 85 | document.getElementById('debug_option_token').value = subscribe.getToken(); 86 | } 87 | } 88 | 89 | if (Copper.behavior.blockSize!=0) { 90 | subscribe.setBlock2(0, Copper.behavior.blockSize); 91 | } 92 | 93 | this.pending = new Copper.ObserveEntry(uri, cb, subscribe.getToken()); 94 | 95 | var that = this; 96 | Copper.endpoint.registerToken(subscribe.getToken(), Copper.myBind(that, that.handle)); 97 | 98 | try { 99 | 100 | subscribe.setObserve(0); 101 | 102 | var that = this; 103 | Copper.clearLabels(); 104 | Copper.endpoint.send(subscribe, Copper.myBind(that, that.handle)); 105 | } catch (ex) { 106 | Copper.logError(ex); 107 | } 108 | }, 109 | 110 | unsubscribe : function(proactive) { 111 | if (this.subscription) { 112 | 113 | try { 114 | if (Copper.behavior.observeCancellation=='get' || proactive===true) { 115 | 116 | Copper.logEvent('INFO: Unsubscribing ' + this.subscription.uri + ' via proactive cancellation'); 117 | 118 | Copper.downloadMethod = Copper.GET; 119 | 120 | let uri = Copper.checkUri(); // get current URI 121 | var cancel = new Copper.CoapMessage(Copper.MSG_TYPE_CON, Copper.GET, uri); // always use CON 122 | 123 | // add all debug options also for cancel 124 | Copper.checkDebugOptions(cancel); 125 | if (Copper.behavior.blockSize!=0) { 126 | cancel.setBlock2(0, Copper.behavior.blockSize); 127 | } 128 | 129 | cancel.setToken(this.subscription.token); 130 | cancel.setObserve(1); 131 | 132 | Copper.clearLabels(); 133 | var that = this; 134 | Copper.endpoint.send( cancel, Copper.myBind(that, that.handle)); 135 | 136 | } else { 137 | 138 | Copper.logEvent('INFO: Unsubscribing ' + this.subscription.uri + ' via reactive cancellation'); 139 | 140 | // forget token for garbage collection 141 | Copper.endpoint.deRegisterToken(this.subscription.token); 142 | 143 | if (Copper.behavior.observeCancellation=='rst' && this.subscription.lastMID!=-1) { 144 | // Send a RST now 145 | var rst = new Copper.CoapMessage(Copper.MSG_TYPE_RST); 146 | rst.setMID(this.subscription.lastMID); 147 | Copper.endpoint.send( rst ); 148 | } 149 | 150 | this.clean(); 151 | } 152 | 153 | } catch (ex) { 154 | Copper.logError(ex); 155 | } 156 | 157 | Copper.updateLabel('info_code', 'Copper: Canceling Observe', false); // call after displayMessageInfo() 158 | } 159 | }, 160 | 161 | handle : function(message) { 162 | 163 | Copper.logEvent('OBSERVE: handle()'); 164 | 165 | if (message.getCode()==Copper.EMPTY) return; 166 | 167 | if (this.pending) { 168 | 169 | // check if server supports observing this resource 170 | if (message.isOption(Copper.OPTION_OBSERVE)) { 171 | 172 | this.subscription = new Copper.ObserveEntry(this.pending.uri, this.pending.callback, message.getToken()); 173 | delete this.pending; 174 | 175 | document.getElementById('toolbar_observe').image = 'chrome://copper/skin/tool_unobserve.png'; 176 | document.getElementById('toolbar_observe').label = 'Cancel '; 177 | 178 | this.subscription.lastMID = (message.getType()==Copper.MSG_TYPE_NON) ? message.getMID() : -1; 179 | 180 | this.subscription.callback(message); 181 | 182 | } else { 183 | Copper.endpoint.deRegisterToken(this.pending.token); 184 | delete this.pending; 185 | 186 | message.getCopperCode = function() { return 'Resource not observable'; }; 187 | 188 | Copper.defaultHandler(message); 189 | } 190 | 191 | } else if (this.subscription) { 192 | this.subscription.lastMID = (message.getType()==Copper.MSG_TYPE_NON) ? message.getMID() : -1; 193 | 194 | let callback = this.subscription.callback; 195 | 196 | if (message.getObserve()==null || !message.isSuccess()) { 197 | Copper.endpoint.deRegisterToken(message.getToken()); 198 | this.clean(); 199 | } 200 | 201 | callback(message); 202 | 203 | } else { 204 | Copper.logError(new Error('Illegal Observe state')); 205 | } 206 | }, 207 | 208 | clean : function() { 209 | delete this.subscription; 210 | 211 | document.getElementById('toolbar_observe').image = 'chrome://copper/skin/tool_observe.png'; 212 | document.getElementById('toolbar_observe').label = 'Observe'; 213 | } 214 | }; 215 | -------------------------------------------------------------------------------- /content/Options.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Code for the options window. 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | Copper.Options = { 39 | 40 | clearResourceCache : function() { 41 | var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.copper.resources"); 42 | 43 | try { 44 | if (confirm('This will delete '+prefs.getChildList('', {}).length+' entries. Continue?')) { 45 | prefs.deleteBranch(''); 46 | } 47 | } catch (ex) { 48 | alert(ex); 49 | } 50 | }, 51 | 52 | clearPayloadCache : function() { 53 | var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("extensions.copper.payloads"); 54 | 55 | try { 56 | if (confirm('This will delete '+prefs.getChildList('', {}).length+' entries. Continue?')) { 57 | prefs.deleteBranch(''); 58 | } 59 | } catch (ex) { 60 | alert(ex); 61 | } 62 | } 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /content/Renderers.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Content-type rendering functions 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | // Rendering functions 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | Copper.renderText = function(message) { 42 | Copper.updateLabel('packet_payload', Copper.bytes2str(message.getPayload()), false); 43 | let str = Copper.bytes2str(message.getPayload()); 44 | if (str.match(/^#[0-9a-f]{3,6}$/i) || str.match(/´rgb\(\s*[0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*\)$/i)) { 45 | 46 | // view corresponding render element 47 | document.getElementById('rendered_img').style.display = 'none'; 48 | document.getElementById('rendered_div').style.display = 'block'; 49 | 50 | document.getElementById('tab_rendered').style.backgroundColor = str.toLowerCase(); 51 | document.getElementById('tabs_payload').selectedIndex = 1; 52 | } else { 53 | document.getElementById('tab_rendered').style.backgroundColor = ''; 54 | document.getElementById('tabs_payload').selectedIndex = 0; 55 | } 56 | }; 57 | 58 | Copper.renderImage = function(message) { 59 | 60 | if (!message.getBlock2More()) { 61 | // only render binary when transfer is complete (binary is heavy) 62 | Copper.renderBinary(message); 63 | } 64 | 65 | // view corresponding render element 66 | document.getElementById('rendered_div').style.display = 'none'; 67 | document.getElementById('rendered_img').style.display = 'block'; 68 | 69 | // causes flickering, but partially added data does not draw anyway 70 | document.getElementById('rendered_img').src = 'data:'+Copper.getContentFormatName(message.getContentFormat())+';base64,'+btoa( Copper.bytes2data(message.getPayload()) ); 71 | document.getElementById('tab_rendered').style.backgroundColor = ''; 72 | document.getElementById('tabs_payload').selectedIndex = 1; 73 | }; 74 | 75 | Copper.renderBinary = function(message) { 76 | 77 | var pl = message.getPayload(); 78 | 79 | // Clear content first 80 | Copper.updateLabel('packet_payload', '', false); 81 | var line = ''; 82 | for (var i in pl) { 83 | line += Copper.leadingZero(pl[i].toString(16).toUpperCase()); 84 | 85 | if (i % 16 == 15) { 86 | line += ' | '; 87 | for (var j=i-15; j<=i; ++j) { 88 | if (pl[j] < 32 || pl[j] >= 127) { 89 | line += '·'; 90 | } else { 91 | line += String.fromCharCode(pl[j] & 0xFF); 92 | } 93 | } 94 | line += '\n'; 95 | Copper.updateLabel('packet_payload', line, true); 96 | line = ''; 97 | } else if (i % 2 == 1) { 98 | line += ' '; 99 | } 100 | } 101 | 102 | // incomplete lines 103 | if ((parseInt(i)+1) % 16 != 0) { 104 | // complete line with spaces 105 | for (var j=0; j<39-((parseInt(i)+1)%16)*2 - ((parseInt(i)+1)%16)/2; ++j) { 106 | line += ' '; 107 | } 108 | 109 | line += ' | '; 110 | for (var j=i-(i%16); j<=i; ++j) { 111 | if (pl[j] < 32) { 112 | line += '·'; 113 | } else { 114 | line += String.fromCharCode(pl[j] & 0xFF); 115 | } 116 | } 117 | Copper.updateLabel('packet_payload', line, true); 118 | } 119 | 120 | document.getElementById('tabs_payload').selectedIndex = 0; 121 | }; 122 | 123 | 124 | Copper.renderLinkFormat = function(message) { 125 | 126 | // Print raw Link Format in case parsing fails 127 | Copper.renderText(message); 128 | 129 | // The box for output at the top-level 130 | document.getElementById('rendered_img').style.display = 'none'; 131 | var view = document.getElementById('rendered_div'); 132 | view.style.display = 'block'; 133 | 134 | while (view.hasChildNodes()) { 135 | view.removeChild(view.firstChild); 136 | } 137 | view.setAttribute("class", "link-content"); 138 | 139 | var parsedObj = Copper.parseLinkFormat( Copper.bytes2str(message.getPayload()) ); 140 | 141 | view.appendChild( Copper.renderLinkFormatUtils.getXulLinks(parsedObj) ); 142 | 143 | document.getElementById('tab_rendered').style.backgroundColor = ''; 144 | document.getElementById('tabs_payload').selectedIndex = 1; 145 | }; 146 | 147 | Copper.renderLinkFormatUtils = { 148 | 149 | htmlns: "http://www.w3.org/1999/xhtml", 150 | 151 | getXulLinks: function(value) { 152 | if (typeof value != 'object') { 153 | return null; 154 | } 155 | 156 | var xulObj = document.createElementNS(this.htmlns, "ul"); 157 | for (var uri in value) { 158 | this.addXulLink(xulObj, value[uri], uri); 159 | } 160 | 161 | return xulObj; 162 | }, 163 | 164 | addXulLink: function(xulObj, attribs, key) { 165 | 166 | var xulChild = document.createElementNS(this.htmlns, "li"); 167 | 168 | var label = document.createElement("label"); 169 | label.setAttribute("class", "uri"); 170 | label.setAttribute("value", key); 171 | xulChild.appendChild(label); 172 | xulChild.appendChild( this.getXulObject(attribs) ); 173 | 174 | xulObj.appendChild(xulChild); 175 | }, 176 | 177 | getXulObject: function(value) { 178 | 179 | if (typeof value != 'object') { 180 | return null; 181 | } 182 | 183 | var xulObj = document.createElementNS(this.htmlns, "ul"); 184 | 185 | if (Array.isArray(value)) { 186 | xulObj.setAttribute("class", "array"); 187 | for (var i = 0; i < value.length; i ++) { 188 | this.addXulChild(xulObj, value[i]); 189 | } 190 | } else { 191 | // object 192 | xulObj.setAttribute("class", "object"); 193 | for (var prop in value) { 194 | this.addXulChild(xulObj, value[prop], prop); 195 | } 196 | } 197 | 198 | return xulObj; 199 | }, 200 | 201 | addXulChild: function(xulObj, value, key) { 202 | 203 | var xulChild = document.createElementNS(this.htmlns, "li"); 204 | 205 | // If the value has a label (object properties will have labels) 206 | if (key != null) { 207 | var label = document.createElement("label"); 208 | label.setAttribute("class", "label"); 209 | label.setAttribute("value", key + ":"); 210 | xulChild.appendChild(label); 211 | } 212 | 213 | if (typeof value == 'object' && value != null) { 214 | xulChild.appendChild( this.getXulObject(value) ); 215 | } else { 216 | xulChild.appendChild( this.getXulValue(value) ); 217 | } 218 | 219 | xulObj.appendChild(xulChild); 220 | }, 221 | 222 | getXulValue: function(value) { 223 | var xulObj = document.createElement("description"); 224 | switch (typeof value) { 225 | case 'object': 226 | if (!value) { 227 | xulObj.setAttribute("value", 'null'); 228 | xulObj.setAttribute("class", "null"); 229 | return xulObj; 230 | } 231 | return null; 232 | 233 | case 'string': 234 | xulObj.appendChild( document.createTextNode(String(value)) ); 235 | xulObj.setAttribute("class", "string"); 236 | return xulObj; 237 | 238 | case 'number': 239 | xulObj.setAttribute("value", isFinite(value) ? String(value) : 'null'); 240 | if (Math.floor(value) == value) { 241 | xulObj.setAttribute("class", "int"); 242 | } else { 243 | xulObj.setAttribute("class", "float"); 244 | } 245 | return xulObj; 246 | 247 | case 'boolean': 248 | xulObj.setAttribute("value", String(value)); 249 | xulObj.setAttribute("class", "bool"); 250 | return xulObj; 251 | 252 | case 'null': 253 | xulObj.setAttribute("value", String(value)); 254 | xulObj.setAttribute("class", "null"); 255 | return xulObj; 256 | 257 | default: 258 | return null; 259 | } 260 | } 261 | }; 262 | 263 | Copper.renderCBOR = function(message) { 264 | // The box for output at the top-level 265 | document.getElementById('rendered_img').style.display = 'none'; 266 | 267 | var view = document.getElementById('rendered_div'); 268 | view.style.display = 'block'; 269 | 270 | while (view.hasChildNodes()) { 271 | view.removeChild(view.firstChild); 272 | } 273 | 274 | view.setAttribute("class", "json-content"); 275 | var parsedObj = Copper.parseCBOR(message.getPayload()); 276 | 277 | // Turn the Javascript object into XUL objects 278 | if (typeof parsedObj == 'object') { 279 | view.appendChild( Copper.renderJSONutils.getXulObject(parsedObj) ); 280 | document.getElementById('tab_rendered').style.backgroundColor = ''; 281 | document.getElementById('tabs_payload').selectedIndex = 1; 282 | } else { 283 | Copper.logError(new Error('Top level element is not an object')); 284 | } 285 | }; 286 | 287 | Copper.renderJSON = function(message) { 288 | 289 | // Print raw JSON in case parsing fails 290 | Copper.renderText(message); 291 | 292 | // The box for output at the top-level 293 | document.getElementById('rendered_img').style.display = 'none'; 294 | var view = document.getElementById('rendered_div'); 295 | view.style.display = 'block'; 296 | 297 | while (view.hasChildNodes()) { 298 | view.removeChild(view.firstChild); 299 | } 300 | view.setAttribute("class", "json-content"); 301 | 302 | var pl = Copper.bytes2str(message.getPayload()).replace(/'/g, '"'); 303 | 304 | try { 305 | // Parse the JSON 306 | var parsedObj = JSON.parse(pl); 307 | // Turn the Javascript object into XUL objects 308 | if (typeof parsedObj == 'object') { 309 | view.appendChild( Copper.renderJSONutils.getXulObject(parsedObj) ); 310 | document.getElementById('tab_rendered').style.backgroundColor = ''; 311 | document.getElementById('tabs_payload').selectedIndex = 1; 312 | } else { 313 | Copper.logError(new Error('Top level element is not a JSON object')); 314 | } 315 | } catch (ex) { 316 | Copper.logError(ex); 317 | } 318 | 319 | }; 320 | 321 | Copper.renderJSONutils = { 322 | 323 | htmlns: "http://www.w3.org/1999/xhtml", 324 | 325 | getXulObject: function(value) { 326 | if (typeof value != 'object') { 327 | return null; 328 | } 329 | 330 | var xulObj = document.createElementNS(this.htmlns, "ul"); 331 | 332 | if (Array.isArray(value)) { 333 | xulObj.setAttribute("class", "array"); 334 | 335 | if (value.length>0) { 336 | var label = document.createElement("label"); 337 | label.setAttribute("value", "(length " + value.length + ")"); 338 | label.setAttribute("style", "color: gray;"); 339 | xulObj.appendChild(label); 340 | 341 | for (var i = 0; i < value.length; i ++) { 342 | this.addXulChild(xulObj, value[i]); 343 | } 344 | } else { 345 | var label = document.createElement("label"); 346 | label.setAttribute("value", " "); 347 | xulObj.appendChild(label); 348 | } 349 | 350 | } else { 351 | // object 352 | xulObj.setAttribute("class", "object"); 353 | for (var prop in value) { 354 | this.addXulChild(xulObj, value[prop], prop); 355 | } 356 | } 357 | 358 | return xulObj; 359 | }, 360 | 361 | addXulChild: function(xulObj, value, key) { 362 | 363 | var xulChild = document.createElementNS(this.htmlns, "li"); 364 | 365 | // If the value has a label (object properties will have labels) 366 | if (key != null) { 367 | var label = document.createElement("label"); 368 | label.setAttribute("class", "label"); 369 | label.setAttribute("value", key + ":"); 370 | xulChild.appendChild(label); 371 | } 372 | 373 | if(value instanceof Uint8Array) { 374 | value = JSON.stringify(value); 375 | } 376 | 377 | if (typeof value == 'object' && value != null) { 378 | xulChild.appendChild( this.getXulObject(value) ); 379 | } else { 380 | xulChild.appendChild( this.getXulValue(value) ); 381 | } 382 | 383 | xulObj.appendChild(xulChild); 384 | }, 385 | 386 | getXulValue: function(value) { 387 | var xulObj = document.createElement("description"); 388 | switch (typeof value) { 389 | case 'object': 390 | if (!value) { 391 | xulObj.setAttribute("value", 'null'); 392 | xulObj.setAttribute("class", "null"); 393 | return xulObj; 394 | } 395 | return null; 396 | 397 | case 'string': 398 | xulObj.appendChild( document.createTextNode(String(value)) ); 399 | xulObj.setAttribute("class", "string"); 400 | return xulObj; 401 | 402 | case 'number': 403 | xulObj.setAttribute("value", isFinite(value) ? String(value) : 'null'); 404 | if (Math.floor(value) == value) { 405 | xulObj.setAttribute("class", "int"); 406 | } else { 407 | xulObj.setAttribute("class", "float"); 408 | } 409 | return xulObj; 410 | 411 | case 'boolean': 412 | xulObj.setAttribute("value", String(value)); 413 | xulObj.setAttribute("class", "bool"); 414 | return xulObj; 415 | 416 | case 'null': 417 | xulObj.setAttribute("value", String(value)); 418 | xulObj.setAttribute("class", "null"); 419 | return xulObj; 420 | 421 | default: 422 | return null; 423 | } 424 | } 425 | }; 426 | 427 | Copper.renderEXI = function(message) { 428 | Copper.updateLabel('packet_payload', Copper.bytes2data(message.getPayload()), message.getBlock2Number()>0); 429 | document.getElementById('tabs_payload').selectedIndex = 0; 430 | }; 431 | -------------------------------------------------------------------------------- /content/ResourceViews.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Views for resources 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | 39 | // Tree view 40 | //////////////////////////////////////////////////////////////////////////////// 41 | 42 | Copper.clearTree = function() { 43 | var elems = document.getElementById('resource_elems'); 44 | while (elems.hasChildNodes()) { 45 | elems.removeChild(elems.firstChild); 46 | } 47 | }; 48 | 49 | Copper.addTreeResource = function(uri, attributes) { 50 | 51 | var tree = document.getElementById('resource_tree'); 52 | var segments; 53 | 54 | var uriTokens = uri.match(/([a-zA-Z]+:\/\/)([^\/]+)(.*)/); 55 | 56 | if (uriTokens) { 57 | // absolute URI 58 | 59 | if (uriTokens[1]=='coap://') { 60 | segments = uriTokens[3].split('/'); 61 | segments.shift(); 62 | segments.unshift(uriTokens[2]); 63 | } else { 64 | Copper.logEvent("WARNING: Non-CoAP resource "+uri+"'"); 65 | return; 66 | } 67 | } else { 68 | segments = uri.split('/'); 69 | segments.shift(); 70 | segments.unshift(Copper.hostname + ':' + Copper.port); 71 | } 72 | 73 | var node = tree; 74 | //Copper.logEvent('Children: '+node.getElementsByTagName('treechildren')[0].childNodes.length); 75 | 76 | for (var i=0; i 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | (function(global, undefined) { "use strict"; 26 | var POW_2_24 = Math.pow(2, -24), 27 | POW_2_32 = Math.pow(2, 32), 28 | POW_2_53 = Math.pow(2, 53); 29 | 30 | function encode(value) { 31 | var data = new ArrayBuffer(256); 32 | var dataView = new DataView(data); 33 | var lastLength; 34 | var offset = 0; 35 | 36 | function ensureSpace(length) { 37 | var newByteLength = data.byteLength; 38 | var requiredLength = offset + length; 39 | while (newByteLength < requiredLength) 40 | newByteLength *= 2; 41 | if (newByteLength !== data.byteLength) { 42 | var oldDataView = dataView; 43 | data = new ArrayBuffer(newByteLength); 44 | dataView = new DataView(data); 45 | var uint32count = (offset + 3) >> 2; 46 | for (var i = 0; i < uint32count; ++i) 47 | dataView.setUint32(i * 4, oldDataView.getUint32(i * 4)); 48 | } 49 | 50 | lastLength = length; 51 | return dataView; 52 | } 53 | function write() { 54 | offset += lastLength; 55 | } 56 | function writeFloat64(value) { 57 | write(ensureSpace(8).setFloat64(offset, value)); 58 | } 59 | function writeUint8(value) { 60 | write(ensureSpace(1).setUint8(offset, value)); 61 | } 62 | function writeUint8Array(value) { 63 | var dataView = ensureSpace(value.length); 64 | for (var i = 0; i < value.length; ++i) 65 | dataView.setUint8(offset + i, value[i]); 66 | write(); 67 | } 68 | function writeUint16(value) { 69 | write(ensureSpace(2).setUint16(offset, value)); 70 | } 71 | function writeUint32(value) { 72 | write(ensureSpace(4).setUint32(offset, value)); 73 | } 74 | function writeUint64(value) { 75 | var low = value % POW_2_32; 76 | var high = (value - low) / POW_2_32; 77 | var dataView = ensureSpace(8); 78 | dataView.setUint32(offset, high); 79 | dataView.setUint32(offset + 4, low); 80 | write(); 81 | } 82 | function writeTypeAndLength(type, length) { 83 | if (length < 24) { 84 | writeUint8(type << 5 | length); 85 | } else if (length < 0x100) { 86 | writeUint8(type << 5 | 24); 87 | writeUint8(length); 88 | } else if (length < 0x10000) { 89 | writeUint8(type << 5 | 25); 90 | writeUint16(length); 91 | } else if (length < 0x100000000) { 92 | writeUint8(type << 5 | 26); 93 | writeUint32(length); 94 | } else { 95 | writeUint8(type << 5 | 27); 96 | writeUint64(length); 97 | } 98 | } 99 | 100 | function encodeItem(value) { 101 | var i; 102 | 103 | if (value === false) 104 | return writeUint8(0xf4); 105 | if (value === true) 106 | return writeUint8(0xf5); 107 | if (value === null) 108 | return writeUint8(0xf6); 109 | if (value === undefined) 110 | return writeUint8(0xf7); 111 | 112 | switch (typeof value) { 113 | case "number": 114 | if (Math.floor(value) === value) { 115 | if (0 <= value && value <= POW_2_53) 116 | return writeTypeAndLength(0, value); 117 | if (-POW_2_53 <= value && value < 0) 118 | return writeTypeAndLength(1, -(value + 1)); 119 | } 120 | writeUint8(0xfb); 121 | return writeFloat64(value); 122 | 123 | case "string": 124 | var utf8data = []; 125 | for (i = 0; i < value.length; ++i) { 126 | var charCode = value.charCodeAt(i); 127 | if (charCode < 0x80) { 128 | utf8data.push(charCode); 129 | } else if (charCode < 0x800) { 130 | utf8data.push(0xc0 | charCode >> 6); 131 | utf8data.push(0x80 | charCode & 0x3f); 132 | } else if (charCode < 0xd800) { 133 | utf8data.push(0xe0 | charCode >> 12); 134 | utf8data.push(0x80 | (charCode >> 6) & 0x3f); 135 | utf8data.push(0x80 | charCode & 0x3f); 136 | } else { 137 | charCode = (charCode & 0x3ff) << 10; 138 | charCode |= value.charCodeAt(++i) & 0x3ff; 139 | charCode += 0x10000; 140 | 141 | utf8data.push(0xf0 | charCode >> 18); 142 | utf8data.push(0x80 | (charCode >> 12) & 0x3f); 143 | utf8data.push(0x80 | (charCode >> 6) & 0x3f); 144 | utf8data.push(0x80 | charCode & 0x3f); 145 | } 146 | } 147 | 148 | writeTypeAndLength(3, utf8data.length); 149 | return writeUint8Array(utf8data); 150 | 151 | default: 152 | var length; 153 | if (Array.isArray(value)) { 154 | length = value.length; 155 | writeTypeAndLength(4, length); 156 | for (i = 0; i < length; ++i) 157 | encodeItem(value[i]); 158 | } else if (value instanceof Uint8Array) { 159 | writeTypeAndLength(2, value.length); 160 | writeUint8Array(value); 161 | } else { 162 | var keys = Object.keys(value); 163 | length = keys.length; 164 | writeTypeAndLength(5, length); 165 | for (i = 0; i < length; ++i) { 166 | var key = keys[i]; 167 | encodeItem(key); 168 | encodeItem(value[key]); 169 | } 170 | } 171 | } 172 | } 173 | 174 | encodeItem(value); 175 | 176 | if ("slice" in data) 177 | return data.slice(0, offset); 178 | 179 | var ret = new ArrayBuffer(offset); 180 | var retView = new DataView(ret); 181 | for (var i = 0; i < offset; ++i) 182 | retView.setUint8(i, dataView.getUint8(i)); 183 | return ret; 184 | } 185 | 186 | function decode(data, tagger, simpleValue) { 187 | var dataView = new DataView(data); 188 | var offset = 0; 189 | 190 | if (typeof tagger !== "function") 191 | tagger = function(value) { return value; }; 192 | if (typeof simpleValue !== "function") 193 | simpleValue = function() { return undefined; }; 194 | 195 | function read(value, length) { 196 | offset += length; 197 | return value; 198 | } 199 | function readArrayBuffer(length) { 200 | return read(new Uint8Array(data, offset, length), length); 201 | } 202 | function readFloat16() { 203 | var tempArrayBuffer = new ArrayBuffer(4); 204 | var tempDataView = new DataView(tempArrayBuffer); 205 | var value = readUint16(); 206 | 207 | var sign = value & 0x8000; 208 | var exponent = value & 0x7c00; 209 | var fraction = value & 0x03ff; 210 | 211 | if (exponent === 0x7c00) 212 | exponent = 0xff << 10; 213 | else if (exponent !== 0) 214 | exponent += (127 - 15) << 10; 215 | else if (fraction !== 0) 216 | return fraction * POW_2_24; 217 | 218 | tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13); 219 | return tempDataView.getFloat32(0); 220 | } 221 | function readFloat32() { 222 | return read(dataView.getFloat32(offset), 4); 223 | } 224 | function readFloat64() { 225 | return read(dataView.getFloat64(offset), 8); 226 | } 227 | function readUint8() { 228 | return read(dataView.getUint8(offset), 1); 229 | } 230 | function readUint16() { 231 | return read(dataView.getUint16(offset), 2); 232 | } 233 | function readUint32() { 234 | return read(dataView.getUint32(offset), 4); 235 | } 236 | function readUint64() { 237 | return readUint32() * POW_2_32 + readUint32(); 238 | } 239 | function readBreak() { 240 | if (dataView.getUint8(offset) !== 0xff) 241 | return false; 242 | offset += 1; 243 | return true; 244 | } 245 | function readLength(additionalInformation) { 246 | if (additionalInformation < 24) 247 | return additionalInformation; 248 | if (additionalInformation === 24) 249 | return readUint8(); 250 | if (additionalInformation === 25) 251 | return readUint16(); 252 | if (additionalInformation === 26) 253 | return readUint32(); 254 | if (additionalInformation === 27) 255 | return readUint64(); 256 | if (additionalInformation === 31) 257 | return -1; 258 | throw "Invalid length encoding"; 259 | } 260 | function readIndefiniteStringLength(majorType) { 261 | var initialByte = readUint8(); 262 | if (initialByte === 0xff) 263 | return -1; 264 | var length = readLength(initialByte & 0x1f); 265 | if (length < 0 || (initialByte >> 5) !== majorType) 266 | throw "Invalid indefinite length element"; 267 | return length; 268 | } 269 | 270 | function appendUtf16data(utf16data, length) { 271 | for (var i = 0; i < length; ++i) { 272 | var value = readUint8(); 273 | if (value & 0x80) { 274 | if (value < 0xe0) { 275 | value = (value & 0x1f) << 6 276 | | (readUint8() & 0x3f); 277 | length -= 1; 278 | } else if (value < 0xf0) { 279 | value = (value & 0x0f) << 12 280 | | (readUint8() & 0x3f) << 6 281 | | (readUint8() & 0x3f); 282 | length -= 2; 283 | } else { 284 | value = (value & 0x0f) << 18 285 | | (readUint8() & 0x3f) << 12 286 | | (readUint8() & 0x3f) << 6 287 | | (readUint8() & 0x3f); 288 | length -= 3; 289 | } 290 | } 291 | 292 | if (value < 0x10000) { 293 | utf16data.push(value); 294 | } else { 295 | value -= 0x10000; 296 | utf16data.push(0xd800 | (value >> 10)); 297 | utf16data.push(0xdc00 | (value & 0x3ff)); 298 | } 299 | } 300 | } 301 | 302 | function decodeItem() { 303 | var initialByte = readUint8(); 304 | var majorType = initialByte >> 5; 305 | var additionalInformation = initialByte & 0x1f; 306 | var i; 307 | var length; 308 | 309 | if (majorType === 7) { 310 | switch (additionalInformation) { 311 | case 25: 312 | return readFloat16(); 313 | case 26: 314 | return readFloat32(); 315 | case 27: 316 | return readFloat64(); 317 | } 318 | } 319 | 320 | length = readLength(additionalInformation); 321 | if (length < 0 && (majorType < 2 || 6 < majorType)) 322 | throw "Invalid length"; 323 | 324 | switch (majorType) { 325 | case 0: 326 | return length; 327 | case 1: 328 | return -1 - length; 329 | case 2: 330 | if (length < 0) { 331 | var elements = []; 332 | var fullArrayLength = 0; 333 | while ((length = readIndefiniteStringLength(majorType)) >= 0) { 334 | fullArrayLength += length; 335 | elements.push(readArrayBuffer(length)); 336 | } 337 | var fullArray = new Uint8Array(fullArrayLength); 338 | var fullArrayOffset = 0; 339 | for (i = 0; i < elements.length; ++i) { 340 | fullArray.set(elements[i], fullArrayOffset); 341 | fullArrayOffset += elements[i].length; 342 | } 343 | return fullArray; 344 | } 345 | return readArrayBuffer(length); 346 | case 3: 347 | var utf16data = []; 348 | if (length < 0) { 349 | while ((length = readIndefiniteStringLength(majorType)) >= 0) 350 | appendUtf16data(utf16data, length); 351 | } else 352 | appendUtf16data(utf16data, length); 353 | return String.fromCharCode.apply(null, utf16data); 354 | case 4: 355 | var retArray; 356 | if (length < 0) { 357 | retArray = []; 358 | while (!readBreak()) 359 | retArray.push(decodeItem()); 360 | } else { 361 | retArray = new Array(length); 362 | for (i = 0; i < length; ++i) 363 | retArray[i] = decodeItem(); 364 | } 365 | return retArray; 366 | case 5: 367 | var retObject = {}; 368 | for (i = 0; i < length || length < 0 && !readBreak(); ++i) { 369 | var key = decodeItem(); 370 | retObject[key] = decodeItem(); 371 | } 372 | return retObject; 373 | case 6: 374 | return tagger(decodeItem(), length); 375 | case 7: 376 | switch (length) { 377 | case 20: 378 | return false; 379 | case 21: 380 | return true; 381 | case 22: 382 | return null; 383 | case 23: 384 | return undefined; 385 | default: 386 | return simpleValue(length); 387 | } 388 | } 389 | } 390 | 391 | var ret = decodeItem(); 392 | if (offset !== data.byteLength) 393 | throw "Remaining bytes"; 394 | return ret; 395 | } 396 | 397 | var obj = { encode: encode, decode: decode }; 398 | 399 | if (typeof define === "function" && define.amd) 400 | define("cbor/cbor", obj); 401 | else if (!global.CBOR) 402 | global.cbor = obj; 403 | 404 | })(Copper); 405 | -------------------------------------------------------------------------------- /content/coap/CoapMessage.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | *******************************************************************************/ 31 | /** 32 | * \file 33 | * A wrapper for the different CoAP versions 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | //create a request/ack, received responses use parse() 39 | Copper.CoapMessage = function(type, code, uri, pl) { 40 | 41 | this.options = new Object(); 42 | this.options[Copper.OPTION_CONTENT_FORMAT] = new Array(0, null); 43 | this.options[Copper.OPTION_MAX_AGE] = new Array(0, null); 44 | this.options[Copper.OPTION_PROXY_URI] = new Array(0, null); 45 | this.options[Copper.OPTION_PROXY_SCHEME] = new Array(0, null); 46 | this.options[Copper.OPTION_ETAG] = new Array(0, null); 47 | this.options[Copper.OPTION_URI_HOST] = new Array(0, null); 48 | this.options[Copper.OPTION_LOCATION_PATH] = new Array(0, null); 49 | this.options[Copper.OPTION_URI_PORT] = new Array(0, null); 50 | this.options[Copper.OPTION_LOCATION_QUERY] = new Array(0, null); 51 | this.options[Copper.OPTION_URI_PATH] = new Array(0, null); 52 | this.options[Copper.OPTION_OBSERVE] = new Array(0, null); 53 | this.options[Copper.OPTION_ACCEPT] = new Array(0, null); 54 | this.options[Copper.OPTION_IF_MATCH] = new Array(0, null); 55 | this.options[Copper.OPTION_URI_QUERY] = new Array(0, null); 56 | this.options[Copper.OPTION_BLOCK2] = new Array(0, null); 57 | this.options[Copper.OPTION_BLOCK1] = new Array(0, null); 58 | this.options[Copper.OPTION_SIZE2] = new Array(0, null); 59 | this.options[Copper.OPTION_SIZE1] = new Array(0, null); 60 | this.options[Copper.OPTION_IF_NONE_MATCH] = new Array(0, null); 61 | 62 | this.type = (type!==undefined) ? type : Copper.MSG_TYPE_CON; 63 | this.code = (code!==undefined) ? code : Copper.EMPTY; 64 | 65 | this.mid = -1; 66 | this.retries = 0; 67 | this.token = new Array(0); 68 | 69 | if (uri!==undefined) this.setUri( uri ); 70 | if (pl!==undefined) { 71 | this.setPayload( pl ); 72 | } else { 73 | this.payload = new Array(0); 74 | } 75 | 76 | return this; 77 | }; 78 | 79 | Copper.CoapMessage.prototype = { 80 | 81 | version : Copper.VERSION, 82 | 83 | // message summary (e.g., for info/debug dumps) 84 | getSummary : function() { 85 | let ret = ''; 86 | ret += ' Type: '+this.getType(true); 87 | ret += '\n Code: '+this.getCode(true); 88 | ret += '\n Message ID: '+this.getMID(); 89 | ret += '\n Token: '+this.getToken(); 90 | if (Object.keys(this.options).length > 0) { 91 | ret += '\n Options: '+this.getOptions(true); 92 | } 93 | if (this.getPayload().length > 0) { 94 | ret += '\n Payload: '+this.getPayload().length+' bytes'; 95 | if (this.isPrintable(this.getContentFormat())) { 96 | ret += '\n'+Copper.bytes2str(this.getPayload()); 97 | } 98 | } 99 | return ret; 100 | }, 101 | 102 | // readable type 103 | getType : function(readable) { 104 | if (readable) { 105 | switch (parseInt(this.type)) { 106 | case Copper.MSG_TYPE_CON: return 'CON'; 107 | case Copper.MSG_TYPE_NON: return 'NON'; 108 | case Copper.MSG_TYPE_ACK: return 'ACK'; 109 | case Copper.MSG_TYPE_RST: return 'RST'; 110 | default: return 'unknown ('+this.type+')'; 111 | } 112 | } else { 113 | return parseInt(this.type); 114 | } 115 | }, 116 | setType : function(type) { 117 | this.type = type; 118 | }, 119 | 120 | isConfirmable : function() { 121 | return this.type==Copper.MSG_TYPE_CON; 122 | }, 123 | 124 | getOptionCount : function() { 125 | return this.optionCount; 126 | }, 127 | 128 | getCode : function(readable) { 129 | if (readable) { 130 | return Copper.getCodeName(this.code); 131 | } else { 132 | return parseInt(this.code); 133 | } 134 | }, 135 | setCode : function(code) { 136 | this.code = code; 137 | }, 138 | 139 | isRequest: function() { 140 | return this.getCode()>=1 && this.getCode()<=31; 141 | }, 142 | isResponse: function() { 143 | return this.getCode()>=64; 144 | }, 145 | 146 | isSuccess: function() { 147 | return Math.floor(this.getCode() / 32) == 2; 148 | }, 149 | isClientError: function() { 150 | return Math.floor(this.getCode() / 32) == 4; 151 | }, 152 | isServerError: function() { 153 | return Math.floor(this.getCode() / 32) == 5; 154 | }, 155 | 156 | getMID : function() { 157 | return this.mid; 158 | }, 159 | setMID : function(id) { 160 | this.mid = id; 161 | }, 162 | 163 | getToken : function() { 164 | // return token as string for easy use as object key 165 | return Copper.bytes2hex(this.token); 166 | }, 167 | setToken : function(token) { 168 | if (!token) { 169 | token = new Array(0); 170 | } else if (token=='empty') { 171 | token = new Array(0); 172 | } else if (!Array.isArray(token)) { 173 | if (token.substr(0,2)=='0x') { 174 | token = Copper.hex2bytes(token); 175 | } else { 176 | token = Copper.str2bytes(token); 177 | } 178 | } 179 | 180 | while (token.length > Copper.TOKEN_LENGTH) { 181 | token.pop(); 182 | Copper.logEvent('WARNING: Token must be 1-'+Copper.TOKEN_LENGTH+' bytes; cutting to '+Copper.TOKEN_LENGTH+' bytes]'); 183 | } 184 | 185 | delete this.token; 186 | this.token = token; 187 | }, 188 | 189 | // readable options list 190 | getOptions : function(asString) { 191 | 192 | if (asString) { 193 | let ret = ''; 194 | 195 | for (let o in this.options) { 196 | 197 | if (Array.isArray(this.options[o][1])) { 198 | 199 | if (ret!='') ret += ', '; 200 | 201 | let name = Copper.getOptionName(o); 202 | let val = this.getOption(o); 203 | let info = this.options[o][0]; 204 | 205 | switch (parseInt(o)) { 206 | case Copper.OPTION_BLOCK2: 207 | val = this.getBlock2Number(); 208 | val += '/'+this.getBlock2More(); 209 | val += '/'+this.getBlock2Size(); 210 | break; 211 | case Copper.OPTION_BLOCK1: 212 | val = this.getBlock1Number(); 213 | val += '/'+this.getBlock1More(); 214 | val += '/'+this.getBlock1Size(); 215 | break; 216 | } 217 | 218 | ret += name + ': ' + val; 219 | } 220 | } 221 | 222 | return ret; 223 | } else { 224 | let ret = new Array(); 225 | 226 | for (let o in this.options) { 227 | if (Array.isArray(this.options[o][1])) { 228 | var name = Copper.getOptionName(o); 229 | var value = this.getOption(o); 230 | var info = this.options[o][0]+' byte' + (this.options[o][0]!=1 ? 's' : ''); 231 | 232 | switch (parseInt(o)) { 233 | case Copper.OPTION_URI_PATH: 234 | case Copper.OPTION_LOCATION_PATH: 235 | value = '/' + value; 236 | break; 237 | case Copper.OPTION_CONTENT_FORMAT: 238 | info = value; 239 | value = Copper.getContentFormatName(value); 240 | break; 241 | case Copper.OPTION_ACCEPT: 242 | info = value; 243 | value = Copper.getContentFormatName(value); 244 | break; 245 | case Copper.OPTION_IF_NONE_MATCH: 246 | info = ''; 247 | value = 'Set'; 248 | break; 249 | case Copper.OPTION_BLOCK2: 250 | value = this.getBlock2Number(); 251 | if (this.getBlock2More()) value += '+'; 252 | value += ' ('+this.getBlock2Size()+' B/block)'; 253 | break; 254 | case Copper.OPTION_BLOCK1: 255 | value = this.getBlock1Number(); 256 | if (this.getBlock1More()) value += '+'; 257 | value += ' ('+this.getBlock1Size()+' B/block)'; 258 | break; 259 | } 260 | 261 | ret.push(new Array(name, value, info)); 262 | } 263 | } 264 | 265 | return ret; 266 | } 267 | }, 268 | // check if option is present 269 | isOption : function(optNumber) { 270 | if (this.options[optNumber]!==undefined && Array.isArray(this.options[optNumber][1])) { 271 | return true; 272 | } else { 273 | return false; 274 | } 275 | }, 276 | // retrieve option 277 | getOptionLength : function(optNumber) { 278 | if (this.options[optNumber]!=null && this.options[optNumber][0]!=null) { 279 | return this.options[optNumber][0]; 280 | } else { 281 | return -1; 282 | } 283 | }, 284 | getOption : function(optNumber) { 285 | var opt = this.options[optNumber][1]; 286 | 287 | // only set options are arrays 288 | if (!Array.isArray(opt)) { 289 | return null; 290 | } 291 | 292 | switch (parseInt(optNumber)) { 293 | // strings 294 | case Copper.OPTION_URI_HOST: 295 | case Copper.OPTION_URI_PATH: 296 | case Copper.OPTION_URI_QUERY: 297 | case Copper.OPTION_LOCATION_PATH: 298 | case Copper.OPTION_LOCATION_QUERY: 299 | case Copper.OPTION_PROXY_URI: 300 | case Copper.OPTION_PROXY_SCHEME: 301 | return Copper.bytes2str(opt); 302 | break; 303 | 304 | // integers 305 | case Copper.OPTION_URI_PORT: 306 | case Copper.OPTION_CONTENT_FORMAT: 307 | case Copper.OPTION_MAX_AGE: 308 | case Copper.OPTION_ACCEPT: 309 | case Copper.OPTION_IF_NONE_MATCH: 310 | case Copper.OPTION_OBSERVE: 311 | case Copper.OPTION_BLOCK2: 312 | case Copper.OPTION_BLOCK1: 313 | case Copper.OPTION_SIZE2: 314 | case Copper.OPTION_SIZE1: 315 | return Copper.bytes2int(opt); 316 | 317 | // byte arrays 318 | case Copper.OPTION_ETAG: 319 | case Copper.OPTION_IF_MATCH: 320 | return Copper.bytes2hex(opt); 321 | default: 322 | return Copper.bytes2hex(opt); 323 | } 324 | return null; 325 | }, 326 | 327 | setOption : function(option, value) { 328 | switch (parseInt(option)) { 329 | // strings 330 | case Copper.OPTION_PROXY_URI: 331 | case Copper.OPTION_PROXY_SCHEME: 332 | case Copper.OPTION_LOCATION_PATH: 333 | case Copper.OPTION_LOCATION_QUERY: 334 | case Copper.OPTION_URI_HOST: 335 | case Copper.OPTION_URI_PATH: 336 | case Copper.OPTION_URI_QUERY: 337 | this.options[option][0] = value.length; 338 | this.options[option][1] = Copper.str2bytes(value); 339 | break; 340 | 341 | // byte arrays 342 | case Copper.OPTION_ETAG: 343 | case Copper.OPTION_IF_MATCH: 344 | this.options[option][0] = value.length; 345 | this.options[option][1] = value; 346 | break; 347 | 348 | // special arrays 349 | case -1: 350 | this.options[option][0] += 1; 351 | if (this.options[option][1]==null) this.options[option][1] = new Array(0); 352 | this.options[option][1][ this.options[option][0] ] = value; 353 | this.options[option][0] += 1; 354 | break; 355 | 356 | // integers 357 | case Copper.OPTION_CONTENT_FORMAT: 358 | case Copper.OPTION_MAX_AGE: 359 | case Copper.OPTION_URI_PORT: 360 | case Copper.OPTION_OBSERVE: 361 | case Copper.OPTION_ACCEPT: 362 | case Copper.OPTION_BLOCK2: 363 | case Copper.OPTION_BLOCK1: 364 | case Copper.OPTION_SIZE2: 365 | case Copper.OPTION_SIZE1: 366 | case Copper.OPTION_IF_NONE_MATCH: 367 | this.options[option][1] = Copper.int2bytes(value); 368 | this.options[option][0] = this.options[option][1].length; 369 | break; 370 | 371 | default: 372 | this.options[option] = new Array(value.length, value); 373 | Copper.logEvent("WARNING: Setting custom option '"+option+"': "+value); 374 | } 375 | }, 376 | 377 | 378 | getContentFormat : function(readable) { 379 | var opt = this.getOption(Copper.OPTION_CONTENT_FORMAT); // integer 380 | 381 | if (opt==null) return null; 382 | 383 | if (readable) { 384 | return new Array(Copper.getContentFormatName(opt), opt); 385 | } else { 386 | return opt; 387 | } 388 | }, 389 | setContentType : function(content) { 390 | if (content>0xFFFF) { 391 | Copper.logWarning('Ignoring Content-Format larger than 65535.'); 392 | } else { 393 | this.setOption(Copper.OPTION_CONTENT_FORMAT, content); 394 | } 395 | }, 396 | 397 | // Copper.OPTION_MAX_AGE:00+ 398 | getMaxAge : function(readable) { 399 | var optLen = this.getOptionLength(Copper.OPTION_MAX_AGE); 400 | var opt = this.getOption(Copper.OPTION_MAX_AGE); // integer 401 | 402 | if (opt==null) return null; 403 | 404 | if (readable) { 405 | 406 | var ret = ''; 407 | var time = opt; 408 | 409 | if (time==0) { 410 | ret += '0 '; 411 | } else { 412 | // split into weeks, days, hours, minutes, and seconds 413 | var s = time % 60; 414 | time = Math.floor(time/60); 415 | var m = time % 60; 416 | time = Math.floor(time/60); 417 | var h = time % 24; 418 | time = Math.floor(time/24); 419 | var d = time % 7; 420 | time = Math.floor(time/7); 421 | var w = time; 422 | 423 | var y = 0; 424 | if (w>104) var y = Math.round(1212424351/60.0/60.0/24.0/365.25*100.0)/100.0; 425 | 426 | // only print from largest to smallest given unit 427 | if (w) ret += w+'w '; 428 | if (d||(w&&(h||m||s))) ret += d+'d '; 429 | if (h||((w||d)&&(m||s))) ret += h+'h '; 430 | if (m||((w||d||h)&&s)) ret += m+'m '; 431 | if (s) ret += s+'s '; 432 | if (y) ret += '(~'+y+'y) '; 433 | } 434 | 435 | return new Array(ret.substring(0, ret.length-1), optLen+' byte(s)'); 436 | } else { 437 | return opt; 438 | } 439 | }, 440 | setMaxAge : function(age) { 441 | if (age>0xFFFFFFFF) { 442 | age = (0xFFFFFFFF & age); 443 | Copper.logWarning('Ignoring Max-Age larger than 2³²'); 444 | } 445 | this.setOption(Copper.OPTION_MAX_AGE, age); 446 | }, 447 | 448 | // Copper.OPTION_PROXY_URI:04+ 449 | getProxyUri : function(readable) { 450 | return this.getOption(Copper.OPTION_PROXY_URI); // string 451 | }, 452 | setProxyUri : function(proxy) { 453 | this.setOption(Copper.OPTION_PROXY_URI, proxy); 454 | }, 455 | 456 | // Copper.OPTION_PROXY_URI:04+ 457 | getProxyScheme : function(readable) { 458 | return this.getOption(Copper.OPTION_PROXY_SCHEME); // string 459 | }, 460 | setProxyScheme : function(scheme) { 461 | this.setOption(Copper.OPTION_PROXY_SCHEME, scheme); 462 | }, 463 | 464 | // Copper.OPTION_ETAG:00+ 465 | getETag : function() { 466 | return this.getOption(Copper.OPTION_ETAG); // byte array 467 | }, 468 | setETag : function(tag) { 469 | 470 | if (!Array.isArray(tag)) { 471 | Copper.logEvent('INFO: Converting ETag to byte array'); 472 | if (tag.substr(0,2)=='0x') { 473 | tag = Copper.hex2bytes(tag); 474 | } else { 475 | tag = Copper.str2bytes(tag); 476 | } 477 | } 478 | 479 | if (tag.length>Copper.ETAG_LENGTH) { 480 | Copper.logWarning('Reducing ETag from '+tag.length+' to '+Copper.ETAG_LENGTH+' bytes.'); 481 | tag = tag.slice(0, Copper.ETAG_LENGTH-1); 482 | } 483 | 484 | this.setOption(Copper.OPTION_ETAG, tag); 485 | }, 486 | 487 | // Copper.OPTION_URI_HOST:04+ / Copper.OPTION_URI_AUTH:03*renamed 488 | getUriHost : function() { 489 | return this.getOption(Copper.OPTION_URI_HOST); // string 490 | }, 491 | setUriHost : function(host) { 492 | this.setOption(Copper.OPTION_URI_HOST, host); 493 | }, 494 | // Copper.OPTION_URI_PORT:04+ 495 | getUriPort : function() { 496 | return this.getOption(Copper.OPTION_URI_PORT); // int 497 | }, 498 | setUriPort : function(port) { 499 | this.setOption(Copper.OPTION_URI_PORT, port); 500 | }, 501 | // multiple Copper.OPTION_URI_PATH:04+ / Copper.OPTION_URI_PATH:03+ 502 | getUriPath : function() { 503 | // multiple Copper.OPTION_URI_PATH options should be concatinated during datagram parsing 504 | // TODO: maybe use a string array later 505 | 506 | return this.getOption(Copper.OPTION_URI_PATH); // string 507 | }, 508 | // Copper.OPTION_URI_QUERY:03+ 509 | getUriQuery : function() { 510 | return this.getOption(Copper.OPTION_URI_QUERY); // string 511 | }, 512 | // convenience function 513 | getUri : function(readable) { 514 | 515 | let host = this.getUriHost(); 516 | let port = this.getUriPort(); 517 | let path = this.getUriPath(); 518 | let query = this.getUriQuery(); 519 | 520 | let uri = ''; 521 | let decoded = 0; 522 | let multiple = null; 523 | 524 | if (host) { 525 | uri += 'coap://' + host; 526 | ++decoded; 527 | } 528 | if (port) { 529 | uri += ':' + port; 530 | ++decoded; 531 | } 532 | if (path) { 533 | uri += '/' + path; 534 | multiple = path.match(/\//g); 535 | decoded += 1 + (multiple!=null ? multiple.length : 0); 536 | } 537 | if (query) { 538 | uri += '?' + query; 539 | multiple = query.match(/&/g); 540 | decoded += 1 + (multiple!=null ? multiple.length : 0); 541 | } 542 | 543 | if (decoded<=0) return null; 544 | 545 | if (readable) { 546 | return new Array('Uri', uri, decoded+(decoded==1 ? ' option' : ' options')); 547 | } else { 548 | return uri; 549 | } 550 | }, 551 | setUri : function(inputUri) { 552 | 553 | var uri = document.createElementNS("http://www.w3.org/1999/xhtml","a"); 554 | /* 555 | * tag as parser: 556 | * 557 | * parser.protocol; // => "http:" 558 | * parser.hostname; // => "example.com" 559 | * parser.port; // => "3000" 560 | * parser.pathname; // => "/pathname/" 561 | * parser.search; // => "?search=test" 562 | * parser.hash; // => "#hash" 563 | * parser.host; // => "example.com:3000" 564 | */ 565 | uri.href = inputUri; 566 | 567 | if (uri.hostname!='' // set 568 | && Copper.behavior.sendUriHost // enabled 569 | && !uri.hostname.match(/^[0-9]{1,3}(\.[0-9]{1,3}){3}$/) // no IPv4 literal 570 | && !uri.hostname.match(/^[0-9a-f]{0,4}(:?:[0-9a-f]{1,4})+$/i)) { // no IPv6 literal 571 | this.setOption(Copper.OPTION_URI_HOST, uri.hostname); 572 | } 573 | if (uri.pathname.length>1) { 574 | this.setOption(Copper.OPTION_URI_PATH, decodeURI(uri.pathname.substr(1))); 575 | } 576 | if (uri.search.length>1) { 577 | this.setOption(Copper.OPTION_URI_QUERY, uri.search.substr(1)); 578 | } 579 | }, 580 | 581 | // multiple Copper.OPTION_LOCATION_PATH:04+ / Copper.OPTION_LOCATION:03*renamed 582 | getLocationPath : function() { 583 | // multiple Copper.OPTION_LOCATION_PATH options should be concatinated during datagram parsing 584 | // TODO: maybe use a string array later 585 | 586 | return this.getOption(Copper.OPTION_LOCATION_PATH); // string 587 | }, 588 | setLocationPath : function(path) { 589 | while (path.charAt(0)=='/') path = path.substr(1); 590 | 591 | this.setOption(Copper.OPTION_LOCATION_PATH, path); 592 | }, 593 | // Copper.OPTION_LOCATION_QUERY:05+ 594 | getLocationQuery : function() { 595 | return this.getOption(Copper.OPTION_LOCATION_QUERY); // string 596 | }, 597 | setLocationQuery : function(query) { 598 | while (query.charAt(0)=='?') query = query.substr(1); 599 | 600 | this.setOption(Copper.OPTION_LOCATION_QUERY, query); 601 | }, 602 | // convenience function 603 | getLocation : function(readable) { 604 | var optLen = this.getOptionLength(Copper.OPTION_LOCATION_PATH); 605 | var opt = this.getOption(Copper.OPTION_LOCATION_PATH); // string 606 | 607 | var optLen2 = 0; 608 | 609 | if (this.getOptionLength(Copper.OPTION_LOCATION_QUERY)) { 610 | opt += '?' + this.getOption(Copper.OPTION_LOCATION_QUERY); 611 | optLen2 = this.getOptionLength(Copper.OPTION_LOCATION_QUERY); 612 | } 613 | 614 | if (optLen+optLen2<=0) return null; 615 | 616 | if (readable) { 617 | var multiple = opt.match(/\/|&/g); 618 | var decoded = 1 + (multiple!=null ? multiple.length : 0) + (optLen2>0 ? 1 : 0); 619 | if (opt.charAt(0)!='/') opt = '/' + opt; 620 | return new Array(opt, decoded+(decoded==1 ? ' option' : ' options')); 621 | } else { 622 | if (opt) opt = '/'+opt; 623 | return opt; 624 | } 625 | }, 626 | 627 | // Copper.OPTION_ACCEPT:07+ 628 | getAccept : function() { 629 | return this.getOption(Copper.OPTION_ACCEPT); // integer 630 | }, 631 | setAccept : function(content) { 632 | if (content>0xFFFF) { 633 | Copper.logWarning('Ignoring Accept Content-Format larger than 65535.'); 634 | } else { 635 | this.setOption(Copper.OPTION_ACCEPT, content); 636 | } 637 | }, 638 | 639 | // Copper.OPTION_IF_MATCH:07+ 640 | getIfMatch : function() { 641 | return this.getOption(Copper.OPTION_IF_MATCH); // byte array 642 | }, 643 | setIfMatch : function(tag) { 644 | if (!Array.isArray(tag)) { 645 | Copper.logEvent('INFO: Converting ETag to byte array'); 646 | if (tag.substr(0,2)=='0x') { 647 | tag = Copper.hex2bytes(tag); 648 | } else { 649 | tag = Copper.str2bytes(tag); 650 | } 651 | } 652 | 653 | while (tag.length>Copper.ETAG_LENGTH) { 654 | Copper.logWarning('Reducing ETag from '+tag.length+' to '+Copper.ETAG_LENGTH+' bytes.'); 655 | tag = tag.slice(0, Copper.ETAG_LENGTH-1); 656 | } 657 | this.setOption(Copper.OPTION_IF_MATCH, tag); 658 | }, 659 | 660 | // Copper.OPTION_BLOCK2:06+ / Copper.OPTION_BLOCK:03+ 661 | getBlock2 : function(readable) { 662 | var opt = this.getOption(Copper.OPTION_BLOCK2); // integer 663 | 664 | if (opt==null) return null; 665 | 666 | if (readable) { 667 | var ret = this.getBlock2Number(); 668 | if (this.getBlock2More()) ret += '+'; 669 | ret += ' ('+this.getBlock2Size()+' B/block)'; 670 | 671 | return ret; 672 | } else { 673 | return opt; 674 | } 675 | }, 676 | setBlock2 : function(num, size, more) { 677 | 678 | if (size!==undefined) { 679 | 680 | let block = num << 4; 681 | 682 | let szx = 0; 683 | 684 | // check for power of two and correct size 685 | if (!Copper.isPowerOfTwo(size)) { 686 | Copper.logEvent('INFO: Block2 size '+size+' not a power of two; using next smaller power.'); 687 | } 688 | if (size<16) { 689 | size = 16; 690 | Copper.logEvent('INFO: Block2 size must be >=16; using 16.'); 691 | } 692 | if (size>1024) { 693 | size = 1024; 694 | Copper.logEvent('INFO: Block2 size must be <=1024; using 1024.'); 695 | } 696 | 697 | // size encoding 698 | size >>= 4; 699 | for (szx = 0; size; ++szx) size >>= 1; 700 | block |= (szx - 1); 701 | 702 | if (more!==undefined) { 703 | block |= more ? 0x08 : 0x00; 704 | } 705 | 706 | this.setOption(Copper.OPTION_BLOCK2, block); 707 | 708 | } else { 709 | this.setOption(Copper.OPTION_BLOCK2, num); 710 | } 711 | }, 712 | // convenience functions for block option parts 713 | getBlock2Number : function() { 714 | return (this.getBlock2() >> 4); 715 | }, 716 | getBlock2Size : function() { 717 | return (16 << (0x07 & this.getBlock2())); 718 | }, 719 | getBlock2More : function() { 720 | return (parseInt(0x08 & this.getBlock2())!=0) ? 1 : 0; 721 | }, 722 | getBlock2Offset : function() { 723 | return this.getBlock2Size() * (this.getBlock2Number() + 1); 724 | }, 725 | 726 | // Copper.OPTION_BLOCK1:06+ 727 | getBlock1 : function(readable) { 728 | var opt = this.getOption(Copper.OPTION_BLOCK1); // integer 729 | 730 | if (opt==null) return null; 731 | 732 | if (readable) { 733 | var ret = this.getBlock1Number(); 734 | if (this.getBlock1More()) ret += '+'; 735 | ret += ' ('+this.getBlock1Size()+' B/block)'; 736 | 737 | return ret; 738 | } else { 739 | return opt; 740 | } 741 | }, 742 | setBlock1 : function(num, size, more) { 743 | let block = num << 4; 744 | let szx = 0; 745 | 746 | // check for power of two and correct size 747 | if (!Copper.isPowerOfTwo(size)) { 748 | Copper.logEvent('INFO: Block1 size '+size+' not a power of two; using next smaller power.'); 749 | } 750 | if (size<16) { 751 | size = 16; 752 | Copper.logEvent('INFO: Block1 size must be >=16; using 16.'); 753 | } 754 | if (size>1024) { 755 | size = 1024; 756 | Copper.logEvent('INFO: Block1 size must be <=1024; using 1024.'); 757 | } 758 | 759 | size >>= 4; 760 | for (szx = 0; size; ++szx) size >>= 1; 761 | block |= szx - 1; 762 | if (more) { 763 | block |= 0x08; 764 | } 765 | 766 | this.setOption(Copper.OPTION_BLOCK1, block); 767 | }, 768 | // convenience functions for block option parts 769 | getBlock1Number : function() { 770 | return (this.getBlock1() >> 4); 771 | }, 772 | getBlock1Size : function() { 773 | return (16 << (0x07 & this.getBlock1())); 774 | }, 775 | getBlock1More : function() { 776 | return (0x08 & this.getBlock1()) ? 1 : 0; 777 | }, 778 | getBlock1Offset : function() { 779 | return this.getBlock1Size() * (this.getBlock1Number() + 1); 780 | }, 781 | 782 | // Copper.OPTION_SIZE2:18+ / Copper.OPTION_SIZE:09+ 783 | getSize : function() { 784 | return this.getOption(Copper.OPTION_SIZE2); // integer 785 | }, 786 | setSize2 : function(num) { 787 | if (num>0xFFFFFFFF) { 788 | Copper.logWarning('Ignoring Size2 larger than 2³²'); 789 | } else { 790 | this.setOption(Copper.OPTION_SIZE2, num); 791 | } 792 | }, 793 | // Copper.OPTION_SIZE1:18+ 794 | getSize1 : function() { 795 | return this.getOption(Copper.OPTION_SIZE1); // integer 796 | }, 797 | setSize1 : function(num) { 798 | if (num>0xFFFFFFFF) { 799 | Copper.logWarning('Ignoring Size1 larger than 2³²'); 800 | } else { 801 | this.setOption(Copper.OPTION_SIZE1, num); 802 | } 803 | }, 804 | 805 | // Copper.OPTION_IF_NONE_MATCH:07+ 806 | getIfNoneMatch : function() { 807 | var opt = this.getOption(Copper.OPTION_IF_NONE_MATCH); // byte array 808 | 809 | return (opt==null ? null : 'none'); 810 | }, 811 | setIfNoneMatch : function() { 812 | // only set option with length 0 (int=0) 813 | this.setOption(Copper.OPTION_IF_NONE_MATCH, 0); 814 | }, 815 | 816 | // Copper.OPTION_SUB_LIFETIME:draft-ietf-core-observe-00*renamed 817 | getObserve : function() { 818 | return this.getOption(Copper.OPTION_OBSERVE); // int 819 | }, 820 | setObserve : function(num) { 821 | if (num> 0xFFFFFF) num = num % 0xFFFFFF; 822 | this.setOption(Copper.OPTION_OBSERVE, num); 823 | }, 824 | 825 | setCustom : function(num, value) { 826 | if (Copper.getOptionName(num).match(/^Unknown/)) { 827 | if (value.substr(0,2)=='0x') { 828 | this.setOption(parseInt(num), Copper.hex2bytes(value)); 829 | } else { 830 | this.setOption(parseInt(num), Copper.str2bytes(value)); 831 | } 832 | } else { 833 | throw new Error('Cannot set '+Copper.getOptionName(num)+' as custom option'); 834 | } 835 | }, 836 | 837 | // payload functions 838 | getPayload : function() { 839 | return this.payload; 840 | }, 841 | getPayloadText : function() { 842 | return Copper.bytes2str(this.payload); 843 | }, 844 | setPayload : function(pl) { 845 | if (!Array.isArray(pl)) pl = Copper.str2bytes(pl); 846 | this.payload = pl; 847 | }, 848 | appendPayload : function(pl) { 849 | this.payload = this.payload.concat(pl); 850 | }, 851 | isPrintable : function(ct) { 852 | if (ct==null) ct = this.getContentFormat(); 853 | 854 | switch (ct) { 855 | case Copper.CONTENT_TYPE_TEXT_PLAIN: 856 | case Copper.CONTENT_TYPE_TEXT_XML: 857 | case Copper.CONTENT_TYPE_TEXT_CSV: 858 | case Copper.CONTENT_TYPE_TEXT_HTML: 859 | case Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT: 860 | case Copper.CONTENT_TYPE_APPLICATION_XML: 861 | case Copper.CONTENT_TYPE_APPLICATION_RDF_XML: 862 | case Copper.CONTENT_TYPE_APPLICATION_SOAP_XML: 863 | case Copper.CONTENT_TYPE_APPLICATION_ATOM_XML: 864 | case Copper.CONTENT_TYPE_APPLICATION_XMPP_XML: 865 | case Copper.CONTENT_TYPE_APPLICATION_JSON: 866 | case Copper.CONTENT_TYPE_APPLICATION_CBOR: 867 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TEXT: 868 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_JSON: 869 | case null: 870 | return true; 871 | 872 | case Copper.CONTENT_TYPE_IMAGE_GIF: 873 | case Copper.CONTENT_TYPE_IMAGE_JPEG: 874 | case Copper.CONTENT_TYPE_IMAGE_PNG: 875 | case Copper.CONTENT_TYPE_IMAGE_TIFF: 876 | case Copper.CONTENT_TYPE_AUDIO_RAW: 877 | case Copper.CONTENT_TYPE_VIDEO_RAW: 878 | case Copper.CONTENT_TYPE_APPLICATION_OCTET_STREAM: 879 | case Copper.CONTENT_TYPE_APPLICATION_EXI: 880 | case Copper.CONTENT_TYPE_APPLICATION_FASTINFOSET: 881 | case Copper.CONTENT_TYPE_APPLICATION_SOAP_FASTINFOSET: 882 | case Copper.CONTENT_TYPE_APPLICATION_X_OBIX_BINARY: 883 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TLV: 884 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_OPAQUE: 885 | default: 886 | return false; 887 | } 888 | }, 889 | 890 | 891 | // convert message into datagram bytes 892 | serialize : function() { 893 | return Copper.serialize(); 894 | }, 895 | 896 | // convert datagram bytes into message 897 | parse : function(datagram) { 898 | this.parse(datagram); 899 | }, 900 | 901 | getRetries : function() { 902 | return this.retries; 903 | }, 904 | 905 | incRetries : function() { 906 | ++this.retries; 907 | }, 908 | 909 | // maybe more arguments needed 910 | respond : function(code, payload, format) { 911 | this.reply = new CoapMessage(Copper.MSG_TYPE_ACK, code, null, payload); 912 | this.reply.setMID(this.getMID()); 913 | this.reply.setToken(this.getToken()); 914 | 915 | if (format) this.reply.setContentType(format); 916 | }, 917 | 918 | getReply : function() { 919 | return this.reply; 920 | } 921 | }; 922 | -------------------------------------------------------------------------------- /content/coap/CoapRFC7252.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | *******************************************************************************/ 31 | /** 32 | * \file Implementation of RFC 7252 33 | * 34 | * \author Matthias Kovatsch 35 | */ 36 | 37 | // Constants 38 | //////////////////////////////////////////////////////////////////////////////// 39 | 40 | Copper.__defineGetter__("VERSION", function() { return 1; }); 41 | Copper.__defineGetter__("DEFAULT_PORT", function() { return 5683; }); 42 | Copper.__defineGetter__("RESPONSE_TIMEOUT", function() { return 2000; }); // ms 43 | Copper.__defineGetter__("RESPONSE_RANDOM_FACTOR", function() { return 1.5; }); // ms 44 | Copper.__defineGetter__("MAX_RETRANSMIT", function() { return 4; }); 45 | Copper.__defineGetter__("ETAG_LENGTH", function() { return 8; }); 46 | Copper.__defineGetter__("TOKEN_LENGTH", function() { return 8; }); 47 | 48 | Copper.__defineGetter__("MSG_TYPE_CON", function() { return 0; }); 49 | Copper.__defineGetter__("MSG_TYPE_NON", function() { return 1; }); 50 | Copper.__defineGetter__("MSG_TYPE_ACK", function() { return 2; }); 51 | Copper.__defineGetter__("MSG_TYPE_RST", function() { return 3; }); 52 | 53 | Copper.__defineGetter__("OPTION_IF_MATCH", function() { return 1; }); 54 | Copper.__defineGetter__("OPTION_URI_HOST", function() { return 3; }); 55 | Copper.__defineGetter__("OPTION_ETAG", function() { return 4; }); 56 | Copper.__defineGetter__("OPTION_IF_NONE_MATCH", function() { return 5; }); 57 | Copper.__defineGetter__("OPTION_URI_PORT", function() { return 7; }); 58 | Copper.__defineGetter__("OPTION_LOCATION_PATH", function() { return 8; }); 59 | Copper.__defineGetter__("OPTION_URI_PATH", function() { return 11; }); 60 | Copper.__defineGetter__("OPTION_CONTENT_FORMAT", function() { return 12; }); 61 | Copper.__defineGetter__("OPTION_MAX_AGE", function() { return 14; }); 62 | Copper.__defineGetter__("OPTION_URI_QUERY", function() { return 15; }); 63 | Copper.__defineGetter__("OPTION_ACCEPT", function() { return 17; }); 64 | Copper.__defineGetter__("OPTION_LOCATION_QUERY", function() { return 20; }); 65 | Copper.__defineGetter__("OPTION_PROXY_URI", function() { return 35; }); 66 | Copper.__defineGetter__("OPTION_PROXY_SCHEME", function() { return 39; }); 67 | Copper.__defineGetter__("OPTION_SIZE1", function() { return 60; }); 68 | 69 | Copper.__defineGetter__("OPTION_OBSERVE", function() { return 6; }); 70 | 71 | Copper.__defineGetter__("OPTION_BLOCK2", function() { return 23; }); 72 | Copper.__defineGetter__("OPTION_BLOCK1", function() { return 27; }); 73 | Copper.__defineGetter__("OPTION_SIZE2", function() { return 28; }); 74 | 75 | Copper.__defineGetter__("EMPTY", function() { return 0; }); 76 | 77 | Copper.__defineGetter__("GET", function() { return 1; }); 78 | Copper.__defineGetter__("POST", function() { return 2; }); 79 | Copper.__defineGetter__("PUT", function() { return 3; }); 80 | Copper.__defineGetter__("DELETE", function() { return 4; }); 81 | Copper.__defineGetter__("FETCH", function() { return 5; }); 82 | Copper.__defineGetter__("PATCH", function() { return 6; }); 83 | Copper.__defineGetter__("IPATCH", function() { return 7; }); 84 | 85 | Copper.__defineGetter__("CODE_2_01_CREATED", function() { return 65; }); 86 | Copper.__defineGetter__("CODE_2_02_DELETED", function() { return 66; }); 87 | Copper.__defineGetter__("CODE_2_03_VALID", function() { return 67; }); 88 | Copper.__defineGetter__("CODE_2_04_CHANGED", function() { return 68; }); 89 | Copper.__defineGetter__("CODE_2_05_CONTENT", function() { return 69; }); 90 | Copper.__defineGetter__("CODE_2_31_CONTINUE", function() { return 95; }); 91 | 92 | Copper.__defineGetter__("CODE_4_00_BAD_REQUEST", function() { return 128; }); 93 | Copper.__defineGetter__("CODE_4_01_UNAUTHORIZED", function() { return 129; }); 94 | Copper.__defineGetter__("CODE_4_02_BAD_OPTION", function() { return 130; }); 95 | Copper.__defineGetter__("CODE_4_03_FORBIDDEN", function() { return 131; }); 96 | Copper.__defineGetter__("CODE_4_04_NOT_FOUND", function() { return 132; }); 97 | Copper.__defineGetter__("CODE_4_05_METHOD_NOT_ALLOWED", function() { return 133; }); 98 | Copper.__defineGetter__("CODE_4_06_NOT_ACCEPTABLE", function() { return 134; }); 99 | Copper.__defineGetter__("CODE_4_08_REQUEST_ENTITY_INCOMPLETE", function() { return 136; }); 100 | Copper.__defineGetter__("CODE_4_12_PRECONDITION_FAILED", function() { return 140; }); 101 | Copper.__defineGetter__("CODE_4_13_REQUEST_ENTITY_TOO_LARGE", function() { return 141; }); 102 | Copper.__defineGetter__("CODE_4_15_UNSUPPORTED_MEDIA_TYPE", function() { return 143; }); 103 | 104 | Copper.__defineGetter__("CODE_5_00_INTERNAL_SERVER_ERROR", function() { return 160; }); 105 | Copper.__defineGetter__("CODE_5_01_NOT_IMPLEMENTED", function() { return 161; }); 106 | Copper.__defineGetter__("CODE_5_02_BAD_GATEWAY", function() { return 162; }); 107 | Copper.__defineGetter__("CODE_5_03_SERVICE_UNAVAILABLE", function() { return 163; }); 108 | Copper.__defineGetter__("CODE_5_04_GATEWAY_TIMEOUT", function() { return 164; }); 109 | Copper.__defineGetter__("CODE_5_05_PROXYING_NOT_SUPPORTED", function() { return 165; }); 110 | 111 | Copper.__defineGetter__("CONTENT_TYPE_TEXT_PLAIN", function() { return 0; }); 112 | Copper.__defineGetter__("CONTENT_TYPE_TEXT_XML", function() { return 1; }); 113 | Copper.__defineGetter__("CONTENT_TYPE_TEXT_CSV", function() { return 2; }); 114 | Copper.__defineGetter__("CONTENT_TYPE_TEXT_HTML", function() { return 3; }); 115 | Copper.__defineGetter__("CONTENT_TYPE_IMAGE_GIF", function() { return 21; }); // 03 116 | Copper.__defineGetter__("CONTENT_TYPE_IMAGE_JPEG", function() { return 22; }); // 03 117 | Copper.__defineGetter__("CONTENT_TYPE_IMAGE_PNG", function() { return 23; }); // 03 118 | Copper.__defineGetter__("CONTENT_TYPE_IMAGE_TIFF", function() { return 24; }); // 03 119 | Copper.__defineGetter__("CONTENT_TYPE_AUDIO_RAW", function() { return 25; }); // 03 120 | Copper.__defineGetter__("CONTENT_TYPE_VIDEO_RAW", function() { return 26; }); // 03 121 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_LINK_FORMAT", function() { return 40; }); 122 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_XML", function() { return 41; }); 123 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_OCTET_STREAM", function() { return 42; }); 124 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_RDF_XML", function() { return 43; }); 125 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_SOAP_XML", function() { return 44; }); 126 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_ATOM_XML", function() { return 45; }); 127 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_XMPP_XML", function() { return 46; }); 128 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_EXI", function() { return 47; }); 129 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_FASTINFOSET", function() { return 48; }); // 04 130 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_SOAP_FASTINFOSET", function() { return 49; }); // 04 131 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_JSON", function() { return 50; }); // 04 132 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_X_OBIX_BINARY", function() { return 51; }); 133 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_CBOR", function() { return 60; }); // 04 134 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TEXT", function() { return 1541; }); 135 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TLV", function() { return 1542; }); 136 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_JSON", function() { return 1543; }); 137 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_OPAQUE", function() { return 1544; }); 138 | 139 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_THING_DESCRIPTION_JSON", function() { return 65200; }); 140 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_BULLETIN_BOARD_JSON", function() { return 65201; }); 141 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_LIGHTING_CONFIG_JSON", function() { return 65202; }); 142 | Copper.__defineGetter__("CONTENT_TYPE_APPLICATION_LIGHTING_JSON", function() { return 65203; }); 143 | 144 | Copper.__defineGetter__("WELL_KNOWN_RESOURCES", function() { return '/.well-known/core'; }); 145 | 146 | //Registries 147 | //////////////////////////////////////////////////////////////////////////////// 148 | 149 | Copper.getCodeName = function(code) { 150 | switch (parseInt(code)) { 151 | // empty 152 | case 0: return 'EMPTY'; 153 | // methods 154 | case Copper.GET: return 'GET'; 155 | case Copper.POST: return 'POST'; 156 | case Copper.PUT: return 'PUT'; 157 | case Copper.DELETE: return 'DELETE'; 158 | case Copper.FETCH: return 'FETCH'; 159 | case Copper.PATCH: return 'PATCH'; 160 | case Copper.IPATCH: return 'iPATCH'; 161 | // response codes 162 | case Copper.CODE_2_01_CREATED: return '2.01 Created'; 163 | case Copper.CODE_2_02_DELETED: return '2.02 Deleted'; 164 | case Copper.CODE_2_03_VALID: return '2.03 Valid'; 165 | case Copper.CODE_2_04_CHANGED: return '2.04 Changed'; 166 | case Copper.CODE_2_05_CONTENT: return '2.05 Content'; 167 | case Copper.CODE_2_31_CONTINUE: return '2.31 Continue'; 168 | case Copper.CODE_4_00_BAD_REQUEST: return '4.00 Bad Request'; 169 | case Copper.CODE_4_01_UNAUTHORIZED: return '4.01 Unauthorized'; 170 | case Copper.CODE_4_02_BAD_OPTION: return '4.02 Bad Option'; 171 | case Copper.CODE_4_03_FORBIDDEN: return '4.03 Forbidden'; 172 | case Copper.CODE_4_04_NOT_FOUND: return '4.04 Not Found'; 173 | case Copper.CODE_4_05_METHOD_NOT_ALLOWED: return '4.05 Method Not Allowed'; 174 | case Copper.CODE_4_06_NOT_ACCEPTABLE: return '4.06 Not Acceptable'; 175 | case Copper.CODE_4_08_REQUEST_ENTITY_INCOMPLETE: return '4.08 Request Entity Incomplete'; 176 | case Copper.CODE_4_12_PRECONDITION_FAILED: return '4.12 Precondition Failed'; 177 | case Copper.CODE_4_13_REQUEST_ENTITY_TOO_LARGE: return '4.13 Request Entity Too Large'; 178 | case Copper.CODE_4_15_UNSUPPORTED_MEDIA_TYPE: return '4.15 Unsupported Content-Format'; 179 | case Copper.CODE_5_00_INTERNAL_SERVER_ERROR: return '5.00 Internal Server Error'; 180 | case Copper.CODE_5_01_NOT_IMPLEMENTED: return '5.01 Not Implemented'; 181 | case Copper.CODE_5_02_BAD_GATEWAY: return '5.02 Bad Gateway'; 182 | case Copper.CODE_5_03_SERVICE_UNAVAILABLE: return '5.03 Service Unavailable'; 183 | case Copper.CODE_5_04_GATEWAY_TIMEOUT: return '5.04 Gateway Timeout'; 184 | case Copper.CODE_5_05_PROXYING_NOT_SUPPORTED: return '5.05 Proxying Not Supported'; 185 | // ... 186 | default: return Math.floor(code/32)+'.'+(code % 32)+' Unknown by Copper'; 187 | } 188 | }; 189 | 190 | Copper.getOptionName = function(number) { 191 | switch (parseInt(number)) { 192 | case Copper.OPTION_CONTENT_FORMAT: return 'Content-Format'; 193 | case Copper.OPTION_MAX_AGE: return 'Max-Age'; 194 | case Copper.OPTION_ACCEPT: return 'Accept'; 195 | 196 | case Copper.OPTION_URI_HOST: return 'Uri-Host'; 197 | case Copper.OPTION_URI_PORT: return 'Uri-Port'; 198 | case Copper.OPTION_URI_PATH: return 'Uri-Path'; 199 | case Copper.OPTION_URI_QUERY: return 'Uri-Query'; 200 | 201 | case Copper.OPTION_LOCATION_PATH: return 'Location-Path'; 202 | case Copper.OPTION_LOCATION_QUERY: return 'Location-Query'; 203 | 204 | case Copper.OPTION_PROXY_URI: return 'Proxy-Uri'; 205 | case Copper.OPTION_PROXY_SCHEME: return 'Proxy-Scheme'; 206 | 207 | case Copper.OPTION_IF_MATCH: return 'If-Match'; 208 | case Copper.OPTION_IF_NONE_MATCH: return 'If-None-Match'; 209 | case Copper.OPTION_ETAG: return 'ETag'; 210 | 211 | case Copper.OPTION_OBSERVE: return 'Observe'; 212 | 213 | case Copper.OPTION_BLOCK2: return 'Block2'; 214 | case Copper.OPTION_BLOCK1: return 'Block1'; 215 | 216 | case Copper.OPTION_SIZE2: return 'Size2'; 217 | case Copper.OPTION_SIZE1: return 'Size1'; 218 | 219 | default: return 'Unknown '+number; 220 | } 221 | }; 222 | 223 | Copper.getContentFormatName = function(type) { 224 | switch (type) { 225 | case Copper.CONTENT_TYPE_TEXT_PLAIN: return 'text/plain'; break; 226 | case Copper.CONTENT_TYPE_TEXT_XML: return 'text/xml'; break; 227 | case Copper.CONTENT_TYPE_TEXT_CSV: return 'text/csv'; break; 228 | case Copper.CONTENT_TYPE_TEXT_HTML: return 'text/html'; break; 229 | case Copper.CONTENT_TYPE_IMAGE_GIF: return 'image/gif'; break; 230 | case Copper.CONTENT_TYPE_IMAGE_JPEG: return 'image/jpeg'; break; 231 | case Copper.CONTENT_TYPE_IMAGE_PNG: return 'image/png'; break; 232 | case Copper.CONTENT_TYPE_IMAGE_TIFF: return 'image/tiff'; break; 233 | case Copper.CONTENT_TYPE_AUDIO_RAW: return 'audio/raw'; break; 234 | case Copper.CONTENT_TYPE_VIDEO_RAW: return 'video/raw'; break; 235 | case Copper.CONTENT_TYPE_APPLICATION_LINK_FORMAT: return 'application/link-format'; break; 236 | case Copper.CONTENT_TYPE_APPLICATION_XML: return 'application/xml'; break; 237 | case Copper.CONTENT_TYPE_APPLICATION_OCTET_STREAM: return 'application/octet-stream'; break; 238 | case Copper.CONTENT_TYPE_APPLICATION_RDF_XML: return 'application/rdf+xml'; break; 239 | case Copper.CONTENT_TYPE_APPLICATION_SOAP_XML: return 'application/soap+xml'; break; 240 | case Copper.CONTENT_TYPE_APPLICATION_ATOM_XML: return 'application/atom+xml'; break; 241 | case Copper.CONTENT_TYPE_APPLICATION_XMPP_XML: return 'application/xmpp+xml'; break; 242 | case Copper.CONTENT_TYPE_APPLICATION_EXI: return 'application/exi'; break; 243 | case Copper.CONTENT_TYPE_APPLICATION_FASTINFOSET: return 'application/fastinfoset'; break; 244 | case Copper.CONTENT_TYPE_APPLICATION_SOAP_FASTINFOSET: return 'application/soap+fastinfoset'; break; 245 | case Copper.CONTENT_TYPE_APPLICATION_JSON: return 'application/json'; break; 246 | case Copper.CONTENT_TYPE_APPLICATION_X_OBIX_BINARY: return 'application/x-obix-binary'; break; 247 | case Copper.CONTENT_TYPE_APPLICATION_CBOR: return 'application/cbor'; break; 248 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TEXT: return 'application/vnd.oma.lwm2m+text'; break; 249 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_TLV: return 'application/vnd.oma.lwm2m+tlv'; break; 250 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_JSON: return 'application/vnd.oma.lwm2m+json'; break; 251 | case Copper.CONTENT_TYPE_APPLICATION_VND_OMA_LWM2M_OPAQUE: return 'application/vnd.oma.lwm2m+opaque'; break; 252 | default: return 'unknown/unknown'; 253 | } 254 | }; 255 | 256 | // CoAP RFC 7252 implementation 257 | //////////////////////////////////////////////////////////////////////////////// 258 | 259 | Copper.serialize = function(message) { 260 | let byteArray = new Array(); 261 | let tempByte = 0x00; 262 | 263 | // first byte: version, type, and token length 264 | tempByte = (0x03 & Copper.VERSION) << 6; // using const for sending packets 265 | tempByte |= (0x03 & message.type) << 4; 266 | tempByte |= (0x0F & message.token.length); 267 | 268 | byteArray.push(tempByte); 269 | 270 | // second byte: method or response code 271 | byteArray.push(0xFF & message.code); 272 | 273 | // third and forth byte: message ID (MID) 274 | byteArray.push(0xFF & (message.mid >>> 8)); 275 | byteArray.push(0xFF & message.mid); 276 | 277 | for (let i in message.token) { 278 | byteArray.push(0xFF & message.token[i]); 279 | } 280 | 281 | // options 282 | message.optionCount = 0; 283 | let optNumber = 0; 284 | 285 | for (let optTypeIt in message.options) { 286 | 287 | if (!Array.isArray(message.options[optTypeIt][1])) { 288 | continue; 289 | } else { 290 | 291 | Copper.logEvent("SERIALIZE: Option "+Copper.getOptionName(optTypeIt)); 292 | 293 | let splitOption = new Array(); 294 | if (optTypeIt==Copper.OPTION_LOCATION_PATH || 295 | optTypeIt==Copper.OPTION_LOCATION_QUERY || 296 | optTypeIt==Copper.OPTION_URI_PATH || 297 | optTypeIt==Copper.OPTION_URI_QUERY) { 298 | 299 | let separator = '/'; // 0x002F 300 | if (optTypeIt==Copper.OPTION_LOCATION_QUERY || optTypeIt==Copper.OPTION_URI_QUERY) { 301 | separator = '&'; // 0x0026 302 | } 303 | 304 | if (Copper.bytes2str(message.options[optTypeIt][1])!="") { 305 | let splitString = Copper.bytes2str(message.options[optTypeIt][1]).split(separator); 306 | for (let s in splitString) { 307 | // sending decoded segments on the wire 308 | splitOption.push(Copper.str2bytes(decodeURIComponent(splitString[s]))); 309 | } 310 | } 311 | } else { 312 | splitOption.push(message.options[optTypeIt][1]); 313 | } 314 | 315 | while ((opt = splitOption.shift())) { 316 | 317 | let optDelta = optTypeIt - optNumber; 318 | 319 | let delta = Copper.optionNibble(optDelta); 320 | let len = Copper.optionNibble(opt.length); 321 | 322 | byteArray.push(0xFF & (delta<<4 | len)); 323 | 324 | if (delta==13) { 325 | byteArray.push(optDelta-13); 326 | } else if (delta==14) { 327 | byteArray.push( (optDelta-269)>>>8 ); 328 | byteArray.push( 0xFF & (optDelta-269) ); 329 | } 330 | if (len==13) { 331 | byteArray.push(opt.length-13); 332 | } else if (len==14) { 333 | byteArray.push( (opt.length)>>>8 ); 334 | byteArray.push( 0xFF & (opt.length-269) ); 335 | } 336 | 337 | // add option value 338 | for (let i in opt) byteArray.push(opt[i]); 339 | 340 | message.optionCount++; 341 | 342 | optNumber = optTypeIt; 343 | } 344 | } 345 | } 346 | 347 | // option terminator 348 | if (message.payload.length>0) { 349 | byteArray.push(0xFF); 350 | } 351 | 352 | // serialize as string 353 | let packet = Copper.bytes2data(byteArray); 354 | 355 | // payload 356 | packet += Copper.bytes2data(message.payload); 357 | 358 | // finished 359 | return packet; 360 | }; 361 | 362 | Copper.parse = function(packet) { 363 | 364 | Copper.logEvent('PACKET (hex): ' + packet.map(function(x){return x.toString(16).toUpperCase();})); 365 | 366 | // first byte: version, type, and option count 367 | let tempByte = packet.shift(); 368 | let tokenLength = parseInt(0x0F & tempByte); 369 | let version = 0xFF & ((tempByte & 0xC0) >>> 6); 370 | if (version != Copper.VERSION) { 371 | throw new Error('Cannot parse CoAP version '+version); 372 | } 373 | 374 | // create the message 375 | var message = new Copper.CoapMessage( 0x03 & ((tempByte) >>> 4), packet.shift() ); 376 | 377 | // third and forth byte: message ID (MID) 378 | message.mid = packet.shift() << 8; 379 | message.mid |= packet.shift(); 380 | 381 | Copper.logEvent("PARSE: Token length = "+tokenLength); 382 | for (let i=0; i0) { 432 | optLen += 1 + message.options[optNumber][0]; 433 | opt = message.options[optNumber][1].concat(separator).concat(opt); 434 | } 435 | } 436 | 437 | message.options[optNumber] = new Array(optLen, opt); 438 | 439 | } else { 440 | message.payload = packet; 441 | break; 442 | } 443 | } 444 | 445 | return message; 446 | }; 447 | 448 | 449 | //Protocol helper functions 450 | //////////////////////////////////////////////////////////////////////////////// 451 | 452 | Copper.optionNibble = function(value) { 453 | if (value < 13) { 454 | return (0xFF & value); 455 | } else if (value <= 0xFF+13) { 456 | return 13; 457 | } else if (value <= 0xFFFF+269) { 458 | return 14; 459 | } else { 460 | throw new Error('Option delta/length larger than 65804 not allowed'); 461 | } 462 | }; 463 | 464 | Copper.isPowerOfTwo = function(i) { 465 | return ((i & (i-1))==0); 466 | }; 467 | 468 | Copper.leadingZero = function(num, len) { 469 | if (!len) len = 2; 470 | num = ''+num; 471 | while (num.length 127) && (c < 2048)) { 505 | b.push(0xFF & ((c >> 6) | 192)); 506 | b.push(0xFF & ((c & 63) | 128)); 507 | } else { 508 | b.push(0xFF & ((c >> 12) | 224)); 509 | b.push(0xFF & (((c >> 6) & 63) | 128)); 510 | b.push(0xFF & ((c & 63) | 128)); 511 | } 512 | } 513 | } 514 | 515 | return b; 516 | }; 517 | 518 | Copper.bytes2str = function(b) { 519 | 520 | let str = ''; 521 | var replaced = 0; 522 | for (let i=0; i= 32 && c < 127)) { 527 | str += String.fromCharCode(c); 528 | } else if(Copper.utf8 && c >= 192 && c < 224 && (i+1 < b.length) && (b[i+1] & 0xc0) == 0x80) { 529 | let c1 = c & 0x1f; 530 | let c2 = b[i+1] & 0x3F; 531 | str += String.fromCharCode((c1 << 6) | c2); 532 | i += 1; 533 | } else if (Copper.utf8 && c >= 224 && c < 240 && (i+2 < b.length) && (b[i+1] & 0xc0) == 0x80 && (b[i+2] & 0xc0) == 0x80) { 534 | let c1 = c & 0x0f; 535 | let c2 = b[i+1] & 0x3F; 536 | let c3 = b[i+2] & 0x3F; 537 | str += String.fromCharCode((c1 << 12) | (c2 << 6) | c3); 538 | i += 2; 539 | } else if (Copper.utf8 && c >= 240 && i+3 < b.length) { 540 | // 4-byte UTF-8 541 | str += String.fromCharCode(0xFFFD); // char '�' 542 | replaced++; 543 | i += 3; 544 | } else if (Copper.utf8 && c >= 128) { 545 | // Incomplete UTF-8 encoding 546 | str += String.fromCharCode(0xFFFD); // char '�' 547 | replaced++; 548 | } else { 549 | if (c < 32) { 550 | str += String.fromCharCode(0x2400 + c); // replacement character block 551 | } else { 552 | str += String.fromCharCode(0xFFFD); // char '�' 553 | replaced++; 554 | } 555 | // str += "\\x" + (c < 16 ? "0" : "") + c.toString(16); 556 | } 557 | } 558 | if (replaced > 0) { 559 | Copper.logEvent('bytes2str: replaced ' + replaced + ' invalid characters'); 560 | } 561 | return str; 562 | }; 563 | 564 | Copper.int2bytes = function(i) { 565 | var b = new Array(0); 566 | while (i>0) { 567 | b.unshift(0xFF & i); 568 | i >>>= 8; 569 | } 570 | return b; 571 | }; 572 | 573 | Copper.bytes2int = function(b) { 574 | var i = 0; 575 | for (let k in b) { 576 | i = (i << 8) | b[k]; 577 | } 578 | //convert to unsigned int 579 | return i>>>0; 580 | }; 581 | 582 | Copper.hex2bytes = function(h) { 583 | var b = new Array(); 584 | for (let i=h.length-2; i>0; i-=2) { 585 | b.unshift(parseInt(('0x'+h.substr(i,2)).replace(/xx/, 'x'))); 586 | } 587 | return b; 588 | }; 589 | 590 | Copper.bytes2hex = function(b) { 591 | 592 | if (!b || !Array.isArray(b) || b.length==0) { 593 | return 'empty'; 594 | } 595 | 596 | var hex = '0x'; 597 | for (let k in b) { 598 | if (b[k]!==undefined) { 599 | hex += Copper.leadingZero(b[k].toString(16).toUpperCase()); 600 | } else { 601 | hex += '--'; 602 | } 603 | } 604 | 605 | return hex; 606 | }; 607 | 608 | Copper.str2hex = function(s) { 609 | var temp; 610 | if (s.substr(0,2)=='0x') { 611 | temp = Copper.hex2bytes(s); 612 | } else { 613 | temp = Copper.str2bytes(s); 614 | } 615 | 616 | return Copper.bytes2hex(temp); 617 | }; 618 | 619 | Copper.float2bytes = function(value) { 620 | var bytes = 0; 621 | switch (value) { 622 | case Number.POSITIVE_INFINITY: bytes = 0x7F800000; break; 623 | case Number.NEGATIVE_INFINITY: bytes = 0xFF800000; break; 624 | case +0.0: bytes = 0x40000000; break; 625 | case -0.0: bytes = 0xC0000000; break; 626 | default: 627 | if (Number.isNaN(value)) { bytes = 0x7FC00000; break; } 628 | 629 | if (value <= -0.0) { 630 | bytes = 0x80000000; 631 | value = -value; 632 | } 633 | 634 | var exponent = Math.floor(Math.log(value) / Math.log(2)); 635 | var significand = ((value / Math.pow(2, exponent)) * 0x00800000) | 0; 636 | 637 | exponent += 127; 638 | if (exponent >= 0xFF) { 639 | exponent = 0xFF; 640 | significand = 0; 641 | } else if (exponent < 0) exponent = 0; 642 | 643 | bytes = bytes | (exponent << 23); 644 | bytes = bytes | (significand & ~(-1 << 23)); 645 | break; 646 | } 647 | return bytes; 648 | }; 649 | 650 | Copper.double2bytes = function(value) { 651 | 652 | var hiWord = 0, loWord = 0; 653 | switch (value) { 654 | case Number.POSITIVE_INFINITY: hiWord = 0x7FF00000; break; 655 | case Number.NEGATIVE_INFINITY: hiWord = 0xFFF00000; break; 656 | case +0.0: hiWord = 0x40000000; break; 657 | case -0.0: hiWord = 0xC0000000; break; 658 | default: 659 | if (Number.isNaN(value)) { hiWord = 0x7FF80000; break; } 660 | 661 | if (value <= -0.0) { 662 | hiWord = 0x80000000; 663 | value = -value; 664 | } 665 | 666 | var exponent = Math.floor(Math.log(value) / Math.log(2)); 667 | var significand = Math.floor((value / Math.pow(2, exponent)) * Math.pow(2, 52)); 668 | 669 | loWord = significand & 0xFFFFFFFF; 670 | significand /= Math.pow(2, 32); 671 | 672 | exponent += 1023; 673 | if (exponent >= 0x7FF) { 674 | exponent = 0x7FF; 675 | significand = 0; 676 | } else if (exponent < 0) exponent = 0; 677 | 678 | hiWord = hiWord | (exponent << 20); 679 | hiWord = hiWord | (significand & ~(-1 << 20)); 680 | break; 681 | } 682 | 683 | let bytes = new Array(0); 684 | 685 | bytes.unshift( loWord>>24 & 0xFF ); 686 | bytes.unshift( loWord>>16 & 0xFF ); 687 | bytes.unshift( loWord>>8 & 0xFF ); 688 | bytes.unshift( loWord>>0 & 0xFF ); 689 | bytes.unshift( hiWord>>24 & 0xFF ); 690 | bytes.unshift( hiWord>>16 & 0xFF ); 691 | bytes.unshift( hiWord>>8 & 0xFF ); 692 | bytes.unshift( hiWord>>0 & 0xFF ); 693 | 694 | return bytes; 695 | }; 696 | -------------------------------------------------------------------------------- /content/coap/TransactionHandler.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | *******************************************************************************/ 31 | /** 32 | * \file 33 | * Code handling message transactions for the CoAP protocol 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | Copper.Transaction = function(myMessage, myTimer) { 39 | this.message = myMessage; 40 | this.timer = myTimer; 41 | 42 | this.rttStart = new Date().getTime(); 43 | 44 | return this; 45 | }; 46 | Copper.Transaction.prototype = { 47 | message : null, 48 | timer : null, 49 | 50 | rttStart : 0 51 | }; 52 | 53 | Copper.TransactionHandler = function(myClient) { 54 | 55 | this.client = myClient; 56 | this.client.register( Copper.myBind(this, this.handle) ); 57 | 58 | this.midGenerator = parseInt(Math.random()*0x10000); 59 | 60 | this.transactions = new Object(); 61 | this.requests = new Object(); 62 | this.registeredTokens = new Object(); 63 | this.registeredMIDs = new Object(); 64 | this.dupFilter = new Array(); 65 | this.dupCache = new Object(); 66 | 67 | return this; 68 | }; 69 | 70 | Copper.TransactionHandler.prototype = { 71 | 72 | midGenerator : 0, 73 | 74 | client : null, 75 | defaultCB : null, 76 | 77 | transactions : null, 78 | 79 | requests : null, 80 | registeredTokens : null, 81 | registeredMIDs : null, 82 | 83 | dupFilter : null, 84 | dupCache : null, 85 | 86 | registerCallback : function(myCB) { 87 | this.defaultCB = myCB; 88 | }, 89 | 90 | nextMID : function() { 91 | this.midGenerator = (this.midGenerator+1) % 0x10000; 92 | //return Math.pow(2, this.midGenerator) % 419; 93 | return this.midGenerator; 94 | }, 95 | 96 | stopRetransmission : function(token) { 97 | for (let mid in this.transactions) { 98 | if (this.transactions[mid].message.getToken()==token) { 99 | if (this.transactions[mid].timer) { 100 | window.clearTimeout(this.transactions[mid].timer); 101 | } 102 | Copper.logEvent('INFO: Stopping MID '+mid); 103 | delete this.transactions[mid]; 104 | } 105 | } 106 | }, 107 | 108 | stopRetransmissions : function() { 109 | for (let mid in this.transactions) { 110 | if (this.transactions[mid].timer) { 111 | window.clearTimeout(this.transactions[mid].timer); 112 | } 113 | Copper.logEvent('INFO: Stopping MID '+mid); 114 | delete this.transactions[mid]; 115 | } 116 | }, 117 | 118 | cancelTransactions : function() { 119 | this.stopRetransmissions(); 120 | 121 | this.requests = new Object(); 122 | this.registeredTokens = new Object(); 123 | this.registeredMIDs = new Object(); 124 | 125 | Copper.observer.clean(); 126 | }, 127 | 128 | registerToken : function(token, cb) { 129 | Copper.logEvent('INFO: Registering token '+token); 130 | this.registeredTokens[token] = cb; 131 | }, 132 | 133 | deRegisterToken : function(token) { 134 | if (this.registeredTokens[token]) { 135 | Copper.logEvent('INFO: Deregistering token '+token); 136 | delete this.registeredTokens[token]; 137 | } 138 | for (let i in this.registeredTokens) { 139 | if (this.registeredTokens[i]) Copper.logEvent(' '+i); 140 | } 141 | }, 142 | 143 | send : function(message, reqCB) { 144 | 145 | if (this.client.ended) return; 146 | 147 | // set MID for message 148 | if (message.getMID()==-1) { 149 | message.setMID( this.nextMID() ); 150 | } 151 | 152 | var that = this; // coping with the JavaScript scope... 153 | var timer = null; 154 | 155 | // store reliable transaction 156 | if (message.isConfirmable()) { 157 | if (Copper.behavior.retransmissions) { 158 | // schedule resend without RANDOM_FACTOR since we already have human jitter 159 | timer = window.setTimeout(function(){Copper.myBind(that,that.resend(message.getMID()));}, Copper.RESPONSE_TIMEOUT); 160 | } else { 161 | // also schedule 'not responding' timeout when retransmissions are disabled 162 | timer = window.setTimeout(function(){Copper.myBind(that,that.resend(message.getMID()));}, 16000); // 16 seconds 163 | } 164 | Copper.logEvent('INFO: Storing MID '+ message.getMID()); 165 | this.transactions[message.getMID()] = new Copper.Transaction(message, timer); 166 | } 167 | 168 | // store request callback through token matching 169 | if (message.isRequest()) { 170 | 171 | while (this.requests[message.getToken()]!=null && this.registeredTokens[message.getToken()]==null) { 172 | Copper.logEvent('INFO: Default token already in use'); 173 | message.setToken([parseInt(Math.random()*0x100), parseInt(Math.random()*0x100)]); 174 | } 175 | this.requests[message.getToken()] = reqCB===undefined ? this.defaultCB : reqCB; 176 | 177 | // also save callback by MID 178 | this.registeredMIDs[message.getMID()] = this.requests[message.getToken()]; 179 | 180 | // store notification (needed for NON) 181 | } else if (message.isResponse() && message.getObserve()!=null) { 182 | this.registeredMIDs[message.getMID()] = reqCB; 183 | 184 | // store ping 185 | } else if (message.getType()==Copper.MSG_TYPE_CON && message.getCode()==0) { 186 | this.registeredMIDs[message.getMID()] = reqCB; 187 | } 188 | 189 | Copper.logMessage(message, true); 190 | 191 | this.client.send( Copper.serialize(message) ); 192 | }, 193 | 194 | resend : function(mid) { 195 | if (Copper.behavior.retransmissions && this.transactions[mid]!==undefined && (this.transactions[mid].message.getRetries() < Copper.MAX_RETRANSMIT)) { 196 | 197 | var that = this; 198 | this.transactions[mid].message.incRetries(); 199 | 200 | var timeout = Copper.RESPONSE_TIMEOUT*Math.pow(2,this.transactions[mid].message.getRetries()); 201 | this.transactions[mid].timer = window.setTimeout(function(){Copper.myBind(that,that.resend(mid));}, timeout); 202 | 203 | Copper.logMessage(this.transactions[mid].message, true); 204 | 205 | this.client.send( Copper.serialize(this.transactions[mid].message) ); 206 | 207 | Copper.popup(Copper.hostname+':'+Copper.port, 'Re-transmitting message '+mid+' ('+this.transactions[mid].message.getRetries()+'/'+Copper.MAX_RETRANSMIT+')'); 208 | 209 | } else { 210 | Copper.logWarning('Message ' + mid + ' timed out.'); 211 | this.cancelTransactions(); 212 | delete this.transactions[mid]; 213 | } 214 | }, 215 | 216 | handle : function(datagram) { 217 | // parse byte message to CoAP message 218 | let message = Copper.parse(datagram); 219 | 220 | Copper.logMessage(message, false); 221 | 222 | if (this.transactions[message.getMID()]) { 223 | // calculate round trip time 224 | var ms = (new Date().getTime() - this.transactions[message.getMID()].rttStart); 225 | message.getRTT = function() { return ms; }; 226 | Copper.logEvent('INFO: MID ' +message.getMID() + ' has RTT ' + ms + ' ms'); 227 | 228 | // stop retransmission 229 | Copper.logEvent('INFO: Closing MID ' + message.getMID() ); 230 | if (this.transactions[message.getMID()].timer) window.clearTimeout(this.transactions[message.getMID()].timer); 231 | 232 | // clear transaction 233 | delete this.transactions[message.getMID()]; 234 | 235 | // filter duplicates 236 | } else if (this.dupFilter.indexOf(message.getMID()) != -1) { 237 | 238 | if (message.getType()==Copper.MSG_TYPE_CON) { 239 | var reply = this.dupCache[message.getMID()]; 240 | if (reply) { 241 | Copper.logEvent('INFO: Replying to duplicate (Message ID: '+message.getMID()+')'); 242 | this.send(reply); 243 | } else if (message.getType()==Copper.MSG_TYPE_CON) { 244 | Copper.logEvent('INFO: Acknowledging duplicate (Message ID: '+message.getMID()+')'); 245 | this.ack(message.getMID()); 246 | } 247 | } else { 248 | Copper.logEvent('INFO: Ignoring duplicate (Message ID: '+message.getMID()+')'); 249 | } 250 | return; 251 | } 252 | 253 | // callback for message 254 | var callback = null; 255 | 256 | // Requests 257 | if (message.isRequest()) { 258 | 259 | // no server role in this add-on 260 | callback = null; 261 | 262 | // Responses 263 | } else if (message.isResponse()) { 264 | // request matching by token 265 | if (this.requests[message.getToken()]) { 266 | 267 | // separate response 268 | if (!this.registeredMIDs[message.getMID()]) { 269 | if (message.getType()==Copper.MSG_TYPE_CON || message.getType()==Copper.MSG_TYPE_NON) { 270 | Copper.logEvent('INFO: Separate response as implicit acknowledgement for token: '+message.getToken() ); 271 | // stop retransmission if implicit acknowledgement 272 | this.stopRetransmission(message.getToken()); 273 | } 274 | } 275 | 276 | callback = this.requests[message.getToken()]; 277 | 278 | delete this.requests[message.getToken()]; 279 | delete this.registeredMIDs[message.getMID()]; 280 | 281 | // check registered Tokens, e.g., subscriptions 282 | } else if (this.registeredTokens[message.getToken()]) { 283 | callback = this.registeredTokens[message.getToken()]; 284 | 285 | // error 286 | } else { 287 | Copper.logEvent('INFO: Received unknown token'); 288 | 289 | if (Copper.behavior.showUnknown) { 290 | // hack for additional info 291 | message.getCopperCode = function() { return 'Unknown token'; }; 292 | this.defaultCB(message); 293 | } 294 | 295 | if (Copper.behavior.rejectUnknown && (message.getType()==Copper.MSG_TYPE_CON || message.getType()==Copper.MSG_TYPE_NON)) { 296 | Copper.logEvent('INFO: Rejecting unknown token'); 297 | this.reset(message.getMID()); 298 | } 299 | 300 | return; 301 | } 302 | 303 | // ACK separate response before handler (which might elicit a new request) 304 | if (message.getType()==Copper.MSG_TYPE_CON) { 305 | this.ack(message.getMID()); 306 | } 307 | 308 | // Empty messages 309 | } else { 310 | callback = this.registeredMIDs[message.getMID()]; 311 | 312 | delete this.registeredMIDs[message.getMID()]; 313 | 314 | // separate response 315 | if (message.getType()==Copper.MSG_TYPE_ACK && message.getCode()==Copper.EMPTY) { 316 | message.getCopperCode = function() { return 'Separate response inbound'; }; 317 | } 318 | } 319 | 320 | // callback might set reply for message used by deduplication 321 | if (callback) { 322 | try { 323 | callback(message); 324 | } catch (ex) { 325 | ex.message = 'Message callback failed:\n' + ex.message; 326 | Copper.logError(ex); 327 | } 328 | } 329 | 330 | // piggyback response or ack received CON messages 331 | if (message.reply) { 332 | this.send(message.reply); 333 | } 334 | 335 | // add to duplicates filter 336 | if (message.getType()!=Copper.MSG_TYPE_RST) { 337 | this.dupFilter.unshift(message.getMID()); 338 | if (message.reply) this.dupCache[message.getMID()] = message.reply; 339 | if (this.dupFilter.length>10) { 340 | delete this.dupCache[this.dupFilter.pop()]; 341 | } 342 | } 343 | }, 344 | 345 | ack : function(mid) { 346 | var ack = new Copper.CoapMessage(Copper.MSG_TYPE_ACK); 347 | ack.setMID( mid ); 348 | Copper.popup(Copper.hostname+':'+Copper.port, 'Sending ACK for message '+mid); 349 | this.send( ack ); 350 | }, 351 | 352 | reset : function(mid) { 353 | var rst = new Copper.CoapMessage(Copper.MSG_TYPE_RST); 354 | rst.setMID( mid ); 355 | Copper.popup(Copper.hostname+':'+Copper.port, 'Sending RST for message '+mid); 356 | this.send( rst ); 357 | }, 358 | 359 | shutdown : function() { 360 | this.client.shutdown(); 361 | } 362 | }; 363 | -------------------------------------------------------------------------------- /content/coap/UdpClient.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | *******************************************************************************/ 31 | /** 32 | * \file 33 | * Code handling the UDP communication for the CoAP protocol 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | /* 39 | * FIXME: inputStream does not separate single datagrams. 40 | * Thus, increased traffic results in merged datagrams, i.e., one or more datagrams are added to the payload of the first one. 41 | * A workaround will probably need native code to provide a datagram handler (transport-service) that pipes them to/from Firefox. 42 | */ 43 | 44 | Copper.UdpClient = function(remoteHost, remotePort) { 45 | 46 | // createTransport requires plain IPv6 address 47 | this.host = remoteHost.replace(/\[/,'').replace(/\]/,''); 48 | this.port = remotePort; 49 | 50 | this.transportService = Components.classes["@mozilla.org/network/socket-transport-service;1"].getService(Components.interfaces.nsISocketTransportService); 51 | this.pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance(Components.interfaces.nsIInputStreamPump); 52 | 53 | this.socket = this.transportService.createTransport(["udp"], 1, this.host, this.port, null); 54 | 55 | this.outputStream = this.socket.openOutputStream(0, 0, 0); 56 | this.inputStream = this.socket.openInputStream(0, 0, 0); // 1,0,0 = OPEN_BLOCKING 57 | 58 | this.pump.init(this.inputStream, -1, -1, 0, 0, false); 59 | this.pump.asyncRead(this, null); 60 | 61 | this.localAddr = null; 62 | this.callback = null; 63 | this.lastSend = null; 64 | this.ended = false; 65 | 66 | return this; 67 | }; 68 | 69 | Copper.UdpClient.prototype = { 70 | 71 | register : function(myCB) { 72 | this.callback = myCB; 73 | }, 74 | 75 | // stream observer functions 76 | onStartRequest : function(request, context) { 77 | // do nothing 78 | }, 79 | 80 | onStopRequest : function(request, context, status) { 81 | if (!this.ended) { 82 | this.shutdown(); 83 | throw new Error('Host/network unreachable'); 84 | } else { 85 | Copper.logWarning('Illegal UdpClient state'); 86 | } 87 | }, 88 | 89 | onDataAvailable : function(request, context, inputStream, offset, count) { 90 | try { 91 | let sis = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream); 92 | sis.init(inputStream); 93 | 94 | var byteArray = new Array(); 95 | for (let i=0; i4) { 101 | Copper.logEvent('UDP: Concatenated 4 + ' + (count-4) + ' bytes'); 102 | if (this.callback) this.callback(byteArray); 103 | // reset for next datagram 104 | i = -1; // i++ still coming 105 | count -= 4; 106 | byteArray = new Array(); 107 | } 108 | } 109 | 110 | Copper.logEvent('UDP: Received ' + byteArray.length + ' bytes'); 111 | 112 | if (this.callback) this.callback(byteArray); 113 | 114 | 115 | } catch (ex) { 116 | Copper.logError(ex); 117 | } 118 | }, 119 | 120 | // UdpClient functions 121 | shutdown : function() { 122 | // will also trigger onStopRequest() 123 | this.ended = true; 124 | this.outputStream.close(); 125 | this.inputStream.close(); 126 | this.socket.close(0); 127 | }, 128 | 129 | send : function(datagram) { 130 | 131 | if (this.ended) return; 132 | 133 | // the transport API also concatenates outgoing datagrams when sent too quickly 134 | let since = new Date() - this.lastSend; 135 | if (since<30) { 136 | var that = this; 137 | window.setTimeout( 138 | function() { Copper.myBind(that,that.send(datagram)); }, 139 | 30-since); 140 | return; 141 | } 142 | 143 | this.lastSend = new Date(); 144 | 145 | try { 146 | this.outputStream.write(datagram, datagram.length); 147 | 148 | Copper.logEvent('UDP: Sent ' + datagram.length + ' bytes'); 149 | } catch (ex) { 150 | Copper.logError(ex); 151 | } 152 | }, 153 | 154 | getAddr : function() { 155 | 156 | try { 157 | this.localAddr = this.socket.getScriptableSelfAddr(); 158 | return this.localAddr; 159 | } catch (ex) { 160 | return null; 161 | } 162 | }, 163 | 164 | setTimeout : function(time) { 165 | this.socket.setTimeout(this.socket.TIMEOUT_READ_WRITE, time); 166 | } 167 | }; 168 | -------------------------------------------------------------------------------- /content/launcher.xul: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Cupper (Cu) does not come with a button that 44 | you have to press to access CoAP resources. 45 | Simply enter a CoAP URI into the address bar 46 | and hit enter. 47 | 48 | 49 | Use the 50 | Discover 51 | button to retrieve the list of available resources 52 | from /.well-known/core. 53 | It is useful to first Ping 54 | the server to see if the remote endpoint responds to CoAP. 55 | 56 | 57 | You can try with 58 | 59 | 60 | 61 | (copy & paste to browser address) 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /content/namespace.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Copper (Cu) CoAP user-agent. 30 | ******************************************************************************/ 31 | /** 32 | * \file 33 | * Namespace definition for the Copper CoAP Browser 34 | * 35 | * \author Matthias Kovatsch \author 36 | */ 37 | 38 | /** 39 | * Copper namespace. 40 | */ 41 | if ("undefined" == typeof(Copper)) { 42 | var Copper = {}; 43 | }; 44 | -------------------------------------------------------------------------------- /content/options.xul: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | 39 | 40 |