From d79d8da64b03af33603d2e83f3687744f03c6a7e Mon Sep 17 00:00:00 2001 From: Luc Date: Mon, 25 Feb 2019 10:31:21 +0800 Subject: [PATCH] Initial commit for code base --- .travis.yml | 48 + README.md | 1 + embedded/build.bat | 10 + embedded/embedded.h | 401 ++ embedded/footer.txt | 1 + embedded/gulpfile.js | 125 + embedded/header.txt | 24 + embedded/package-lock.json | 5671 +++++++++++++++++ embedded/package.json | 29 + embedded/tool.html.gz | Bin 0 -> 5916 bytes embedded/www/css/style.css | 105 + embedded/www/js/script.js | 430 ++ embedded/www/tool.html | 108 + esp3d/esp3d.ino | 19 + esp3d/src/configuration.h | 147 + esp3d/src/core/commands.cpp | 378 ++ esp3d/src/core/commands.h | 93 + esp3d/src/core/debug_esp3d.cpp | 39 + esp3d/src/core/debug_esp3d.h | 53 + esp3d/src/core/esp3d.cpp | 151 + esp3d/src/core/esp3d.h | 42 + esp3d/src/core/esp3doutput.cpp | 368 ++ esp3d/src/core/esp3doutput.h | 102 + esp3d/src/core/espcmd/ESP100.cpp | 65 + esp3d/src/core/espcmd/ESP101.cpp | 54 + esp3d/src/core/espcmd/ESP102.cpp | 80 + esp3d/src/core/espcmd/ESP103.cpp | 90 + esp3d/src/core/espcmd/ESP105.cpp | 66 + esp3d/src/core/espcmd/ESP106.cpp | 54 + esp3d/src/core/espcmd/ESP107.cpp | 65 + esp3d/src/core/espcmd/ESP108.cpp | 65 + esp3d/src/core/espcmd/ESP110.cpp | 127 + esp3d/src/core/espcmd/ESP111.cpp | 61 + esp3d/src/core/espcmd/ESP112.cpp | 65 + esp3d/src/core/espcmd/ESP115.cpp | 71 + esp3d/src/core/espcmd/ESP120.cpp | 65 + esp3d/src/core/espcmd/ESP121.cpp | 64 + esp3d/src/core/espcmd/ESP130.cpp | 65 + esp3d/src/core/espcmd/ESP131.cpp | 64 + esp3d/src/core/espcmd/ESP201.cpp | 122 + esp3d/src/core/espcmd/ESP400.cpp | 340 + esp3d/src/core/espcmd/ESP401.cpp | 110 + esp3d/src/core/espcmd/ESP410.cpp | 104 + esp3d/src/core/espcmd/ESP420.cpp | 964 +++ esp3d/src/core/espcmd/ESP444.cpp | 52 + esp3d/src/core/espcmd/ESP550.cpp | 54 + esp3d/src/core/espcmd/ESP555.cpp | 54 + esp3d/src/core/espcmd/ESP710.cpp | 53 + esp3d/src/core/espcmd/ESP720.cpp | 99 + esp3d/src/core/espcmd/ESP800.cpp | 214 + esp3d/src/core/genLinkedList.h | 223 + esp3d/src/core/hal.cpp | 244 + esp3d/src/core/hal.h | 58 + esp3d/src/core/settings_esp3d.cpp | 937 +++ esp3d/src/core/settings_esp3d.h | 130 + esp3d/src/include/esp3d_config.h | 29 + esp3d/src/include/sanity_esp3d.h | 86 + esp3d/src/include/version.h | 28 + esp3d/src/main.cpp | 36 + .../authentication/authentication_service.cpp | 336 + .../authentication/authentication_service.h | 96 + esp3d/src/modules/bluetooth/BT_service.cpp | 297 + esp3d/src/modules/bluetooth/BT_service.h | 82 + esp3d/src/modules/ethernet/ethconfig.cpp | 171 + esp3d/src/modules/ethernet/ethconfig.h | 48 + .../src/modules/filesystem/esp_filesystem.cpp | 768 +++ esp3d/src/modules/filesystem/esp_filesystem.h | 96 + esp3d/src/modules/http/embedded.h | 401 ++ .../modules/http/handles/handle-command.cpp | 48 + .../http/handles/handle-description_xml.cpp | 39 + .../http/handles/handle-filenotfound.cpp | 100 + .../src/modules/http/handles/handle-files.cpp | 204 + .../src/modules/http/handles/handle-login.cpp | 119 + .../src/modules/http/handles/handle-root.cpp | 53 + .../modules/http/handles/handle-updatefw.cpp | 54 + .../src/modules/http/handles/upload-files.cpp | 133 + .../modules/http/handles/upload-updatefw.cpp | 126 + esp3d/src/modules/http/http_server.cpp | 220 + esp3d/src/modules/http/http_server.h | 95 + esp3d/src/modules/network/netconfig.cpp | 373 ++ esp3d/src/modules/network/netconfig.h | 83 + esp3d/src/modules/network/netservices.cpp | 303 + esp3d/src/modules/network/netservices.h | 40 + esp3d/src/modules/serial/serial_service.cpp | 266 + esp3d/src/modules/serial/serial_service.h | 80 + esp3d/src/modules/telnet/telnet_server.cpp | 281 + esp3d/src/modules/telnet/telnet_server.h | 87 + .../modules/websocket/websocket_server.cpp | 236 + .../src/modules/websocket/websocket_server.h | 88 + esp3d/src/modules/wifi/wificonfig.cpp | 531 ++ esp3d/src/modules/wifi/wificonfig.h | 81 + libraries/ESP32SSDP/ESP32SSDP.cpp | 477 ++ libraries/ESP32SSDP/ESP32SSDP.h | 128 + libraries/ESP32SSDP/README.rst | 22 + libraries/ESP32SSDP/examples/SSDP/SSDP.ino | 51 + libraries/ESP32SSDP/keywords.txt | 53 + libraries/ESP32SSDP/library.properties | 9 + libraries/arduinoWebSockets/.gitignore | 29 + libraries/arduinoWebSockets/.travis.yml | 40 + libraries/arduinoWebSockets/LICENSE | 502 ++ libraries/arduinoWebSockets/README.md | 98 + .../Nginx/esp8266.ssl.reverse.proxy.conf | 83 + .../WebSocketClientAVR/WebSocketClientAVR.ino | 84 + .../esp32/WebSocketClient/WebSocketClient.ino | 106 + .../WebSocketClientSSL/WebSocketClientSSL.ino | 102 + .../esp32/WebSocketServer/WebSocketServer.ino | 100 + .../WebSocketClient/WebSocketClient.ino | 92 + .../WebSocketClientSSL/WebSocketClientSSL.ino | 88 + .../WebSocketClientSocketIO.ino | 113 + .../WebSocketClientStomp.ino | 149 + .../WebSocketClientStompOverSockJs.ino | 150 + .../WebSocketServer/WebSocketServer.ino | 86 + .../WebSocketServerAllFunctionsDemo.ino | 132 + .../WebSocketServerFragmentation.ino | 94 + .../WebSocketServerHttpHeaderValidation.ino | 86 + .../WebSocketServer_LEDcontrol.ino | 121 + .../ParticleWebSocketClient/application.cpp | 46 + libraries/arduinoWebSockets/library.json | 25 + .../arduinoWebSockets/library.properties | 9 + .../arduinoWebSockets/src/WebSockets.cpp | 654 ++ libraries/arduinoWebSockets/src/WebSockets.h | 312 + .../src/WebSocketsClient.cpp | 762 +++ .../arduinoWebSockets/src/WebSocketsClient.h | 136 + .../src/WebSocketsServer.cpp | 873 +++ .../arduinoWebSockets/src/WebSocketsServer.h | 212 + .../arduinoWebSockets/src/libb64/AUTHORS | 7 + .../arduinoWebSockets/src/libb64/LICENSE | 29 + .../arduinoWebSockets/src/libb64/cdecode.c | 98 + .../src/libb64/cdecode_inc.h | 28 + .../arduinoWebSockets/src/libb64/cencode.c | 119 + .../src/libb64/cencode_inc.h | 31 + .../arduinoWebSockets/src/libsha1/libsha1.c | 202 + .../arduinoWebSockets/src/libsha1/libsha1.h | 21 + .../arduinoWebSockets/tests/webSocket.html | 49 + .../tests/webSocketServer/index.js | 57 + .../tests/webSocketServer/package.json | 27 + libraries/arduinoWebSockets/travis/common.sh | 53 + 137 files changed, 26083 insertions(+) create mode 100644 embedded/build.bat create mode 100644 embedded/embedded.h create mode 100644 embedded/footer.txt create mode 100644 embedded/gulpfile.js create mode 100644 embedded/header.txt create mode 100644 embedded/package-lock.json create mode 100644 embedded/package.json create mode 100644 embedded/tool.html.gz create mode 100644 embedded/www/css/style.css create mode 100644 embedded/www/js/script.js create mode 100644 embedded/www/tool.html create mode 100644 esp3d/esp3d.ino create mode 100644 esp3d/src/configuration.h create mode 100644 esp3d/src/core/commands.cpp create mode 100644 esp3d/src/core/commands.h create mode 100644 esp3d/src/core/debug_esp3d.cpp create mode 100644 esp3d/src/core/debug_esp3d.h create mode 100644 esp3d/src/core/esp3d.cpp create mode 100644 esp3d/src/core/esp3d.h create mode 100644 esp3d/src/core/esp3doutput.cpp create mode 100644 esp3d/src/core/esp3doutput.h create mode 100644 esp3d/src/core/espcmd/ESP100.cpp create mode 100644 esp3d/src/core/espcmd/ESP101.cpp create mode 100644 esp3d/src/core/espcmd/ESP102.cpp create mode 100644 esp3d/src/core/espcmd/ESP103.cpp create mode 100644 esp3d/src/core/espcmd/ESP105.cpp create mode 100644 esp3d/src/core/espcmd/ESP106.cpp create mode 100644 esp3d/src/core/espcmd/ESP107.cpp create mode 100644 esp3d/src/core/espcmd/ESP108.cpp create mode 100644 esp3d/src/core/espcmd/ESP110.cpp create mode 100644 esp3d/src/core/espcmd/ESP111.cpp create mode 100644 esp3d/src/core/espcmd/ESP112.cpp create mode 100644 esp3d/src/core/espcmd/ESP115.cpp create mode 100644 esp3d/src/core/espcmd/ESP120.cpp create mode 100644 esp3d/src/core/espcmd/ESP121.cpp create mode 100644 esp3d/src/core/espcmd/ESP130.cpp create mode 100644 esp3d/src/core/espcmd/ESP131.cpp create mode 100644 esp3d/src/core/espcmd/ESP201.cpp create mode 100644 esp3d/src/core/espcmd/ESP400.cpp create mode 100644 esp3d/src/core/espcmd/ESP401.cpp create mode 100644 esp3d/src/core/espcmd/ESP410.cpp create mode 100644 esp3d/src/core/espcmd/ESP420.cpp create mode 100644 esp3d/src/core/espcmd/ESP444.cpp create mode 100644 esp3d/src/core/espcmd/ESP550.cpp create mode 100644 esp3d/src/core/espcmd/ESP555.cpp create mode 100644 esp3d/src/core/espcmd/ESP710.cpp create mode 100644 esp3d/src/core/espcmd/ESP720.cpp create mode 100644 esp3d/src/core/espcmd/ESP800.cpp create mode 100644 esp3d/src/core/genLinkedList.h create mode 100644 esp3d/src/core/hal.cpp create mode 100644 esp3d/src/core/hal.h create mode 100644 esp3d/src/core/settings_esp3d.cpp create mode 100644 esp3d/src/core/settings_esp3d.h create mode 100644 esp3d/src/include/esp3d_config.h create mode 100644 esp3d/src/include/sanity_esp3d.h create mode 100644 esp3d/src/include/version.h create mode 100644 esp3d/src/main.cpp create mode 100644 esp3d/src/modules/authentication/authentication_service.cpp create mode 100644 esp3d/src/modules/authentication/authentication_service.h create mode 100644 esp3d/src/modules/bluetooth/BT_service.cpp create mode 100644 esp3d/src/modules/bluetooth/BT_service.h create mode 100644 esp3d/src/modules/ethernet/ethconfig.cpp create mode 100644 esp3d/src/modules/ethernet/ethconfig.h create mode 100644 esp3d/src/modules/filesystem/esp_filesystem.cpp create mode 100644 esp3d/src/modules/filesystem/esp_filesystem.h create mode 100644 esp3d/src/modules/http/embedded.h create mode 100644 esp3d/src/modules/http/handles/handle-command.cpp create mode 100644 esp3d/src/modules/http/handles/handle-description_xml.cpp create mode 100644 esp3d/src/modules/http/handles/handle-filenotfound.cpp create mode 100644 esp3d/src/modules/http/handles/handle-files.cpp create mode 100644 esp3d/src/modules/http/handles/handle-login.cpp create mode 100644 esp3d/src/modules/http/handles/handle-root.cpp create mode 100644 esp3d/src/modules/http/handles/handle-updatefw.cpp create mode 100644 esp3d/src/modules/http/handles/upload-files.cpp create mode 100644 esp3d/src/modules/http/handles/upload-updatefw.cpp create mode 100644 esp3d/src/modules/http/http_server.cpp create mode 100644 esp3d/src/modules/http/http_server.h create mode 100644 esp3d/src/modules/network/netconfig.cpp create mode 100644 esp3d/src/modules/network/netconfig.h create mode 100644 esp3d/src/modules/network/netservices.cpp create mode 100644 esp3d/src/modules/network/netservices.h create mode 100644 esp3d/src/modules/serial/serial_service.cpp create mode 100644 esp3d/src/modules/serial/serial_service.h create mode 100644 esp3d/src/modules/telnet/telnet_server.cpp create mode 100644 esp3d/src/modules/telnet/telnet_server.h create mode 100644 esp3d/src/modules/websocket/websocket_server.cpp create mode 100644 esp3d/src/modules/websocket/websocket_server.h create mode 100644 esp3d/src/modules/wifi/wificonfig.cpp create mode 100644 esp3d/src/modules/wifi/wificonfig.h create mode 100644 libraries/ESP32SSDP/ESP32SSDP.cpp create mode 100644 libraries/ESP32SSDP/ESP32SSDP.h create mode 100644 libraries/ESP32SSDP/README.rst create mode 100644 libraries/ESP32SSDP/examples/SSDP/SSDP.ino create mode 100644 libraries/ESP32SSDP/keywords.txt create mode 100644 libraries/ESP32SSDP/library.properties create mode 100644 libraries/arduinoWebSockets/.gitignore create mode 100644 libraries/arduinoWebSockets/.travis.yml create mode 100644 libraries/arduinoWebSockets/LICENSE create mode 100644 libraries/arduinoWebSockets/README.md create mode 100644 libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf create mode 100644 libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino create mode 100644 libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino create mode 100644 libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino create mode 100644 libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino create mode 100644 libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino create mode 100644 libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp create mode 100644 libraries/arduinoWebSockets/library.json create mode 100644 libraries/arduinoWebSockets/library.properties create mode 100644 libraries/arduinoWebSockets/src/WebSockets.cpp create mode 100644 libraries/arduinoWebSockets/src/WebSockets.h create mode 100644 libraries/arduinoWebSockets/src/WebSocketsClient.cpp create mode 100644 libraries/arduinoWebSockets/src/WebSocketsClient.h create mode 100644 libraries/arduinoWebSockets/src/WebSocketsServer.cpp create mode 100644 libraries/arduinoWebSockets/src/WebSocketsServer.h create mode 100644 libraries/arduinoWebSockets/src/libb64/AUTHORS create mode 100644 libraries/arduinoWebSockets/src/libb64/LICENSE create mode 100644 libraries/arduinoWebSockets/src/libb64/cdecode.c create mode 100644 libraries/arduinoWebSockets/src/libb64/cdecode_inc.h create mode 100644 libraries/arduinoWebSockets/src/libb64/cencode.c create mode 100644 libraries/arduinoWebSockets/src/libb64/cencode_inc.h create mode 100644 libraries/arduinoWebSockets/src/libsha1/libsha1.c create mode 100644 libraries/arduinoWebSockets/src/libsha1/libsha1.h create mode 100644 libraries/arduinoWebSockets/tests/webSocket.html create mode 100644 libraries/arduinoWebSockets/tests/webSocketServer/index.js create mode 100644 libraries/arduinoWebSockets/tests/webSocketServer/package.json create mode 100644 libraries/arduinoWebSockets/travis/common.sh diff --git a/.travis.yml b/.travis.yml index 8b137891..20e25fb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1 +1,49 @@ +sudo: false +language: bash + +os: + - linux + +before_script: + - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz + - tar xf arduino-1.8.8-linux64.tar.xz + - mv arduino-1.8.8 $HOME/arduino_ide + - cd $HOME/arduino_ide/hardware + - mkdir esp8266com + - cd esp8266com + - git clone https://github.com/esp8266/Arduino.git esp8266 + - cd esp8266/tools + - python get.py + - cd .. + - echo 'build.flash_ld=eagle.flash.4m.ld' >> platform.txt + - echo 'build.flash_freq=40' >> platform.txt + - echo 'build.flash_size=4M' >> platform.txt + - echo 'build.flash_mode=dio' >> platform.txt + - echo 'build.f_cpu=160000000L' >> platform.txt + - echo 'build.flash_flags=-DFLASHMODE_DIO' >> platform.txt + - sed -i "s/build.lwip_lib=-llwip_gcc/build.lwip_lib=-llwip2-536-feat/g" ./platform.txt + - sed -i "s/build.lwip_include=lwip/build.lwip_include=lwip2/g" ./platform.txt + - sed -i "s/build.lwip_flags=-DLWIP_OPEN_SRC/build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0/g" ./platform.txt + - sed -i "s/generic.build.f_cpu=80000000L/generic.build.f_cpu=160000000L/g" ./boards.txt + - cd $HOME/arduino_ide/hardware + - mkdir esp32 + - cd esp32 + - git clone https://github.com/espressif/arduino-esp32.git esp32 + - cd esp32/tools + - python get.py + - cd .. + - echo 'build.flash_freq=40m' >> platform.txt + - echo 'build.partitions=min_spiffs' >> platform.txt + - mv $TRAVIS_BUILD_DIR/libraries/ESP32SSDP $HOME/arduino_ide/libraries/ + - mv $TRAVIS_BUILD_DIR/libraries/arduinoWebSockets $HOME/arduino_ide/libraries/ + script: + - cd $TRAVIS_BUILD_DIR + - source command.sh + - export PATH="$HOME/arduino_ide:$PATH" + - arduino --board esp8266com:esp8266:generic --save-prefs + - arduino --get-pref sketchbook.path + - build_sketch $TRAVIS_BUILD_DIR/esp3d/esp3d.ino + - arduino --board esp32:esp32:esp32 --save-prefs + - build_sketch $TRAVIS_BUILD_DIR/esp3d/esp3d.ino + diff --git a/README.md b/README.md index be97a8fc..e3455321 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # ESP3D 3.0

Coming Soon!

+

Do not use it !

diff --git a/embedded/build.bat b/embedded/build.bat new file mode 100644 index 00000000..77957010 --- /dev/null +++ b/embedded/build.bat @@ -0,0 +1,10 @@ +cd %~dp0 +cmd.exe /c gulp package +cmd.exe /c bin2c -o embedded.h -m tool.html.gz +cat header.txt > out.h +cat embedded.h >> out.h +cat footer.txt >> out.h +cat out.h > embedded.h +cat out.h > ../esp3d/src/modules/http/embedded.h +rm -f out.h +pause diff --git a/embedded/embedded.h b/embedded/embedded.h new file mode 100644 index 00000000..31ad3b91 --- /dev/null +++ b/embedded/embedded.h @@ -0,0 +1,401 @@ +/* + embedded.h - ESP3D data file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//data generated by https://github.com/AraHaan/bin2c +//bin2c Conversion Tool v0.14.0 - Windows - [FINAL]. +#ifndef __embedded_h +#define __embedded_h +/* Generated by bin2c, do not edit manually */ + +/* Contents of file tool.html.gz */ +#define tool_html_gz_size 5916 +const unsigned char tool_html_gz[5916] = { + 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x3C, 0x69, 0x73, 0xDB, 0x46, + 0x96, 0x7F, 0xA5, 0xDD, 0x53, 0x31, 0x89, 0x25, 0x40, 0xE2, 0xE2, 0x2D, 0xD2, 0xE3, 0xC4, 0xF6, + 0x44, 0x5B, 0x76, 0x9C, 0xB2, 0xE4, 0xF1, 0x6E, 0xD9, 0x2E, 0x17, 0x48, 0x34, 0x49, 0xAC, 0x41, + 0x80, 0x02, 0x9A, 0xA2, 0x64, 0x59, 0xFB, 0xDB, 0xF7, 0xBD, 0xEE, 0xC6, 0xC5, 0x4B, 0x47, 0x3C, + 0x3B, 0xF9, 0x90, 0xD0, 0x24, 0x80, 0x3E, 0x5E, 0xBF, 0xFB, 0xE8, 0x86, 0x72, 0xB2, 0xE0, 0xCB, + 0x70, 0x7C, 0xB2, 0x60, 0x9E, 0x3F, 0x3E, 0x49, 0xF9, 0x75, 0xC8, 0xC6, 0xD8, 0x72, 0x33, 0x8B, + 0x23, 0x6E, 0xCC, 0xBC, 0x65, 0x10, 0x5E, 0x0F, 0x52, 0x2F, 0x4A, 0x8D, 0x94, 0x25, 0xC1, 0x6C, + 0x68, 0x2C, 0x53, 0x83, 0xB3, 0x2B, 0x6E, 0xA4, 0xC1, 0x37, 0x66, 0x78, 0xFE, 0xFF, 0xAC, 0x53, + 0x3E, 0xB0, 0x4C, 0xF3, 0xA7, 0xA1, 0xB1, 0x61, 0x93, 0xAF, 0x01, 0x3F, 0xD0, 0x2B, 0xC0, 0x61, + 0x2B, 0x3C, 0xAE, 0xAE, 0x6E, 0x27, 0xB1, 0x7F, 0x5D, 0x59, 0x82, 0xFE, 0xCA, 0xC2, 0x4B, 0xC6, + 0x83, 0xA9, 0x47, 0x7E, 0x63, 0x6B, 0x46, 0xF5, 0xFC, 0x59, 0x7F, 0x9E, 0x04, 0x5E, 0xA8, 0x97, + 0x70, 0x28, 0xC1, 0x72, 0x57, 0x57, 0xC3, 0x30, 0x88, 0x98, 0xB1, 0x60, 0xC1, 0x7C, 0x01, 0x6B, + 0x35, 0x5D, 0xBB, 0xD7, 0xEE, 0x5A, 0xAE, 0x33, 0x9C, 0xC6, 0x61, 0x9C, 0x0C, 0xFE, 0xE6, 0x38, + 0xCE, 0x70, 0xE2, 0x4D, 0xBF, 0xCE, 0x93, 0x78, 0x1D, 0xF9, 0x86, 0x6A, 0x9D, 0xCD, 0x66, 0xB7, + 0xDC, 0x9B, 0x84, 0xEC, 0x66, 0x12, 0x27, 0x3E, 0x4B, 0x06, 0xE6, 0x50, 0xDE, 0x18, 0xE9, 0xCA, + 0x9B, 0x06, 0xD1, 0x1C, 0x1A, 0x96, 0xDE, 0x95, 0xB1, 0x09, 0x7C, 0xBE, 0x10, 0x14, 0xDC, 0x72, + 0xFF, 0x66, 0xB3, 0x08, 0x38, 0x13, 0x23, 0xD8, 0x20, 0x8A, 0x37, 0x89, 0xB7, 0x1A, 0xAE, 0x3C, + 0xDF, 0xC7, 0xE1, 0xF6, 0x72, 0x79, 0xCB, 0x17, 0x37, 0x82, 0x78, 0x2F, 0x0C, 0xE6, 0xD1, 0x20, + 0x64, 0x33, 0x7E, 0xDB, 0x14, 0x8B, 0x8C, 0x39, 0xD2, 0x3B, 0xE6, 0xC9, 0x98, 0xFB, 0xFA, 0x4E, + 0xD3, 0x22, 0x6F, 0x12, 0x42, 0xA8, 0x8E, 0xCA, 0x9B, 0x16, 0x37, 0xD9, 0x52, 0xBD, 0xC3, 0x34, + 0x5F, 0xB2, 0x04, 0x59, 0x16, 0x2A, 0x14, 0x78, 0xBC, 0xCA, 0xC8, 0x82, 0xDB, 0x81, 0xB5, 0xBA, + 0x22, 0x69, 0x1C, 0x06, 0x3E, 0xF9, 0x9B, 0xEF, 0xFB, 0x0A, 0x37, 0x23, 0xE5, 0x49, 0xB0, 0x62, + 0x7E, 0x8E, 0xD0, 0x20, 0xE2, 0x0B, 0x23, 0x9E, 0x19, 0xFC, 0x7A, 0xC5, 0xEA, 0xB1, 0xEF, 0x6B, + 0x37, 0x7B, 0xD8, 0xD7, 0xC7, 0xCF, 0xAD, 0x77, 0xB3, 0x8A, 0xD3, 0x80, 0x07, 0x71, 0x34, 0x48, + 0x58, 0xE8, 0xF1, 0xE0, 0x92, 0x0D, 0xFD, 0x20, 0x5D, 0x85, 0xDE, 0xF5, 0x60, 0x12, 0xC6, 0xD3, + 0xAF, 0x39, 0x7B, 0x50, 0xE8, 0xC4, 0x6A, 0x03, 0xE6, 0x82, 0x43, 0x3E, 0x9B, 0xC6, 0x89, 0x27, + 0x26, 0x46, 0x71, 0xC4, 0x32, 0x59, 0x4D, 0xA7, 0xD3, 0xDB, 0xA6, 0x37, 0x45, 0x38, 0x37, 0x85, + 0xA0, 0xF6, 0x88, 0xCF, 0x34, 0xCD, 0x6C, 0x20, 0xF1, 0x74, 0x6F, 0x30, 0x8B, 0xA7, 0xEB, 0x14, + 0xAE, 0x8B, 0x18, 0x38, 0x50, 0x9A, 0x7A, 0xDB, 0x5C, 0x79, 0x11, 0x0B, 0x6F, 0x96, 0x5E, 0x32, + 0x0F, 0x22, 0x63, 0x12, 0x73, 0x1E, 0x2F, 0x07, 0x36, 0x20, 0xB3, 0x5F, 0x27, 0x14, 0xB7, 0xB6, + 0x38, 0x95, 0xF1, 0x30, 0xF1, 0xFC, 0x60, 0x9D, 0x0E, 0x50, 0xE7, 0x32, 0x65, 0x9F, 0xC4, 0x57, + 0x46, 0xBA, 0xF0, 0xFC, 0x78, 0x33, 0x30, 0x09, 0xCE, 0xC2, 0x6F, 0x32, 0x9F, 0x78, 0x75, 0x53, + 0xC7, 0x4F, 0xD3, 0x6C, 0x6B, 0xC3, 0xFB, 0x0C, 0x52, 0x98, 0x1A, 0xC2, 0x30, 0x72, 0xAE, 0x01, + 0xC3, 0xB2, 0x0E, 0x54, 0x04, 0x68, 0xBB, 0xD9, 0xE5, 0xE8, 0x71, 0x45, 0x6F, 0xE3, 0x27, 0xA3, + 0x40, 0x35, 0x96, 0x68, 0x02, 0xBD, 0x30, 0x12, 0x54, 0xA3, 0x8C, 0x3A, 0x07, 0x79, 0x53, 0xF4, + 0xA1, 0x16, 0xEF, 0xE9, 0x52, 0x9C, 0xDC, 0xD6, 0xA8, 0x59, 0x9C, 0x2C, 0x61, 0x91, 0x88, 0x27, + 0x71, 0x78, 0x53, 0xD5, 0x04, 0x69, 0x49, 0xDE, 0x9A, 0xC7, 0x43, 0xA5, 0xB7, 0x0E, 0x32, 0x32, + 0x23, 0xA7, 0x83, 0xD4, 0xD8, 0xD0, 0xF0, 0x20, 0xE3, 0x6E, 0xB7, 0xDB, 0x87, 0x04, 0x59, 0xB4, + 0x06, 0x4B, 0x6F, 0xCE, 0xA4, 0x9E, 0xED, 0x8A, 0x17, 0x54, 0xEE, 0x7E, 0xE2, 0x0D, 0xA2, 0x94, + 0x71, 0x72, 0x40, 0x7E, 0xDD, 0xAA, 0x94, 0xEF, 0x1C, 0x6B, 0xC4, 0x06, 0x4F, 0xC0, 0xA1, 0x49, + 0xDB, 0x29, 0x0B, 0x87, 0x30, 0x2F, 0x65, 0x06, 0xE8, 0x6A, 0xBC, 0xE6, 0xA4, 0x69, 0xB5, 0x53, + 0xBD, 0x80, 0xBB, 0xD3, 0x57, 0x65, 0xB8, 0xB4, 0x82, 0x9B, 0xAA, 0xA8, 0x3B, 0x1D, 0x6F, 0xC6, + 0xFA, 0x43, 0x98, 0x81, 0x9C, 0x04, 0xAF, 0xF6, 0x08, 0xD2, 0x74, 0x13, 0x3A, 0x7B, 0x59, 0x87, + 0x65, 0xDA, 0xBA, 0xD5, 0x6D, 0xEB, 0xB6, 0xE3, 0xE8, 0xCD, 0x8E, 0xA6, 0x70, 0x40, 0x5E, 0xAF, + 0xB6, 0xEC, 0x4C, 0xAA, 0xEF, 0x84, 0x47, 0xB9, 0x2A, 0x04, 0x91, 0x90, 0xA7, 0xD4, 0x88, 0xEA, + 0x60, 0x53, 0x4A, 0x7E, 0x23, 0x45, 0xED, 0x9A, 0xE6, 0xB0, 0xE4, 0x4B, 0xA7, 0x2C, 0xE2, 0x2C, + 0xD9, 0x76, 0x6F, 0xCB, 0xC0, 0xF7, 0x43, 0x26, 0x43, 0x52, 0xBC, 0x9E, 0x2E, 0x0C, 0xF4, 0x08, + 0xC0, 0xCF, 0xA5, 0x17, 0x05, 0xAB, 0x75, 0x28, 0xFC, 0xCB, 0xF0, 0x70, 0xCF, 0x74, 0x9D, 0xA4, + 0xC0, 0xA2, 0x55, 0x1C, 0x08, 0xE0, 0xF7, 0xD4, 0x18, 0x21, 0xB7, 0x95, 0x97, 0x00, 0x46, 0xC3, + 0x23, 0xF1, 0xE0, 0x81, 0xFA, 0xBC, 0x47, 0x05, 0x97, 0xF1, 0x37, 0x63, 0x9D, 0x62, 0x44, 0x62, + 0x21, 0x9B, 0x72, 0x89, 0x0E, 0xD2, 0xBA, 0xD3, 0xB8, 0xDD, 0x20, 0x78, 0x6E, 0xAC, 0x12, 0x20, + 0x23, 0xB9, 0x3E, 0xEE, 0x48, 0x1D, 0xA7, 0xEB, 0x4D, 0xBA, 0x5B, 0xEE, 0xC1, 0x66, 0x1D, 0xDF, + 0x73, 0x2B, 0x50, 0x94, 0xB3, 0xD5, 0x2B, 0x6D, 0xD2, 0xEB, 0x56, 0x9A, 0x84, 0x03, 0xAE, 0x34, + 0x0D, 0xF6, 0xCC, 0x1C, 0xEC, 0xCE, 0xDC, 0x71, 0xDD, 0x7B, 0x90, 0xB5, 0x7B, 0x1D, 0xB3, 0x6F, + 0x6E, 0x21, 0x6B, 0xD9, 0xF6, 0xC4, 0x35, 0x05, 0xB2, 0xC1, 0x72, 0x7E, 0xA3, 0x84, 0xBA, 0xF0, + 0xA2, 0x6D, 0xB7, 0xDD, 0xC9, 0xBD, 0x57, 0xD9, 0xFE, 0x45, 0x90, 0x90, 0x73, 0x15, 0x0A, 0x7B, + 0xFC, 0x89, 0x89, 0x9F, 0xAD, 0x75, 0x3B, 0x53, 0xFC, 0x3C, 0xDA, 0x9C, 0x50, 0x3F, 0xE6, 0x09, + 0xBB, 0x7E, 0x88, 0xDB, 0xA8, 0x4C, 0x14, 0x58, 0x0B, 0x34, 0x8F, 0x93, 0xED, 0x98, 0xCA, 0x08, + 0xB3, 0xB1, 0x77, 0x91, 0xF9, 0xEF, 0xA4, 0x28, 0x04, 0xA4, 0xC0, 0x42, 0xBE, 0xEA, 0xC5, 0xED, + 0xA0, 0x9A, 0x0F, 0x88, 0xC8, 0x5F, 0x74, 0x56, 0xB4, 0x06, 0xFB, 0x82, 0x68, 0xB5, 0xE6, 0x1F, + 0x31, 0x77, 0x19, 0xCD, 0x82, 0x90, 0x7D, 0x1E, 0x0C, 0x32, 0x7A, 0xF0, 0xD1, 0x58, 0xAF, 0xC2, + 0xD8, 0xF3, 0x8D, 0xC9, 0x1A, 0x7C, 0xCE, 0x5F, 0x6E, 0xE9, 0xFF, 0xD7, 0x2D, 0x0D, 0x8F, 0x1A, + 0x77, 0x7B, 0x32, 0x35, 0x7D, 0xB6, 0x65, 0x64, 0x6E, 0x67, 0xD2, 0xF3, 0xBD, 0x07, 0x09, 0x55, + 0x45, 0xC1, 0xBF, 0x44, 0xFB, 0xE7, 0x11, 0xAD, 0x63, 0x4D, 0x4C, 0x7F, 0x3B, 0x07, 0xB5, 0x26, + 0x1D, 0xBF, 0xD7, 0x7E, 0x98, 0x68, 0xA5, 0xB5, 0xFF, 0x25, 0xDA, 0x3F, 0xB9, 0x68, 0xED, 0x4E, + 0xDF, 0x9B, 0x4C, 0xB3, 0xC2, 0x65, 0x16, 0xC7, 0xC0, 0x91, 0x23, 0x75, 0x8B, 0xD5, 0x35, 0x7B, + 0xFB, 0x60, 0xDF, 0xA3, 0x74, 0xD9, 0x29, 0x40, 0xFE, 0x0D, 0x4B, 0x2E, 0x63, 0xDF, 0x2B, 0x8A, + 0x1D, 0xC1, 0xB2, 0xBC, 0x2A, 0x9E, 0x05, 0x57, 0xCC, 0x1F, 0x7E, 0x83, 0x9C, 0xDD, 0x67, 0x57, + 0xB8, 0x8D, 0x00, 0x9A, 0xA8, 0xB0, 0x92, 0xB0, 0x4C, 0x2C, 0x45, 0xB1, 0xC6, 0x02, 0x95, 0xC5, + 0x06, 0x73, 0x58, 0xEC, 0x38, 0x64, 0x75, 0x92, 0xB8, 0x47, 0xCD, 0x9F, 0x85, 0x10, 0x52, 0x45, + 0x05, 0xB5, 0xB7, 0x22, 0xDE, 0x6D, 0x2D, 0x87, 0x5B, 0x57, 0x53, 0xA8, 0x8A, 0x72, 0x01, 0x14, + 0xEE, 0xE6, 0x40, 0x95, 0x67, 0x99, 0xD5, 0x0A, 0xB0, 0x52, 0x1D, 0x96, 0x3B, 0xA5, 0xAD, 0x1D, + 0x9C, 0xAB, 0xBA, 0x0F, 0x4D, 0x1F, 0xD8, 0x05, 0x1F, 0xF3, 0x2C, 0xB4, 0x54, 0x27, 0x63, 0xBD, + 0x61, 0xA1, 0xE2, 0x9B, 0x95, 0xAC, 0xC1, 0xD6, 0x86, 0xBB, 0x7B, 0x0E, 0xD2, 0xF8, 0x25, 0x6B, + 0x32, 0xA1, 0xEF, 0x61, 0xC7, 0xDF, 0x66, 0x0C, 0x3F, 0x19, 0x1F, 0xB0, 0xA2, 0x2E, 0x69, 0x89, + 0xAD, 0x16, 0xCC, 0x94, 0x44, 0x64, 0x43, 0x7B, 0x95, 0xC4, 0xC6, 0xCF, 0xA1, 0x22, 0xF9, 0x81, + 0xEC, 0xAB, 0xD4, 0xA2, 0x33, 0xFC, 0x64, 0xE8, 0x55, 0x77, 0x02, 0x4C, 0x85, 0x5D, 0xD6, 0xBB, + 0xAD, 0xE2, 0x9D, 0x0C, 0x7B, 0xA5, 0x34, 0x6E, 0xB3, 0xCD, 0x96, 0x0F, 0x27, 0x65, 0x17, 0x9D, + 0x3F, 0x28, 0xED, 0xDB, 0xE6, 0x22, 0xF0, 0xD9, 0x97, 0x80, 0x57, 0x2C, 0xE4, 0xF6, 0xEF, 0x4B, + 0xE6, 0x07, 0x1E, 0xA9, 0x2F, 0xC1, 0x67, 0x4B, 0x8D, 0xEF, 0x76, 0x40, 0xE2, 0xDA, 0xCD, 0x96, + 0x8E, 0xCA, 0xBE, 0x76, 0x0F, 0x21, 0x65, 0x93, 0xD2, 0x69, 0xC2, 0x58, 0x44, 0x20, 0xD5, 0x85, + 0xF9, 0xF9, 0x1E, 0x5D, 0xB7, 0xD3, 0x3D, 0x38, 0x5F, 0xEC, 0xDF, 0xDD, 0x9E, 0xB4, 0xE4, 0xF6, + 0xE6, 0x09, 0x0F, 0x38, 0x5C, 0x5E, 0x9E, 0xFD, 0xEE, 0xBC, 0x20, 0x3C, 0x8E, 0x43, 0xB2, 0x02, + 0x0F, 0x7D, 0xD2, 0x92, 0xCD, 0x27, 0x2D, 0xB9, 0x15, 0x2A, 0x76, 0xC3, 0x4E, 0xFC, 0xE0, 0x92, + 0x4C, 0x43, 0x2F, 0x4D, 0x47, 0x54, 0xB8, 0x16, 0x0A, 0xB3, 0x71, 0xD7, 0x8C, 0x08, 0xC0, 0x23, + 0x8A, 0x90, 0xB1, 0x2D, 0x81, 0x2F, 0x4C, 0xF2, 0xB2, 0xC1, 0xB2, 0xA2, 0xA0, 0x64, 0x91, 0xB0, + 0xD9, 0x88, 0x2E, 0x38, 0x5F, 0xA5, 0x83, 0x56, 0x6B, 0xB3, 0xD9, 0x34, 0xE7, 0x01, 0x5F, 0xAC, + 0x27, 0xCD, 0x69, 0xBC, 0x6C, 0x85, 0xEB, 0xA9, 0x21, 0x1F, 0x5B, 0x02, 0x99, 0x16, 0x07, 0xC2, + 0x5A, 0x4E, 0xD3, 0xA4, 0x84, 0x83, 0x46, 0x33, 0x3E, 0xA2, 0x5F, 0x20, 0xD5, 0x8D, 0xBE, 0xC2, + 0x0A, 0xE9, 0xE5, 0x3C, 0x5F, 0x93, 0x2D, 0x01, 0xB0, 0x10, 0xB3, 0x7A, 0xB8, 0x0C, 0xD8, 0xE6, + 0xE7, 0xF8, 0x6A, 0x44, 0x31, 0x9D, 0xB6, 0x1C, 0x13, 0x7E, 0x6C, 0xD3, 0x84, 0x59, 0x73, 0x19, + 0x61, 0xB0, 0x42, 0x1F, 0x51, 0x71, 0x0B, 0x16, 0xC3, 0xEA, 0x6D, 0x53, 0xC7, 0x01, 0x1A, 0xB0, + 0xD2, 0x0B, 0x59, 0xDD, 0xD2, 0x89, 0x61, 0x69, 0x30, 0x7C, 0xE5, 0xF1, 0x05, 0xF1, 0x47, 0xF4, + 0x4D, 0x07, 0x41, 0x58, 0x5D, 0xF7, 0xC2, 0x71, 0x00, 0x62, 0xD7, 0x25, 0x46, 0x3B, 0x74, 0x7A, + 0x30, 0xAA, 0x6D, 0x87, 0x6D, 0xB8, 0x5C, 0xB8, 0x7D, 0xF8, 0x75, 0x49, 0x1F, 0x7A, 0x9C, 0x3E, + 0x36, 0xD9, 0xA1, 0xE5, 0xB8, 0xA4, 0x67, 0x5E, 0x74, 0x2C, 0x62, 0xB8, 0x3D, 0x62, 0x99, 0xD0, + 0x65, 0x99, 0xED, 0xD0, 0xE8, 0x99, 0x70, 0xE3, 0xB8, 0xA1, 0x03, 0x40, 0x2E, 0x6C, 0x18, 0xEA, + 0xBA, 0xC4, 0x81, 0xE9, 0x7D, 0x27, 0x84, 0xA1, 0x9D, 0x10, 0x60, 0x02, 0x90, 0xDE, 0x05, 0xF6, + 0x38, 0x04, 0x7E, 0xBB, 0xCE, 0x05, 0x4C, 0x71, 0x70, 0x51, 0x78, 0x70, 0x43, 0x43, 0x8D, 0x80, + 0x1B, 0x18, 0x7F, 0x01, 0x8F, 0x30, 0xB2, 0x8F, 0x0B, 0x0B, 0x20, 0x06, 0x02, 0x0E, 0xD5, 0x2A, + 0x17, 0xB8, 0xB6, 0x81, 0x38, 0x14, 0x08, 0x08, 0xC4, 0xAC, 0x10, 0xA1, 0x39, 0x17, 0xB8, 0xBA, + 0x81, 0x58, 0x28, 0xD4, 0x0D, 0x81, 0xBB, 0x21, 0x89, 0xB3, 0xC8, 0x05, 0xE2, 0x20, 0xD7, 0x45, + 0x74, 0x0D, 0x41, 0x3F, 0x3E, 0xB4, 0xC5, 0x18, 0x18, 0x82, 0x33, 0xEC, 0x0B, 0x44, 0x00, 0xE8, + 0x47, 0x28, 0x12, 0x88, 0x23, 0xD7, 0x31, 0x7A, 0xD6, 0x85, 0xD1, 0x31, 0x09, 0x62, 0x81, 0x18, + 0x20, 0x02, 0x3D, 0x94, 0x89, 0x8B, 0x78, 0x02, 0x40, 0x58, 0xDA, 0x45, 0x44, 0x7A, 0x04, 0x51, + 0xB7, 0x49, 0x27, 0x14, 0xEB, 0x02, 0xFD, 0x46, 0x87, 0xB8, 0x40, 0x67, 0x07, 0xD8, 0x0D, 0xF4, + 0xC3, 0xC2, 0x70, 0x07, 0x2C, 0x12, 0x9D, 0x21, 0x0C, 0xBC, 0xB0, 0x1C, 0x04, 0x2B, 0x67, 0x3A, + 0x44, 0x72, 0x16, 0x49, 0x76, 0xBB, 0x04, 0x08, 0x86, 0x95, 0xC4, 0x6A, 0x16, 0xCC, 0x84, 0x9E, + 0x10, 0xB1, 0x84, 0x95, 0x60, 0x3D, 0x89, 0x23, 0xF4, 0x86, 0x82, 0x02, 0x68, 0x46, 0x36, 0x23, + 0x4D, 0xDF, 0x84, 0xA0, 0x7B, 0xC0, 0xD0, 0x0B, 0xA3, 0xD7, 0x47, 0x4A, 0x05, 0xAB, 0x3B, 0x0E, + 0x87, 0xAF, 0x60, 0x48, 0xB3, 0xCD, 0x8B, 0xBB, 0xAC, 0x13, 0xAF, 0x70, 0x81, 0x0E, 0xD9, 0x6E, + 0x14, 0x77, 0xB2, 0xEB, 0x1B, 0xE8, 0x52, 0x0B, 0x95, 0x09, 0x2E, 0x73, 0xF8, 0x82, 0xF2, 0x8E, + 0xC9, 0x09, 0xA4, 0x36, 0x51, 0x6E, 0x1F, 0x59, 0x05, 0x47, 0xC7, 0xAF, 0x82, 0x64, 0xB9, 0x81, + 0x14, 0x08, 0x86, 0xC1, 0x00, 0x18, 0xED, 0xC1, 0x17, 0x8D, 0xE9, 0x8F, 0x1B, 0x94, 0xF1, 0xE1, + 0xE5, 0xCF, 0xEF, 0x4F, 0xFF, 0x64, 0x66, 0x65, 0xF6, 0xDD, 0x8B, 0x9E, 0x8D, 0x10, 0x3B, 0x66, + 0x13, 0xB5, 0xCF, 0x46, 0xD6, 0xBA, 0xC0, 0xFC, 0x76, 0x9F, 0x5B, 0x56, 0x07, 0xDB, 0x7A, 0xD8, + 0xD6, 0x77, 0xF1, 0xB6, 0x0F, 0x12, 0xE8, 0x89, 0x8B, 0x6B, 0xE7, 0x5D, 0xA8, 0x7A, 0xED, 0xAE, + 0x60, 0x78, 0x7E, 0x87, 0x8A, 0x2B, 0x3A, 0x8D, 0x4E, 0x4F, 0x4D, 0x34, 0x72, 0x10, 0x46, 0x19, + 0xB0, 0x91, 0xAD, 0x06, 0xE2, 0xEA, 0xE7, 0x28, 0xA8, 0x07, 0x3B, 0x1F, 0x21, 0x06, 0x88, 0x69, + 0x72, 0x96, 0x00, 0xD6, 0xCF, 0xE0, 0xF7, 0xE5, 0x92, 0x19, 0x40, 0x22, 0x90, 0xC8, 0xAE, 0x02, + 0x55, 0xD1, 0x05, 0xB8, 0xF7, 0xDB, 0x84, 0x67, 0x73, 0x4B, 0xF0, 0xD4, 0x12, 0x92, 0x0B, 0xB8, + 0xEA, 0xB7, 0x37, 0xBD, 0x5E, 0x0F, 0xFA, 0xFA, 0xC2, 0xC4, 0xD1, 0xCA, 0x2D, 0xD0, 0x57, 0x9B, + 0x0B, 0x04, 0x85, 0xE7, 0x68, 0x77, 0x51, 0x9F, 0x01, 0xA9, 0x3E, 0x7A, 0x08, 0xCB, 0x46, 0x7B, + 0x03, 0xDE, 0xD8, 0x30, 0x08, 0x7F, 0xF0, 0x49, 0xDE, 0xE0, 0x15, 0x7A, 0xE0, 0xF6, 0x02, 0x17, + 0x21, 0x36, 0x28, 0xA8, 0x05, 0x6C, 0x27, 0x56, 0x9F, 0xB8, 0x62, 0x39, 0xC0, 0xB9, 0x8B, 0xA4, + 0xC3, 0x08, 0xA3, 0x0B, 0xC0, 0x3A, 0xE8, 0xD0, 0x3A, 0x08, 0xB5, 0x07, 0x4E, 0xC4, 0x42, 0x9D, + 0xEF, 0x10, 0xE9, 0x6A, 0x4C, 0x94, 0x05, 0x5C, 0x01, 0xC5, 0x0B, 0x1B, 0x3D, 0x11, 0x18, 0x6A, + 0x17, 0x9C, 0x82, 0xC5, 0x71, 0x62, 0xCF, 0xE6, 0x7D, 0x29, 0x18, 0x0B, 0xA8, 0x43, 0xE7, 0xD1, + 0x43, 0xE2, 0x1C, 0x47, 0x30, 0x16, 0x17, 0x53, 0x0F, 0xB6, 0x2B, 0xFA, 0x45, 0xB7, 0x98, 0xD1, + 0x43, 0x8B, 0xE9, 0x9A, 0xF2, 0x0A, 0x10, 0xBB, 0xB0, 0xD0, 0x85, 0x05, 0x96, 0x0C, 0x2C, 0x23, + 0x6E, 0xC6, 0x57, 0x17, 0x7A, 0x2F, 0x8C, 0xBE, 0xF0, 0xC7, 0x88, 0x14, 0x50, 0xD2, 0xEB, 0x7F, + 0x7B, 0xE3, 0x82, 0x2B, 0xE8, 0xDA, 0x5D, 0xF0, 0x2A, 0xE8, 0x4D, 0x94, 0x57, 0x14, 0x5F, 0x21, + 0x51, 0x07, 0x57, 0x11, 0xC2, 0x17, 0xF3, 0x1D, 0x98, 0x8A, 0x92, 0x40, 0xB2, 0x2C, 0xF0, 0x24, + 0x48, 0x9A, 0x43, 0x1C, 0xA1, 0x27, 0x96, 0xC5, 0x1D, 0x14, 0x8A, 0xD5, 0x0D, 0x01, 0x16, 0xF8, + 0x13, 0x58, 0x14, 0xF9, 0x8F, 0x28, 0x22, 0xE2, 0x80, 0x45, 0x47, 0xDD, 0x0A, 0xEF, 0x89, 0x0E, + 0x14, 0x9C, 0x05, 0xA0, 0x03, 0x8B, 0x0A, 0x6C, 0x0D, 0x1B, 0x58, 0x6D, 0x72, 0xC3, 0xB1, 0x91, + 0x9F, 0x0F, 0x32, 0xFE, 0x53, 0xAC, 0x96, 0x66, 0x50, 0xF2, 0xEC, 0xB1, 0x7E, 0x22, 0xC2, 0xF5, + 0x88, 0x96, 0x32, 0xE2, 0x52, 0x15, 0x27, 0x32, 0x0D, 0x4A, 0x02, 0xB0, 0xAB, 0x57, 0x1F, 0xFE, + 0xF9, 0xF2, 0xDD, 0xD9, 0xE9, 0xDB, 0xDF, 0xA8, 0x9A, 0xDB, 0xC2, 0x78, 0xDC, 0x92, 0x27, 0x68, + 0x27, 0x2D, 0x88, 0xE1, 0x7B, 0x03, 0xB9, 0xAC, 0x03, 0xC7, 0x27, 0x0B, 0x5B, 0x40, 0x79, 0x73, + 0xF6, 0x0F, 0x9C, 0xBF, 0xB0, 0xE1, 0x27, 0xEB, 0xDA, 0x3F, 0x97, 0xA8, 0x94, 0x46, 0xAD, 0x7E, + 0xFA, 0xFA, 0xE5, 0xD9, 0x7F, 0x9F, 0x9D, 0xBF, 0x7C, 0x43, 0x77, 0x87, 0x66, 0xC7, 0x36, 0xE0, + 0xE6, 0xA0, 0x75, 0x41, 0x5E, 0x41, 0x51, 0x9B, 0x5E, 0xA7, 0x9C, 0x2D, 0x0F, 0xC0, 0x16, 0x69, + 0x1F, 0x00, 0x12, 0x75, 0x31, 0x11, 0x75, 0x31, 0xC5, 0x4A, 0x58, 0xAE, 0x25, 0x6A, 0x62, 0x59, + 0x9C, 0x51, 0x12, 0x79, 0x4B, 0xE8, 0x5C, 0x5E, 0x63, 0x63, 0xFA, 0xF1, 0x33, 0x25, 0xCB, 0x75, + 0xC8, 0x83, 0x15, 0xB2, 0x2C, 0xBB, 0xA3, 0xC0, 0x79, 0x09, 0xA9, 0x70, 0xA3, 0xA4, 0xB4, 0xEB, + 0x4A, 0xD5, 0x0A, 0xB2, 0xBE, 0x96, 0x6B, 0x54, 0x4A, 0x6E, 0x4A, 0xE2, 0x68, 0x1A, 0x06, 0xD3, + 0xAF, 0x23, 0x7A, 0xC6, 0x22, 0x1F, 0x97, 0xAA, 0x6B, 0x43, 0x70, 0x8D, 0x5E, 0xB8, 0x86, 0x79, + 0xEF, 0xC5, 0x58, 0x3A, 0x7E, 0x1A, 0x4D, 0xD2, 0xD5, 0x50, 0xFE, 0x9E, 0xAC, 0x92, 0x78, 0x9E, + 0xB0, 0x34, 0xCD, 0xE4, 0x77, 0x19, 0xA4, 0xC1, 0x24, 0x08, 0x03, 0x7E, 0x3D, 0x00, 0xC6, 0xF9, + 0x2C, 0xCA, 0x50, 0x5F, 0x25, 0x73, 0xB9, 0xA4, 0xB8, 0x81, 0x54, 0x4E, 0xE4, 0x53, 0x42, 0x7D, + 0x14, 0x08, 0x48, 0xC3, 0x12, 0xF9, 0xDD, 0x23, 0xBF, 0x43, 0xAC, 0x53, 0x72, 0x97, 0x39, 0x59, + 0xE6, 0xDD, 0x45, 0x9A, 0xF6, 0x10, 0x56, 0x54, 0xE8, 0xFE, 0x25, 0x5E, 0x42, 0xB5, 0xEF, 0xD7, + 0x6B, 0x61, 0x90, 0xF2, 0x9A, 0x5E, 0xF3, 0xC2, 0xB0, 0x56, 0x62, 0xC3, 0x3B, 0x36, 0x03, 0x6C, + 0x17, 0xB4, 0x50, 0xDD, 0xF2, 0xAA, 0x88, 0x67, 0x0E, 0xED, 0x97, 0x84, 0x41, 0xA8, 0xF0, 0x83, + 0xA4, 0xAE, 0xD1, 0xAD, 0xD8, 0x56, 0x89, 0x46, 0xAE, 0x59, 0x04, 0x23, 0xBC, 0xAF, 0xC4, 0x22, + 0x17, 0xFF, 0xC1, 0xF8, 0x04, 0xF4, 0x80, 0x40, 0x5B, 0x9B, 0x92, 0x6B, 0xE4, 0x1D, 0xCD, 0x66, + 0x3B, 0xA5, 0xD9, 0x36, 0xDC, 0x27, 0x30, 0xC8, 0x86, 0xCB, 0xB5, 0xB8, 0x80, 0x14, 0xC3, 0x11, + 0x55, 0xB5, 0x3B, 0x6D, 0x15, 0x70, 0x70, 0xE8, 0xB5, 0x00, 0x97, 0xC5, 0xC4, 0x76, 0x29, 0x24, + 0xB6, 0xEF, 0x84, 0x83, 0xE6, 0x89, 0x70, 0x2C, 0x89, 0x90, 0x0D, 0x97, 0x7C, 0xC7, 0x02, 0x5A, + 0x7B, 0xEA, 0x71, 0xA3, 0x20, 0xF6, 0x40, 0xD8, 0x0A, 0x88, 0xD8, 0xFC, 0xA0, 0xE3, 0x06, 0x30, + 0x10, 0x60, 0x28, 0xAF, 0xA1, 0x4C, 0x64, 0x8B, 0xA7, 0x2A, 0xE5, 0x46, 0xAE, 0x0A, 0xDD, 0x01, + 0x4F, 0x93, 0x73, 0x32, 0x88, 0x66, 0x71, 0xA6, 0x8D, 0xE5, 0xD9, 0x15, 0x87, 0x20, 0x13, 0x78, + 0x35, 0x43, 0x3E, 0x54, 0x4E, 0xC2, 0x69, 0xA6, 0xB8, 0xC5, 0x76, 0x0D, 0x6A, 0x95, 0x2C, 0x0B, + 0x84, 0x56, 0x2D, 0xCA, 0xF2, 0x3D, 0x07, 0xB5, 0x01, 0xD8, 0x0B, 0x6C, 0x1F, 0xFF, 0x06, 0x8A, + 0x9D, 0x3F, 0x9C, 0x01, 0xD9, 0xD9, 0x43, 0x45, 0x23, 0xB6, 0xDA, 0x14, 0x45, 0xA2, 0x55, 0x61, + 0xAA, 0x16, 0x43, 0x8D, 0xCE, 0x0D, 0xFF, 0x0B, 0x2A, 0x9F, 0x18, 0x27, 0x6B, 0x93, 0xE3, 0x0E, + 0x4E, 0x55, 0x88, 0xD2, 0xC0, 0x52, 0xEE, 0xF1, 0x75, 0x4A, 0x73, 0x8E, 0xEE, 0xFC, 0xDE, 0xE1, + 0xE2, 0x3E, 0xBC, 0xFF, 0xFD, 0xC5, 0xF3, 0xF3, 0x97, 0xC7, 0x1D, 0x9C, 0xCA, 0xE3, 0xC8, 0xFB, + 0x95, 0x0F, 0x2A, 0x7E, 0x87, 0x7F, 0xAB, 0x18, 0xE9, 0x41, 0x77, 0xB7, 0x39, 0xE8, 0xEC, 0x4A, + 0x39, 0xE2, 0x83, 0x1D, 0x1C, 0x3C, 0x94, 0xEC, 0x5B, 0xFA, 0xB0, 0x5D, 0xCF, 0x86, 0x44, 0x94, + 0x97, 0x79, 0x88, 0x5B, 0x9B, 0x6D, 0x72, 0xC7, 0x86, 0xB7, 0xFB, 0x5D, 0x5B, 0x0E, 0x59, 0xC4, + 0x46, 0x1C, 0xBE, 0x4C, 0xE7, 0xF4, 0x30, 0xF8, 0xF1, 0x3B, 0x06, 0x72, 0x4C, 0x38, 0x70, 0x5B, + 0x27, 0xE0, 0xDC, 0xBD, 0x14, 0xCA, 0x50, 0x2F, 0xE0, 0x4D, 0xF8, 0x2F, 0x0B, 0x9F, 0x39, 0xA8, + 0x29, 0x94, 0xF9, 0x28, 0xFE, 0x71, 0x1E, 0x58, 0x0F, 0x04, 0xC6, 0x42, 0x4C, 0x38, 0x2D, 0x8C, + 0xE7, 0xC0, 0x4E, 0x28, 0x87, 0x73, 0x93, 0x12, 0xF5, 0x74, 0x55, 0xF0, 0x95, 0x12, 0x7B, 0x5F, + 0x97, 0xDC, 0x59, 0x81, 0x9E, 0x85, 0x33, 0x3E, 0x05, 0xD4, 0x79, 0x30, 0x0B, 0xA6, 0x62, 0x9F, + 0x14, 0xE2, 0xAB, 0xB3, 0x47, 0xE7, 0x8A, 0x1D, 0x0F, 0x2A, 0x69, 0x18, 0x57, 0x12, 0x06, 0xD9, + 0x8D, 0x9E, 0x81, 0x92, 0x3C, 0xDD, 0x1E, 0xBF, 0x4F, 0xC1, 0x38, 0x15, 0x79, 0x5B, 0x61, 0xAE, + 0x7C, 0x2C, 0x9E, 0xA9, 0x80, 0x9C, 0x2E, 0x88, 0x44, 0xF9, 0x57, 0x92, 0x0B, 0xDC, 0x39, 0x2A, + 0x58, 0xB5, 0x48, 0xEE, 0x8F, 0xC4, 0xEF, 0xD0, 0xB7, 0x01, 0x3F, 0xF1, 0x00, 0x44, 0x56, 0x6A, + 0x8A, 0x42, 0x66, 0x75, 0x1C, 0x99, 0xC9, 0xBE, 0x34, 0xA4, 0xBC, 0x07, 0xB4, 0x95, 0x2C, 0x64, + 0x9A, 0x7E, 0xC8, 0x1E, 0x8A, 0xB8, 0xF6, 0xFA, 0x1D, 0xC6, 0x20, 0xA5, 0xEF, 0x67, 0xEB, 0xC9, + 0x32, 0xE0, 0x7B, 0x3D, 0x44, 0x3A, 0x05, 0xB7, 0xC8, 0xC7, 0x97, 0x5E, 0x42, 0x36, 0xE9, 0x97, + 0x34, 0x5E, 0x27, 0x53, 0xA6, 0x4F, 0xD7, 0x09, 0xEE, 0x5A, 0xA3, 0xF7, 0x1D, 0xD1, 0x16, 0xD5, + 0x01, 0xE9, 0x05, 0x0A, 0x5A, 0x8A, 0x79, 0xF4, 0xC4, 0xD2, 0x37, 0x6C, 0xB2, 0x16, 0x46, 0x84, + 0x0F, 0xB3, 0x3C, 0xEF, 0x51, 0x5D, 0x69, 0x3C, 0xFD, 0xCA, 0xF8, 0x97, 0x55, 0x9C, 0xF0, 0x91, + 0xA9, 0x7B, 0xE9, 0x75, 0x34, 0xFD, 0x02, 0xCD, 0x50, 0xC5, 0x2D, 0xD7, 0x51, 0x09, 0x0A, 0xEA, + 0xE2, 0x17, 0xE4, 0x13, 0xD5, 0x41, 0x37, 0xBF, 0xC4, 0xB3, 0x59, 0x15, 0x80, 0xB0, 0x08, 0xE6, + 0x43, 0xE3, 0x70, 0xB6, 0x8E, 0xC4, 0xC6, 0x3C, 0x98, 0xE1, 0xE5, 0xC4, 0x83, 0x00, 0x7B, 0x83, + 0x28, 0x03, 0x6D, 0x25, 0x67, 0x43, 0x75, 0x3E, 0x2A, 0xA1, 0xDE, 0x4C, 0x57, 0x60, 0x61, 0x75, + 0x20, 0x40, 0xD3, 0x23, 0x41, 0x47, 0x30, 0x02, 0x40, 0x71, 0x52, 0x67, 0x0D, 0x9C, 0xE7, 0x2B, + 0x36, 0xD6, 0x64, 0x88, 0xAE, 0x91, 0x9C, 0x7D, 0x9F, 0x68, 0x99, 0x03, 0xB5, 0x56, 0x6D, 0x48, + 0x0E, 0x27, 0x0A, 0x9F, 0xE8, 0xB8, 0x25, 0x0C, 0x90, 0x0E, 0x83, 0x13, 0xDE, 0x0C, 0x59, 0x34, + 0xE7, 0x0B, 0xC3, 0x1A, 0x6A, 0x07, 0x56, 0x39, 0xB0, 0x08, 0x6D, 0xD4, 0xA3, 0xC6, 0x88, 0x7F, + 0x0C, 0x3E, 0x37, 0x10, 0xE3, 0x06, 0xBD, 0x6B, 0x51, 0xDA, 0x90, 0x83, 0x73, 0x57, 0xA3, 0xB0, + 0xD0, 0x83, 0x46, 0x63, 0x98, 0x30, 0xBE, 0x4E, 0x22, 0x22, 0x50, 0x28, 0xFB, 0x05, 0x7A, 0x9B, + 0x33, 0x12, 0xD4, 0x3C, 0x5D, 0x7C, 0x09, 0x40, 0x7F, 0x81, 0x99, 0x72, 0x3C, 0xCD, 0x92, 0x85, + 0x5A, 0xDB, 0xAE, 0x41, 0x90, 0xAF, 0x59, 0x70, 0x81, 0xB4, 0xA0, 0xD6, 0xA9, 0x61, 0x5A, 0x80, + 0x17, 0x19, 0xD5, 0x6A, 0x76, 0xBB, 0x96, 0xE5, 0x0E, 0xB5, 0x6E, 0x4D, 0xA9, 0x78, 0x0D, 0xC3, + 0xFD, 0x20, 0x61, 0xFE, 0xB0, 0x46, 0x5A, 0x80, 0xC8, 0x2E, 0xB8, 0xFD, 0x00, 0xEC, 0x2A, 0x00, + 0x91, 0x2E, 0xEC, 0x80, 0x70, 0x4C, 0x09, 0xA2, 0x77, 0x00, 0xA3, 0x4E, 0xB7, 0x00, 0x08, 0xDE, + 0xF8, 0x6E, 0x9C, 0xEC, 0x2A, 0x40, 0xCB, 0x94, 0x10, 0xF1, 0xAA, 0x40, 0xF6, 0xCA, 0x20, 0xDD, + 0x7B, 0x43, 0xB4, 0xFB, 0x7B, 0x21, 0x38, 0xF7, 0xA1, 0xD2, 0x95, 0x20, 0x5C, 0x47, 0x22, 0xD5, + 0x95, 0x38, 0x75, 0x73, 0x80, 0x25, 0x78, 0x9D, 0x7B, 0x01, 0xEC, 0xFC, 0x68, 0x80, 0xBD, 0x1F, + 0x01, 0x50, 0xA6, 0x80, 0x08, 0xB6, 0xC8, 0x8A, 0x6B, 0xB6, 0x5B, 0x52, 0x09, 0xB8, 0xCF, 0xB2, + 0xE2, 0x9A, 0xD8, 0xA1, 0xB1, 0x71, 0xBB, 0xA0, 0x57, 0x1B, 0xFF, 0x48, 0x15, 0xFD, 0xA3, 0xFA, + 0xF9, 0x63, 0x95, 0xF3, 0x07, 0x6B, 0xE6, 0x1F, 0x55, 0xCB, 0x1F, 0xAB, 0x93, 0x3F, 0x56, 0x21, + 0xFF, 0x25, 0xDA, 0x58, 0xB8, 0x46, 0x3C, 0x4B, 0xD9, 0xF6, 0x8C, 0xF7, 0x55, 0x54, 0xDB, 0x85, + 0x7F, 0xB5, 0x7C, 0x5F, 0xB0, 0xF6, 0xA6, 0xAB, 0x3B, 0xE4, 0xB5, 0xAD, 0xF7, 0xC8, 0xEB, 0xAE, + 0x6E, 0x39, 0xE2, 0xD7, 0x24, 0xAF, 0x2D, 0x75, 0xE9, 0xE9, 0x96, 0x25, 0x2F, 0x6D, 0xD9, 0xD8, + 0x81, 0x8B, 0x29, 0x2E, 0x7D, 0xDD, 0xEA, 0x8A, 0xDF, 0xBE, 0x68, 0xB2, 0x61, 0xB8, 0xAD, 0x2E, + 0xB6, 0x6E, 0xF5, 0xC4, 0xA5, 0x27, 0xDA, 0x3A, 0x08, 0xB5, 0x43, 0xBE, 0x21, 0x81, 0x49, 0xFC, + 0x15, 0x28, 0x14, 0xBB, 0x30, 0x35, 0x59, 0x7F, 0xD5, 0x04, 0xA5, 0x7B, 0x09, 0x95, 0x09, 0xF8, + 0x17, 0xAC, 0x58, 0x99, 0x76, 0x53, 0x8A, 0x47, 0x8D, 0x11, 0xC3, 0x30, 0xA4, 0x97, 0x23, 0x10, + 0x15, 0x25, 0x8A, 0x4E, 0x21, 0x02, 0x51, 0xAD, 0x80, 0x01, 0x51, 0x1D, 0x0F, 0xB9, 0xCF, 0xA0, + 0xBE, 0x8A, 0xE6, 0x69, 0x9D, 0xE9, 0x3C, 0x63, 0x5A, 0x9D, 0x8D, 0x58, 0x93, 0xC7, 0xAF, 0xE3, + 0x0D, 0x4B, 0x7E, 0x81, 0x84, 0xB6, 0xAE, 0x69, 0x27, 0x75, 0x3E, 0xE2, 0x5B, 0x6D, 0xCF, 0x0C, + 0x6B, 0xC0, 0x4F, 0xD8, 0x33, 0x6B, 0x60, 0x16, 0x50, 0xF1, 0x40, 0xC9, 0xE3, 0xD3, 0x85, 0xC8, + 0x2D, 0x44, 0x81, 0x83, 0x18, 0x62, 0xCC, 0xE7, 0x98, 0x2E, 0x60, 0xFA, 0x30, 0x0C, 0x66, 0x00, + 0x8D, 0x96, 0xB7, 0x27, 0xCE, 0xC4, 0xC8, 0x01, 0xA1, 0x0D, 0xD6, 0x94, 0xB3, 0x74, 0xDE, 0xA8, + 0x0E, 0xF9, 0x5E, 0x7E, 0x38, 0x8F, 0xB9, 0x17, 0x12, 0x79, 0x2C, 0x2F, 0x26, 0x71, 0x6C, 0x38, + 0x3E, 0x07, 0x32, 0x53, 0xBF, 0x3C, 0x65, 0x0D, 0xCF, 0xC7, 0x67, 0xBC, 0x9D, 0x4E, 0xD7, 0x2B, + 0xF9, 0x42, 0x34, 0xA1, 0x62, 0xE8, 0xC9, 0x92, 0x41, 0x72, 0x47, 0x96, 0x41, 0x04, 0x4A, 0x53, + 0x13, 0x25, 0x84, 0xF4, 0x0B, 0x0B, 0xD0, 0xAA, 0x51, 0xAD, 0x0F, 0x77, 0x32, 0x75, 0xAB, 0xE1, + 0x0A, 0x71, 0x3E, 0x1F, 0xB2, 0x02, 0x10, 0xA2, 0x98, 0xAC, 0x0A, 0xE1, 0xED, 0xFE, 0x9F, 0xA8, + 0xEE, 0xC7, 0xD3, 0xF5, 0x12, 0xE4, 0xD8, 0x9C, 0x33, 0xFE, 0x32, 0x64, 0x78, 0xFB, 0xF3, 0xF5, + 0x29, 0xC8, 0x4F, 0x15, 0x8A, 0x5A, 0x33, 0x88, 0x22, 0x96, 0xFC, 0x7A, 0xFE, 0xE6, 0xF5, 0x88, + 0xEB, 0x82, 0x9D, 0x20, 0xEA, 0x27, 0xE5, 0xD4, 0x49, 0x72, 0x3A, 0xA8, 0x64, 0x53, 0x90, 0xC5, + 0xF0, 0x53, 0x3C, 0xEA, 0x7E, 0x3B, 0xC3, 0x9C, 0x4A, 0xAF, 0xF4, 0xC9, 0xA4, 0xC7, 0xD6, 0x86, + 0x82, 0x3A, 0x9E, 0x64, 0x96, 0x56, 0x7E, 0x75, 0xED, 0x48, 0xEA, 0x53, 0xC9, 0xDA, 0x60, 0x08, + 0xAB, 0x9B, 0x90, 0xC6, 0x58, 0xF7, 0xC8, 0x82, 0x30, 0xC1, 0x82, 0x54, 0xA8, 0x64, 0xB1, 0x45, + 0x46, 0x04, 0xAA, 0x19, 0x62, 0x9E, 0x0D, 0xFE, 0xA1, 0x36, 0x86, 0xCA, 0x15, 0x2B, 0xA9, 0xAC, + 0x50, 0xA2, 0xB7, 0xAC, 0x29, 0x74, 0xAB, 0x09, 0xF8, 0xF1, 0x7A, 0xA6, 0x77, 0x65, 0xF5, 0xDD, + 0xD1, 0xEC, 0x26, 0x96, 0x80, 0x3A, 0x17, 0x17, 0xED, 0x56, 0x43, 0xAE, 0x8D, 0xCA, 0x2C, 0x7A, + 0xFA, 0xB4, 0x0E, 0x7A, 0x69, 0x6A, 0x22, 0xC7, 0x44, 0x06, 0xC6, 0x23, 0x73, 0x18, 0x9F, 0x64, + 0x0B, 0x49, 0x26, 0x0D, 0xE3, 0x46, 0x43, 0xA3, 0x86, 0x05, 0x0C, 0x97, 0x80, 0xEB, 0xAA, 0xFF, + 0x63, 0xFC, 0xB9, 0x89, 0x7B, 0x29, 0x1A, 0x80, 0x11, 0x5C, 0x3C, 0x7F, 0x37, 0x56, 0xDA, 0x22, + 0xEA, 0x48, 0xF0, 0x3F, 0x65, 0xA7, 0x53, 0xF2, 0x45, 0x7B, 0xFC, 0x0F, 0x19, 0x7F, 0xE2, 0x85, + 0x0B, 0xB2, 0x74, 0x1B, 0x5C, 0x88, 0x6E, 0x5B, 0xE8, 0x88, 0x6C, 0xBC, 0xEF, 0xC8, 0x4B, 0x57, + 0xB4, 0x59, 0xE8, 0x3C, 0x5E, 0x5B, 0xB6, 0xFA, 0xB5, 0x08, 0x0E, 0xB3, 0xEE, 0xE1, 0x4E, 0xF0, + 0x15, 0x15, 0x72, 0x65, 0xC9, 0x20, 0x7C, 0x8D, 0xD7, 0x1A, 0xB9, 0xB2, 0xE1, 0x02, 0x6E, 0xF7, + 0xDA, 0x16, 0x01, 0x70, 0x0B, 0x82, 0x7C, 0x34, 0x14, 0xF2, 0x56, 0xAD, 0x95, 0x91, 0xA8, 0xF6, + 0x84, 0x44, 0x02, 0x0B, 0x0D, 0xB5, 0x93, 0xF3, 0x17, 0x2A, 0x71, 0xFE, 0xA4, 0x32, 0xE7, 0x4F, + 0x99, 0xFB, 0xA6, 0xF9, 0x5B, 0x00, 0xAB, 0xAB, 0x21, 0xC5, 0x63, 0x24, 0x79, 0x6E, 0x54, 0x03, + 0x53, 0x10, 0xBE, 0xAB, 0xC4, 0x50, 0x14, 0x55, 0xA3, 0x96, 0x9F, 0x0D, 0xC9, 0xA3, 0xA1, 0x4A, + 0xB5, 0x55, 0xDA, 0xAF, 0xAE, 0xE1, 0xCA, 0x5B, 0x93, 0x75, 0x2A, 0x5E, 0xEF, 0x68, 0xE2, 0x9F, + 0xCD, 0x34, 0xE7, 0xDF, 0x40, 0x6A, 0x5B, 0x03, 0x9E, 0x3E, 0x2D, 0x8D, 0xD8, 0xED, 0xFE, 0xFE, + 0x1D, 0x75, 0xC2, 0xD2, 0x14, 0x95, 0xB2, 0xF2, 0xC2, 0x6D, 0xF0, 0xF3, 0x17, 0x63, 0xA0, 0x51, + 0x52, 0xBB, 0xA5, 0x01, 0x6A, 0xAC, 0x1C, 0x91, 0x09, 0xDA, 0xFC, 0xA9, 0x56, 0xC6, 0xFB, 0x93, + 0xDA, 0x58, 0xFC, 0x44, 0x4B, 0x56, 0xF5, 0x02, 0xDC, 0x39, 0x67, 0x75, 0xE1, 0x34, 0xAA, 0x2C, + 0xA0, 0x35, 0x0D, 0x4B, 0x06, 0x84, 0x5C, 0xCE, 0xFB, 0x2B, 0x58, 0xE1, 0x7A, 0xD2, 0x3C, 0xF0, + 0x07, 0xB5, 0xAF, 0xD0, 0xE4, 0x10, 0x34, 0x39, 0xDC, 0xD6, 0xE4, 0x50, 0x69, 0xF2, 0x68, 0x5B, + 0x93, 0xC3, 0x5D, 0x4D, 0x7E, 0x94, 0x0E, 0x97, 0x34, 0xB8, 0x2F, 0xE3, 0x64, 0x1F, 0x23, 0x1E, + 0x44, 0x4B, 0x08, 0x8C, 0xEA, 0xA7, 0x8D, 0xB1, 0xCF, 0x45, 0x8D, 0x75, 0x51, 0xC7, 0xDB, 0x42, + 0xD1, 0x6D, 0x31, 0x14, 0x2F, 0x18, 0x31, 0x51, 0xED, 0x1D, 0x31, 0xBF, 0x2D, 0x7E, 0x6D, 0xA9, + 0xF5, 0xD0, 0x7F, 0xBF, 0x80, 0x59, 0xA8, 0x25, 0x45, 0x81, 0x54, 0x0B, 0x3A, 0x92, 0x2B, 0x50, + 0x9E, 0x5E, 0xEC, 0xBE, 0x9A, 0x54, 0x76, 0x7D, 0xA5, 0x98, 0x5B, 0x12, 0x54, 0x58, 0x08, 0x6A, + 0x98, 0x49, 0x6A, 0xAB, 0xAF, 0xA2, 0x16, 0xB9, 0xDD, 0x3C, 0x56, 0x4B, 0x0E, 0xAF, 0xFF, 0x28, + 0x45, 0x39, 0x18, 0x74, 0xF0, 0xE4, 0xA5, 0x1C, 0x71, 0xA2, 0x67, 0x14, 0xCF, 0x49, 0x48, 0xC5, + 0xAC, 0x48, 0x90, 0x42, 0x30, 0x4C, 0xD3, 0xF2, 0xEE, 0x97, 0x3C, 0xAA, 0x20, 0x01, 0xA7, 0x03, + 0x9A, 0xD9, 0x38, 0x94, 0xE2, 0x8A, 0x32, 0x52, 0xDB, 0xDA, 0xFC, 0xA8, 0x8D, 0xFF, 0x11, 0x13, + 0x1E, 0x13, 0xF9, 0xE6, 0x47, 0x50, 0x1C, 0x3D, 0x79, 0xE3, 0x23, 0x21, 0xB1, 0xD8, 0x7A, 0xAD, + 0x46, 0xC5, 0x83, 0x13, 0xC4, 0x8E, 0x74, 0x85, 0x1E, 0xB5, 0x1F, 0x51, 0xA4, 0x2E, 0xCA, 0x0A, + 0x31, 0xA1, 0x8A, 0xA3, 0x59, 0x90, 0x2C, 0xEB, 0xF4, 0x17, 0x79, 0x43, 0x7C, 0xEC, 0xC2, 0x31, + 0xF1, 0x0C, 0x55, 0x4D, 0x66, 0x10, 0x60, 0x26, 0x95, 0x34, 0x4B, 0x0C, 0x62, 0x54, 0x67, 0x3B, + 0x30, 0xB3, 0x3C, 0xED, 0x18, 0x58, 0x18, 0x03, 0xEA, 0x15, 0x27, 0xD7, 0x47, 0x60, 0xC3, 0x98, + 0x2A, 0xF8, 0xD2, 0xC1, 0x85, 0xDA, 0x57, 0x59, 0x25, 0x10, 0xFC, 0x78, 0x9D, 0xBE, 0xC8, 0xC0, + 0x89, 0xFD, 0x4F, 0xC8, 0x14, 0x40, 0xD6, 0xD1, 0x3A, 0x0C, 0xC1, 0xD1, 0x6D, 0x81, 0x9E, 0x66, + 0x30, 0x00, 0x74, 0x13, 0xFC, 0xC1, 0x12, 0x52, 0xBB, 0x62, 0x85, 0xF2, 0x50, 0x11, 0x63, 0x71, + 0x99, 0x68, 0x14, 0xB1, 0x0D, 0xF9, 0xAF, 0x37, 0xAF, 0x7F, 0xE5, 0x7C, 0xF5, 0x8E, 0x5D, 0xAC, + 0x21, 0xD5, 0xD3, 0x83, 0x11, 0x6D, 0x09, 0x9D, 0x7C, 0x26, 0x5F, 0xC5, 0x1C, 0x01, 0x19, 0xF7, + 0x57, 0x2F, 0xE4, 0x49, 0x04, 0x18, 0x83, 0x26, 0x35, 0x9B, 0x4D, 0xDC, 0x0C, 0x81, 0xC4, 0x0C, + 0xC1, 0xC9, 0xED, 0xDB, 0x06, 0x8B, 0xA6, 0xB1, 0xCF, 0xDE, 0xBF, 0x3B, 0xAD, 0x73, 0x4D, 0x76, + 0xCA, 0x5D, 0xAE, 0x52, 0x47, 0x39, 0x0F, 0xD2, 0xA3, 0x66, 0x1C, 0x01, 0x61, 0xFE, 0x35, 0x26, + 0x4F, 0x6C, 0x0A, 0x89, 0xCC, 0x9C, 0x8D, 0xF2, 0x6C, 0x41, 0xBB, 0x71, 0x47, 0xA3, 0xA8, 0x29, + 0x06, 0x60, 0xEE, 0x09, 0x3C, 0xA9, 0xDB, 0xA6, 0x89, 0x6D, 0x32, 0xD9, 0x7A, 0xB6, 0x27, 0x8F, + 0xFD, 0xCF, 0xB3, 0xB7, 0xBF, 0x41, 0xA0, 0x4A, 0x20, 0xF9, 0xC5, 0xA9, 0xE9, 0x2A, 0x8E, 0x52, + 0x76, 0xCE, 0xAE, 0xB8, 0xA6, 0x0D, 0x5C, 0xD3, 0x2A, 0x4D, 0x7E, 0xF7, 0xBA, 0xAE, 0x0D, 0xEA, + 0x20, 0xEE, 0x34, 0x0E, 0x59, 0x33, 0x8C, 0xE7, 0xF5, 0xAC, 0x4B, 0xD3, 0x5F, 0x7D, 0x78, 0x99, + 0x24, 0xE0, 0x9D, 0x35, 0xE0, 0x32, 0x62, 0xB9, 0x62, 0x51, 0x9D, 0xFE, 0xE3, 0xE5, 0x39, 0x90, + 0xAC, 0x43, 0x0E, 0x02, 0x4D, 0x29, 0xB0, 0xBC, 0xBE, 0x25, 0x02, 0xB9, 0x13, 0xAE, 0x64, 0x7C, + 0xD4, 0x2A, 0xB2, 0xCD, 0x79, 0x4D, 0x3A, 0x08, 0x4C, 0xB6, 0x4D, 0x8C, 0x6E, 0xD2, 0xF7, 0x6B, + 0x37, 0x07, 0x27, 0x57, 0x8F, 0x18, 0xB5, 0x66, 0xE5, 0x34, 0x31, 0x13, 0xCB, 0x61, 0xFB, 0x4A, + 0xE6, 0x30, 0x47, 0x38, 0xD2, 0x66, 0xB1, 0x4B, 0xAE, 0x76, 0xCC, 0x43, 0x46, 0x87, 0xB2, 0x04, + 0x40, 0xBD, 0x79, 0x15, 0x27, 0xCB, 0x17, 0x1E, 0xF7, 0x86, 0xBC, 0xE9, 0xAD, 0x56, 0x48, 0xAC, + 0xB4, 0xCE, 0x72, 0x66, 0x5A, 0x44, 0xB0, 0x08, 0x22, 0x58, 0x74, 0x92, 0xE1, 0x3F, 0x8C, 0x20, + 0x76, 0xA9, 0x24, 0x97, 0x7D, 0x8C, 0x3E, 0xEB, 0x71, 0x39, 0x91, 0x6B, 0x04, 0xCA, 0x19, 0x9E, + 0xD1, 0x02, 0x78, 0xAC, 0x07, 0x32, 0xAA, 0xE9, 0xC5, 0x7A, 0xC5, 0x91, 0x05, 0x70, 0x7D, 0x17, + 0x80, 0x76, 0x2B, 0x63, 0xE7, 0xAE, 0x96, 0x0F, 0x43, 0x25, 0xB1, 0xDF, 0xDF, 0x9E, 0x9D, 0x63, + 0xFE, 0x2D, 0xE0, 0x50, 0x21, 0xB9, 0xB0, 0x29, 0x59, 0xD8, 0x84, 0x30, 0xF2, 0xF2, 0x12, 0x20, + 0xBE, 0x06, 0xFF, 0xC4, 0x40, 0xC1, 0x91, 0x3B, 0xF2, 0x7C, 0x81, 0xEA, 0x45, 0xC2, 0xAA, 0xDD, + 0x80, 0x68, 0x32, 0xBA, 0xC0, 0xC4, 0x56, 0x6B, 0xB1, 0xD5, 0x97, 0x15, 0x4B, 0xA8, 0x37, 0x9E, + 0xCF, 0xFC, 0x96, 0x2A, 0x6E, 0xFE, 0x03, 0xEA, 0x8C, 0xC3, 0xE6, 0x24, 0xD9, 0x2F, 0x45, 0x76, + 0xC4, 0x0B, 0xDE, 0x25, 0x63, 0x82, 0x42, 0x6E, 0x60, 0xA1, 0xF7, 0x0A, 0x5F, 0x92, 0xAD, 0x9B, + 0x1A, 0x56, 0x26, 0xB7, 0xB7, 0x3A, 0x66, 0x42, 0x40, 0x7A, 0x84, 0xE3, 0xCA, 0x66, 0x24, 0x8C, + 0x66, 0x14, 0x66, 0x8A, 0x5F, 0x7F, 0xDC, 0xCA, 0x8F, 0xD0, 0x2B, 0x75, 0xFC, 0x72, 0x3C, 0x42, + 0x14, 0xB6, 0xA0, 0x96, 0x83, 0xF1, 0x47, 0xED, 0x3A, 0xDC, 0xB2, 0x6B, 0x6D, 0xE0, 0x85, 0x0C, + 0xCA, 0x0C, 0xFA, 0x3C, 0x22, 0x0C, 0xCD, 0x96, 0x60, 0xCD, 0x06, 0xEA, 0xE2, 0x3F, 0x81, 0x52, + 0x1A, 0x38, 0x22, 0x6C, 0x95, 0x6B, 0xB7, 0x85, 0xB5, 0xFE, 0x1A, 0xF8, 0xEC, 0x79, 0x18, 0xA2, + 0x7C, 0xF3, 0xBD, 0x71, 0x73, 0x77, 0x6F, 0xFC, 0xE9, 0xD3, 0x7C, 0xDB, 0xBE, 0x39, 0x0D, 0x63, + 0x2C, 0xA8, 0x0B, 0x5A, 0xC4, 0x4B, 0x8F, 0xA3, 0xEA, 0x63, 0x83, 0xD6, 0x01, 0xF7, 0xA9, 0xF4, + 0x92, 0xCC, 0xD7, 0x8E, 0x90, 0xBE, 0xED, 0x5B, 0xD9, 0xE1, 0xA1, 0xA5, 0xF7, 0x24, 0x32, 0x2E, + 0xAB, 0x57, 0x42, 0x47, 0x14, 0xDF, 0x09, 0x3D, 0xB2, 0x4A, 0x7E, 0xFE, 0xB8, 0x7F, 0x62, 0xC1, + 0x91, 0xDC, 0xE3, 0xDD, 0x64, 0xBC, 0xA1, 0xAF, 0x3C, 0x60, 0xBF, 0x8F, 0x61, 0xBF, 0x38, 0x5B, + 0xC0, 0xD7, 0x37, 0x21, 0x6B, 0x7C, 0xF5, 0xE1, 0x49, 0x79, 0x97, 0xE2, 0xD5, 0x87, 0xB7, 0x5F, + 0xEB, 0x47, 0xBC, 0xD6, 0xA1, 0x38, 0xC2, 0x40, 0xA9, 0x8A, 0xF3, 0x0D, 0x70, 0xEF, 0x8F, 0x62, + 0x81, 0xF8, 0x33, 0x06, 0xAA, 0x15, 0xC7, 0x26, 0x47, 0x01, 0x1D, 0x64, 0x88, 0x02, 0x53, 0x50, + 0x75, 0x1A, 0x05, 0xFC, 0xFD, 0x69, 0xFD, 0x48, 0x34, 0x05, 0x65, 0x6D, 0x4D, 0x65, 0xDC, 0x7D, + 0x36, 0x5D, 0xFA, 0x95, 0x40, 0x47, 0x3F, 0x42, 0xB2, 0xD4, 0x33, 0xCD, 0xCF, 0x10, 0xD3, 0x0F, + 0x1C, 0xD2, 0xEC, 0x9E, 0xFC, 0xDC, 0x15, 0x10, 0xC1, 0x19, 0x6D, 0xC5, 0xC4, 0x2C, 0xDC, 0xC8, + 0x1D, 0x9B, 0x4A, 0x78, 0xCC, 0x9C, 0xD4, 0xE1, 0x90, 0x38, 0xBC, 0x8C, 0x03, 0x9F, 0xA0, 0x73, + 0xE0, 0xCD, 0x57, 0x1F, 0xFE, 0xC9, 0x92, 0x14, 0x96, 0xF9, 0xFE, 0xBD, 0xD4, 0xFA, 0x6B, 0x9C, + 0x72, 0x59, 0x69, 0x95, 0x1A, 0x3F, 0xB0, 0x89, 0x3C, 0xE4, 0xDD, 0x6E, 0x3D, 0x13, 0xD6, 0x83, + 0x27, 0x53, 0xDB, 0x3D, 0xBF, 0x94, 0x09, 0xAF, 0x74, 0x16, 0xEF, 0xF5, 0x54, 0x9A, 0x9F, 0x57, + 0x78, 0xF3, 0x0C, 0x08, 0x34, 0x07, 0xC7, 0xA4, 0x9A, 0xBD, 0xC7, 0x54, 0x51, 0xB3, 0x4B, 0x74, + 0x93, 0x39, 0x61, 0x3A, 0xFD, 0x0D, 0x75, 0xFE, 0x49, 0x65, 0x51, 0xD0, 0x95, 0xF2, 0x09, 0x9B, + 0xA9, 0x97, 0x13, 0x82, 0xF2, 0x40, 0x4D, 0xD3, 0x21, 0x5F, 0x4B, 0xD1, 0xFB, 0xFB, 0x02, 0x46, + 0xCE, 0x05, 0x00, 0x51, 0x3A, 0xB1, 0xDB, 0x86, 0x90, 0x0F, 0xD3, 0xB4, 0xED, 0xD3, 0xBB, 0x2D, + 0x9E, 0x55, 0x26, 0x56, 0x87, 0xC2, 0xDA, 0x67, 0xA0, 0x42, 0x8B, 0x24, 0x8E, 0xE2, 0x75, 0x9A, + 0x2D, 0x5F, 0x61, 0x2A, 0x60, 0x71, 0xE8, 0x2C, 0x70, 0x0F, 0x4A, 0x95, 0xA9, 0x1A, 0x26, 0x3C, + 0x68, 0xC1, 0xBA, 0xF0, 0x7B, 0x12, 0xA3, 0x5D, 0x57, 0x57, 0x68, 0x43, 0xC5, 0x68, 0x0F, 0xEE, + 0x62, 0x6E, 0xF1, 0xAB, 0x2A, 0x51, 0x44, 0x77, 0x4B, 0xFD, 0xB7, 0xD1, 0xAC, 0x4E, 0xC0, 0x54, + 0x8C, 0x85, 0x50, 0xBA, 0xEC, 0xCB, 0xE2, 0x76, 0xF8, 0x9E, 0xDB, 0x80, 0x36, 0x04, 0xF9, 0xE4, + 0xCE, 0xED, 0x76, 0x2B, 0x9B, 0x63, 0x07, 0xB2, 0xB9, 0x0A, 0x1F, 0x6E, 0xEE, 0x15, 0x17, 0xEA, + 0x79, 0xD3, 0x68, 0xBF, 0x1C, 0x9E, 0xA1, 0x03, 0xC9, 0x05, 0x5E, 0xA7, 0x1B, 0x7C, 0x0D, 0x97, + 0x36, 0x72, 0x26, 0x83, 0xFB, 0x11, 0xE3, 0x9A, 0x0B, 0xE0, 0x72, 0x83, 0xB6, 0x36, 0x90, 0x88, + 0x7C, 0xA4, 0x5E, 0xE2, 0xAF, 0x83, 0x28, 0xA6, 0x9F, 0xB5, 0xC1, 0x43, 0xE6, 0xCB, 0x0C, 0x6B, + 0x40, 0x1B, 0x55, 0x3D, 0xAA, 0x00, 0xD4, 0x9A, 0x93, 0x20, 0x82, 0xE2, 0xEE, 0x5C, 0x9C, 0x7E, + 0x7B, 0x49, 0xE2, 0x5D, 0x4F, 0xD6, 0xB3, 0x19, 0x83, 0x1A, 0xA3, 0x20, 0x0F, 0x14, 0x0E, 0xD8, + 0x35, 0x2A, 0x67, 0x44, 0x65, 0x3E, 0xD3, 0x0F, 0x67, 0x54, 0xDB, 0x77, 0xA8, 0x6C, 0xDE, 0x56, + 0x80, 0x08, 0x2E, 0x55, 0xA0, 0xEC, 0x3B, 0x88, 0xAE, 0x88, 0x90, 0xFE, 0xAF, 0x80, 0xAD, 0xE2, + 0xF3, 0xF7, 0xEF, 0x29, 0xE3, 0xE7, 0xC1, 0x92, 0xC5, 0x6B, 0x5E, 0x2F, 0x89, 0x47, 0x77, 0x98, + 0xA3, 0x55, 0xD7, 0x12, 0x39, 0xC0, 0x51, 0x8C, 0xB1, 0x3C, 0xAB, 0x4C, 0x59, 0x42, 0xDA, 0xE7, + 0xCD, 0xAB, 0x08, 0x82, 0x3B, 0x7D, 0x02, 0xA9, 0x1F, 0x98, 0xAE, 0x07, 0x15, 0x2F, 0x2C, 0x19, + 0x4D, 0x19, 0x94, 0x80, 0xCF, 0x91, 0x4F, 0x3F, 0x0B, 0x3E, 0x69, 0x45, 0x1A, 0x88, 0xA3, 0xB2, + 0x23, 0xF1, 0x01, 0x78, 0x7C, 0x1B, 0x7D, 0x98, 0xCC, 0x1A, 0x41, 0xD3, 0xB3, 0x1D, 0xD9, 0xD3, + 0x17, 0x14, 0xDA, 0x3F, 0x9A, 0x9F, 0xA1, 0x2D, 0x3B, 0x9E, 0xE7, 0x1F, 0xAD, 0xCF, 0x55, 0xBA, + 0x4F, 0x5F, 0x40, 0x71, 0xA9, 0xBA, 0xD1, 0xEB, 0xC8, 0x3F, 0xE3, 0x2D, 0xCD, 0x55, 0x7D, 0x4F, + 0xC4, 0xDC, 0xA7, 0x4F, 0xF3, 0x80, 0x7D, 0xCA, 0x49, 0xCA, 0xD8, 0x32, 0x25, 0xD7, 0xF1, 0x9A, + 0xE0, 0xFB, 0x44, 0x2A, 0x11, 0x21, 0x33, 0xA8, 0x37, 0x89, 0x17, 0xC5, 0x60, 0x4F, 0x90, 0x31, + 0x2B, 0x3D, 0xD1, 0x71, 0x58, 0x22, 0xC6, 0x45, 0xF1, 0x86, 0x94, 0xF3, 0x16, 0x0A, 0xB6, 0x56, + 0xCA, 0x96, 0xCA, 0xEF, 0xF9, 0x20, 0x5B, 0x76, 0x4A, 0xE4, 0xAD, 0x17, 0x98, 0xC8, 0x33, 0xAA, + 0xDD, 0x5D, 0x06, 0x6D, 0x1E, 0x5F, 0x04, 0xE1, 0x6B, 0x48, 0x8F, 0xCB, 0x37, 0x4B, 0x8B, 0x3E, + 0x62, 0x3A, 0xBE, 0x65, 0x74, 0xAC, 0x7E, 0xBA, 0x6B, 0x66, 0x29, 0x34, 0x1D, 0xCB, 0xD9, 0x1E, + 0x9F, 0xEE, 0xC9, 0xB7, 0xA6, 0x8E, 0x96, 0x78, 0x59, 0xC9, 0x56, 0x2D, 0xF3, 0xF4, 0x3B, 0x2B, + 0x38, 0x0A, 0xBE, 0xE6, 0x61, 0x95, 0x9B, 0x2C, 0xDC, 0x8A, 0x79, 0x0F, 0x28, 0xD8, 0x64, 0x30, + 0x05, 0x52, 0xFE, 0x0C, 0x35, 0x9B, 0xE0, 0xE8, 0x9D, 0x55, 0xDB, 0x8E, 0x84, 0x1F, 0x5F, 0xAF, + 0x65, 0x89, 0x5C, 0x5E, 0xB2, 0xDD, 0x6D, 0x0B, 0xF7, 0xAD, 0xD4, 0x76, 0xB0, 0x3C, 0xF2, 0x66, + 0xDC, 0x11, 0x30, 0xD9, 0x3B, 0x72, 0x8F, 0x33, 0x86, 0xC7, 0xDB, 0x6F, 0x65, 0x66, 0xF6, 0x5E, + 0xE8, 0xEA, 0xEA, 0x9E, 0x16, 0x5F, 0xD4, 0x97, 0xFF, 0x22, 0xFF, 0xB0, 0x3B, 0xBD, 0x84, 0xE3, + 0x50, 0x3A, 0xC4, 0xC3, 0x85, 0x2C, 0x3A, 0x40, 0x6A, 0x89, 0x53, 0x0E, 0x29, 0xF6, 0xA7, 0x4F, + 0xA9, 0x5B, 0x7D, 0x2C, 0xF7, 0x7E, 0xFF, 0xAE, 0xCA, 0x5E, 0xE5, 0x74, 0x67, 0xA2, 0x5C, 0xC3, + 0xB4, 0xCB, 0x86, 0x48, 0x91, 0x8D, 0xD2, 0xAA, 0x83, 0xA6, 0x18, 0xC5, 0x42, 0x51, 0x19, 0x0F, + 0x45, 0x36, 0x85, 0x6B, 0x3A, 0xE5, 0xF1, 0xD2, 0x4A, 0x84, 0x3F, 0xB8, 0xD3, 0x28, 0xF0, 0x68, + 0xD5, 0x35, 0x75, 0x3E, 0x82, 0xD8, 0x2C, 0xFE, 0x1E, 0x01, 0x38, 0x5C, 0x2F, 0x29, 0x72, 0xD4, + 0x18, 0x59, 0x77, 0x3B, 0x2B, 0x29, 0x96, 0xE8, 0x3E, 0xDA, 0x56, 0x28, 0xAE, 0x6B, 0x19, 0x91, + 0xEE, 0x9A, 0x27, 0x98, 0x48, 0x4E, 0x41, 0x6F, 0x93, 0x7C, 0x7D, 0x8E, 0x39, 0x83, 0x4A, 0x85, + 0x12, 0x86, 0xF6, 0x80, 0x7B, 0xA5, 0xBA, 0x85, 0x79, 0x82, 0x20, 0x79, 0x1F, 0xDB, 0xB0, 0x9C, + 0x2D, 0x77, 0xDE, 0xB9, 0x95, 0x50, 0x44, 0x47, 0xCC, 0x40, 0x0F, 0xDB, 0x67, 0xF1, 0x5A, 0xE8, + 0x81, 0x82, 0xB3, 0xB4, 0x85, 0x88, 0xAF, 0x15, 0x3E, 0x0A, 0x92, 0x88, 0x0A, 0xC3, 0x3B, 0x02, + 0x6E, 0x58, 0x38, 0x0A, 0xB5, 0x83, 0x0C, 0x82, 0x3B, 0x3C, 0x7A, 0xB5, 0x33, 0x1A, 0x5F, 0xF3, + 0x13, 0x48, 0x3C, 0x7B, 0x7F, 0xF6, 0xF2, 0x5D, 0xB9, 0xD8, 0x45, 0x27, 0x0B, 0x28, 0x44, 0x1C, + 0x9C, 0x6F, 0x83, 0x3E, 0xFD, 0xFD, 0xF9, 0xD9, 0xD9, 0x87, 0xB7, 0xEF, 0x5E, 0xEC, 0x1F, 0xC2, + 0x71, 0xC8, 0xD9, 0xFB, 0x9F, 0xDF, 0x9C, 0x9E, 0x8F, 0xAE, 0x71, 0x5B, 0x2E, 0xD8, 0x17, 0x11, + 0x82, 0xBB, 0xB7, 0x86, 0x83, 0x9D, 0xAD, 0xE1, 0x27, 0xD0, 0xA6, 0xEA, 0x02, 0x51, 0x25, 0x04, + 0xD5, 0x2A, 0x21, 0x2F, 0x03, 0x2A, 0xF9, 0x56, 0x90, 0x97, 0x0A, 0x83, 0xAC, 0xE2, 0x07, 0x51, + 0x07, 0xE5, 0x32, 0x21, 0x12, 0x61, 0x28, 0xC8, 0xCA, 0x84, 0x4D, 0x10, 0xF9, 0xF1, 0x66, 0x8F, + 0xE3, 0xCE, 0xE6, 0xDF, 0x0E, 0x4F, 0x5A, 0xEA, 0xDD, 0xCF, 0x93, 0x96, 0x7A, 0xE5, 0x5C, 0xFC, + 0xCF, 0x02, 0xFF, 0x0F, 0x90, 0xD3, 0xC8, 0x0C, 0x33, 0x50, 0x00, 0x00 +}; +#endif //__embedded_h \ No newline at end of file diff --git a/embedded/footer.txt b/embedded/footer.txt new file mode 100644 index 00000000..c124467c --- /dev/null +++ b/embedded/footer.txt @@ -0,0 +1 @@ +#endif //__embedded_h \ No newline at end of file diff --git a/embedded/gulpfile.js b/embedded/gulpfile.js new file mode 100644 index 00000000..8deb4a79 --- /dev/null +++ b/embedded/gulpfile.js @@ -0,0 +1,125 @@ +var gulp = require('gulp'), + jshint = require('gulp-jshint'), + gulpif = require('gulp-if'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + cleanCSS = require('gulp-clean-css'), + removeCode = require('gulp-remove-code'), + merge = require('merge-stream'), + del = require('del'), + zip = require('gulp-zip'), + gzip = require('gulp-gzip'), + htmlmin = require('gulp-htmlmin'), + replace = require('gulp-replace'), + fs = require('fs'), + smoosher = require('gulp-smoosher'); + +var demoMode = false; +var testMode = false; + +function clean() { + return del(['dist']); +} + +function clean2() { + return del(['dist/js', 'dist/css']); +} +function lint() { + return gulp.src('www/js/**/script.js') + .pipe(jshint()) + .pipe(jshint.reporter('default')); +} + +function Copytest() { + return merge( + gulp.src(['www/tool.html']) + .pipe(removeCode({production: false})) + .pipe(gulp.dest('dist')), + gulp.src(['www/images/**/*.*']) + .pipe(gulp.dest('dist/images')) + ) +} + +function Copy() { + return merge( + gulp.src(['www/tool.html']) + .pipe(removeCode({production: true})) + .pipe(gulp.dest('dist')), + gulp.src(['www/images/**/*.*']) + .pipe(gulp.dest('dist/images')) + ) +} + +function concatApptest() { + return merge( + gulp.src([ 'www/js/**/*.js']) + .pipe(concat('script.js')) + .pipe(removeCode({production: false})) + .pipe(gulp.dest('./dist/js')), + + gulp.src([ 'www/css/**/*.css']) + .pipe(concat('style.css')) + .pipe(gulp.dest('./dist/css/')) + ) +} + +function concatApp() { + return merge( + gulp.src([ 'www/js/**/*.js']) + .pipe(concat('script.js')) + .pipe(removeCode({production: true})) + .pipe(gulp.dest('./dist/js')), + + gulp.src([ 'www/css/**/*.css']) + .pipe(concat('style.css')) + .pipe(gulp.dest('./dist/css/')) + ) +} + +function minifyApp() { + return merge( + gulp.src(['dist/js/script.js']) + .pipe(uglify({mangle: true})) + .pipe(gulp.dest('./dist/js/')), + + gulp.src('dist/css/style.css') + .pipe(cleanCSS({debug: true}, function(details) { + console.log(details.name + ': ' + details.stats.originalSize); + console.log(details.name + ': ' + details.stats.minifiedSize); + })) + .pipe(gulp.dest('./dist/css/')), + + gulp.src('dist/tool.html') + .pipe(htmlmin({collapseWhitespace: true, minifyCSS: true})) + .pipe(gulp.dest('dist')) + ) +} + +function smoosh() { + return gulp.src('dist/tool.html') + .pipe(smoosher()) + .pipe(gulp.dest('dist')) +} + +function compress() { + return gulp.src('dist/tool.html') + .pipe(gzip()) + .pipe(gulp.dest('.')); +} + +gulp.task(clean); +gulp.task(lint); +gulp.task(Copy); +gulp.task(Copytest); +gulp.task(concatApp); +gulp.task(concatApptest); +gulp.task(minifyApp); +gulp.task(smoosh); +gulp.task(clean2); + +var defaultSeries = gulp.series(clean, lint, Copy, concatApp, smoosh); +var packageSeries = gulp.series(clean, lint, Copy, concatApp,minifyApp, smoosh, compress, clean2); + +gulp.task('default', defaultSeries); +gulp.task('package', packageSeries); + diff --git a/embedded/header.txt b/embedded/header.txt new file mode 100644 index 00000000..423d2edb --- /dev/null +++ b/embedded/header.txt @@ -0,0 +1,24 @@ +/* + embedded.h - ESP3D data file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//data generated by https://github.com/AraHaan/bin2c +//bin2c Conversion Tool v0.14.0 - Windows - [FINAL]. +#ifndef __embedded_h +#define __embedded_h diff --git a/embedded/package-lock.json b/embedded/package-lock.json new file mode 100644 index 00000000..585e4ceb --- /dev/null +++ b/embedded/package-lock.json @@ -0,0 +1,5671 @@ +{ + "name": "embedded4ESP3D", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "CSSselect": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/CSSselect/-/CSSselect-0.4.1.tgz", + "integrity": "sha1-+Kt+H4QYzmPNput713ioXX7EkrI=", + "dev": true, + "requires": { + "CSSwhat": "0.4", + "domutils": "1.4" + } + }, + "CSSwhat": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/CSSwhat/-/CSSwhat-0.4.7.tgz", + "integrity": "sha1-hn2g/zn3eGEyQsRM/qg/CqTr35s=", + "dev": true + }, + "acorn": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.6.4.tgz", + "integrity": "sha1-6x9FtKQ/ox0DcBpexG87Umc+kO4=", + "dev": true + }, + "alter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", + "dev": true, + "requires": { + "stable": "~0.1.3" + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "async-done": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", + "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^1.0.7", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-replace/-/async-replace-1.0.1.tgz", + "integrity": "sha1-0/CFfM0C8elOsUnLX4nVisTwIdY=", + "dev": true, + "requires": { + "async": "^1.4.2" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "binary-extensions": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", + "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", + "dev": true + }, + "binaryextensions": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.2.tgz", + "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bufferstreams": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.1.3.tgz", + "integrity": "sha512-HaJnVuslRF4g2kSDeyl++AaVizoitCpL9PglzCYwy0uHHyvWerfvEb8jWmYbF1z4kiVFolGomnxSGl+GUQp2jg==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "cdnizer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/cdnizer/-/cdnizer-3.0.2.tgz", + "integrity": "sha512-XTBGCv61pn9r8o/HDkUlPLv9hQ1bx0maWj07UdIU1VV/2P5KAswN9OEHPu6Tz7C5zMXIywdERtpCw0osQYm1ZA==", + "dev": true, + "requires": { + "cdnjs-cdn-data": "^0.1.1", + "google-cdn-data": "^0.1.6", + "jsdelivr-cdn-data": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "lodash": "^4.17.11", + "minimatch": "^3.0.2" + } + }, + "cdnjs-cdn-data": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cdnjs-cdn-data/-/cdnjs-cdn-data-0.1.2.tgz", + "integrity": "sha1-hl00uk5I3Rtz/WaOJKYaWt+biyE=", + "dev": true, + "requires": { + "semver": "~5.0.1" + }, + "dependencies": { + "semver": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz", + "integrity": "sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no=", + "dev": true + } + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cheerio": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.13.1.tgz", + "integrity": "sha1-SK8RNFYbNSf4PZFWxPmo69grBuw=", + "dev": true, + "requires": { + "CSSselect": "~0.4.0", + "entities": "0.x", + "htmlparser2": "~3.4.0", + "underscore": "~1.5" + } + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "^7.1.1" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "^0.10.9" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, + "deprecated": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.2.tgz", + "integrity": "sha1-vJ3Pm86RdPz5CQzxKVExxDnGgv0=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "dev": true, + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz", + "integrity": "sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", + "integrity": "sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-0.5.0.tgz", + "integrity": "sha1-9hHLWuIhBQ4AEsZpeVA/164ZzEk=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "filesize": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.1.6.tgz", + "integrity": "sha1-WISSTvyBpkTjcJqsQDIWGDw9eYo=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "fined": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", + "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha1-24Sfznf2cIpfjzhq5TOgkHtUrnA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=", + "dev": true + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.21", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + } + }, + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "google-cdn-data": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/google-cdn-data/-/google-cdn-data-0.1.25.tgz", + "integrity": "sha1-nDwxSasYp8LV7V8PC07ovEWZK3E=", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "gulp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", + "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", + "dev": true, + "requires": { + "glob-watcher": "^5.0.0", + "gulp-cli": "^2.0.0", + "undertaker": "^1.0.0", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "gulp-cli": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz", + "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^2.5.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + } + } + }, + "gulp-bytediff": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-bytediff/-/gulp-bytediff-1.0.0.tgz", + "integrity": "sha1-VXPidyiwsW1cqIaU/NNhYzj5xS0=", + "dev": true, + "requires": { + "filesize": "~3.1.3", + "gulp-util": "~3.0.6", + "map-stream": "~0.0.6" + } + }, + "gulp-cdnizer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-cdnizer/-/gulp-cdnizer-2.0.2.tgz", + "integrity": "sha512-kXTdxYMiUoQXvRUaqwm5+uGR2LqUy/7GHde2EtRBK1qY0BUKnbV2mInLBvdsITFQpZ/F6zX2FVFIpLa0qz/8Bw==", + "dev": true, + "requires": { + "cdnizer": "^3.0.2", + "gulp-util": "^3.0.0", + "through2": "^0.5.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-clean-css": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.0.0.tgz", + "integrity": "sha512-/Hs+dmclQfFBSpwrAKd4wTVsahJvrVIg2ga0J7Eo7DKVTVfJrM7wXlfU1mK9iJ9Y7OmkO/YstZVtmhfAKzZ00g==", + "dev": true, + "requires": { + "clean-css": "4.2.1", + "plugin-error": "1.0.1", + "through2": "3.0.0", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "through2": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.0.tgz", + "integrity": "sha512-8B+sevlqP4OiCjonI1Zw03Sf8PuV1eRsYQgLad5eonILOdyeRsY27A/2Ze8IlvlMvq31OH+3fz/styI7Ya62yQ==", + "dev": true, + "requires": { + "readable-stream": "2 || 3", + "xtend": "~4.0.1" + } + } + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + } + }, + "gulp-gzip": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/gulp-gzip/-/gulp-gzip-1.4.2.tgz", + "integrity": "sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "bytes": "^3.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.0", + "stream-to-array": "^2.3.0", + "through2": "^2.0.3" + } + }, + "gulp-htmlmin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz", + "integrity": "sha512-ASlyDPZOSKjHYUifYV0rf9JPDflN9IRIb8lw2vRqtYMC4ljU3zAmnnaVXwFQ3H+CfXxZSUesZ2x7jrnPJu93jA==", + "dev": true, + "requires": { + "html-minifier": "^3.5.20", + "plugin-error": "^1.0.1", + "through2": "^2.0.3" + } + }, + "gulp-if": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-2.0.2.tgz", + "integrity": "sha1-pJe351cwBQQcqivIt92jyARE1ik=", + "dev": true, + "requires": { + "gulp-match": "^1.0.3", + "ternary-stream": "^2.0.1", + "through2": "^2.0.1" + } + }, + "gulp-jshint": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-jshint/-/gulp-jshint-2.1.0.tgz", + "integrity": "sha512-sP3NK8Y/1e58O0PH9t6s7DAr/lKDSUbIY207oWSeufM6/VclB7jJrIBcPCsyhrFTCDUl9DauePbt6VqP2vPM5w==", + "dev": true, + "requires": { + "lodash": "^4.12.0", + "minimatch": "^3.0.3", + "plugin-error": "^0.1.2", + "rcloader": "^0.2.2", + "through2": "^2.0.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulp-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.0.3.tgz", + "integrity": "sha1-kcfA1/Kb7NZgbVfYCn+Hdqh6uo4=", + "dev": true, + "requires": { + "minimatch": "^3.0.3" + } + }, + "gulp-ng-annotate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-ng-annotate/-/gulp-ng-annotate-2.1.0.tgz", + "integrity": "sha512-wjazOa5qE83akCih+lK2a0LFvkLbIMeblxr54ofmc3WKJ3Ipx/BM98ZCtCDfQW/008EVUSRqwfEjFKEEGI0QbA==", + "dev": true, + "requires": { + "bufferstreams": "^1.1.0", + "merge": "^1.2.0", + "ng-annotate": "^1.2.1", + "plugin-error": "^0.1.2", + "through2": "^2.0.1", + "vinyl-sourcemaps-apply": "^0.2.1" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulp-remove-code": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/gulp-remove-code/-/gulp-remove-code-3.0.4.tgz", + "integrity": "sha512-nfGXuE2ra/o008t+XPzd3/dbkgmO4XNLEUibCFlv4KS5+V2cLGU0m9Rmdd4L9ZkduwC1+/AuSEyySt7CZhcLzw==", + "dev": true, + "requires": { + "bufferstreams": "^2.0.1", + "escape-string-regexp": "^1.0.5", + "object.entries": "^1.0.4", + "plugin-error": "^1.0.1", + "through2": "^2.0.3" + }, + "dependencies": { + "bufferstreams": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-2.0.1.tgz", + "integrity": "sha512-ZswyIoBfFb3cVDsnZLLj2IDJ/0ppYdil/v2EGlZXvoefO689FokEmFEldhN5dV7R2QBxFneqTJOMIpfqhj+n0g==", + "dev": true, + "requires": { + "readable-stream": "^2.3.6" + } + } + } + }, + "gulp-replace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", + "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "dev": true, + "requires": { + "istextorbinary": "2.2.1", + "readable-stream": "^2.0.1", + "replacestream": "^4.0.0" + } + }, + "gulp-smoosher": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/gulp-smoosher/-/gulp-smoosher-0.0.9.tgz", + "integrity": "sha1-IiqiHu5TEzzvKL8ki1xJdbid2x8=", + "dev": true, + "requires": { + "async": "^0.9.0", + "async-replace": "^1.0.0", + "cheerio": "~0.13.1", + "gulp-util": "~2.2.14", + "lodash": "^3.10.1", + "through2": "~0.4.1" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "^1.1.0", + "escape-string-regexp": "^1.0.0", + "has-ansi": "^0.1.0", + "strip-ansi": "^0.3.0", + "supports-color": "^0.2.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "^0.5.0", + "dateformat": "^1.0.7-1.2.3", + "lodash._reinterpolate": "^2.4.1", + "lodash.template": "^2.4.1", + "minimist": "^0.2.0", + "multipipe": "^0.1.0", + "through2": "^0.5.0", + "vinyl": "^0.2.1" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "requires": { + "lodash._escapehtmlchar": "~2.4.1", + "lodash._reunescapedhtml": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "requires": { + "lodash._escapestringchar": "~2.4.1", + "lodash._reinterpolate": "~2.4.1", + "lodash.defaults": "~2.4.1", + "lodash.escape": "~2.4.1", + "lodash.keys": "~2.4.1", + "lodash.templatesettings": "~2.4.1", + "lodash.values": "~2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "requires": { + "lodash._reinterpolate": "~2.4.1", + "lodash.escape": "~2.4.1" + } + }, + "minimist": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", + "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "^0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "through2": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", + "dev": true, + "requires": { + "readable-stream": "~1.0.17", + "xtend": "~2.1.1" + }, + "dependencies": { + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "~0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-uglify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.1.tgz", + "integrity": "sha512-KVffbGY9d4Wv90bW/B1KZJyunLMyfHTBbilpDvmcrj5Go0/a1G3uVpt+1gRBWSw/11dqR3coJ1oWNTt1AiXuWQ==", + "dev": true, + "requires": { + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash": "^4.13.1", + "make-error-cause": "^1.1.1", + "safe-buffer": "^5.1.2", + "through2": "^2.0.0", + "uglify-js": "^3.0.5", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-4.2.0.tgz", + "integrity": "sha512-I+697f6jf+PncdTrqfuwoauxgnLG1yHRg3vlmvDgmJuEnlEHy4meBktJ/oHgfyg4tp6X25wuZqUOraVeVg97wQ==", + "dev": true, + "requires": { + "get-stream": "^3.0.0", + "plugin-error": "^0.1.2", + "through2": "^2.0.1", + "vinyl": "^2.1.0", + "yazl": "^2.1.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "htmlparser2": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.4.0.tgz", + "integrity": "sha1-oc1l9YI60oXhnWOwha1yLQpR6uc=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.2", + "domutils": "1.3", + "readable-stream": "1.1" + }, + "dependencies": { + "domutils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz", + "integrity": "sha1-mtTVm1r2ymhMYv5tdo7xcOcN8ZI=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "requires": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + } + }, + "jsdelivr-cdn-data": { + "version": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "from": "git://github.com/shahata/jsdelivr-cdn-data.git#d014a2ad1bdfb4c6e3d3cefc7f264435281b91e0", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "jshint": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.7.tgz", + "integrity": "sha512-Q8XN38hGsVQhdlM+4gd1Xl7OB1VieSuCJf+fEJjpo59JH99bVJhXRXAh26qQ15wfdd1VPMuDWNeSWoNl53T4YA==", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.10", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + }, + "dependencies": { + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "dev": true, + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "requires": { + "lodash._htmlescapes": "~2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "requires": { + "lodash._htmlescapes": "~2.4.1", + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "requires": { + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "^1.2.0" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "ng-annotate": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ng-annotate/-/ng-annotate-1.2.2.tgz", + "integrity": "sha1-3D/FG6Cy+LOF2+BH9NoG9YCh/WE=", + "dev": true, + "requires": { + "acorn": "~2.6.4", + "alter": "~0.2.0", + "convert-source-map": "~1.1.2", + "optimist": "~0.6.1", + "ordered-ast-traverse": "~1.1.1", + "simple-fmt": "~0.1.0", + "simple-is": "~0.2.0", + "source-map": "~0.5.3", + "stable": "~0.1.5", + "stringmap": "~0.2.2", + "stringset": "~0.2.1", + "tryor": "~0.1.2" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + } + } + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "now-and-later": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.0.tgz", + "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "ordered-ast-traverse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ordered-ast-traverse/-/ordered-ast-traverse-1.1.1.tgz", + "integrity": "sha1-aEOhcLwO7otSDMjdwd3TqjD6BXw=", + "dev": true, + "requires": { + "ordered-esprima-props": "~1.1.0" + } + }, + "ordered-esprima-props": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ordered-esprima-props/-/ordered-esprima-props-1.1.0.tgz", + "integrity": "sha1-qYJwht9fAQqmDpvQK24DNc6i/8s=", + "dev": true + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.0.tgz", + "integrity": "sha512-02GTVHD1u0nWc20n2G7WX/PgdhNFG04j5fi1OkaJzPWLTcf6vh6229Lta1wTmXG/7Dg42tCssgkccVt7qvd8Kg==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "rcfinder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/rcfinder/-/rcfinder-0.1.9.tgz", + "integrity": "sha1-8+gPOH3fmugK4wpBADKWQuroERU=", + "dev": true, + "requires": { + "lodash.clonedeep": "^4.3.2" + } + }, + "rcloader": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/rcloader/-/rcloader-0.2.2.tgz", + "integrity": "sha1-WNIpi0YtC5v9ITPSoex0+9cFxxc=", + "dev": true, + "requires": { + "lodash.assign": "^4.2.0", + "lodash.isobject": "^3.0.2", + "lodash.merge": "^4.6.0", + "rcfinder": "^0.1.6" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-fmt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=", + "dev": true + }, + "simple-is": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringmap": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=", + "dev": true + }, + "stringset": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "ternary-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-2.0.1.tgz", + "integrity": "sha1-Bk5Im0tb9gumpre8fy9cJ07Pgmk=", + "dev": true, + "requires": { + "duplexify": "^3.5.0", + "fork-stream": "^0.0.4", + "merge-stream": "^1.0.0", + "through2": "^2.0.1" + } + }, + "textextensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.4.0.tgz", + "integrity": "sha512-qftQXnX1DzpSV8EddtHIT0eDDEiBF8ywhFYR2lI9xrGtxqKN+CvLXhACeCIGbCpQfxxERbrkZEFb8cZcDKbVZA==", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tryor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", + "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "underscore": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.5.2.tgz", + "integrity": "sha1-EzXF5PXm0zu7SwBrqMhqAPVW3gg=", + "dev": true + }, + "undertaker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", + "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", + "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "dev": true, + "requires": { + "source-map": "^0.5.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } + } + } +} diff --git a/embedded/package.json b/embedded/package.json new file mode 100644 index 00000000..b7a2d060 --- /dev/null +++ b/embedded/package.json @@ -0,0 +1,29 @@ +{ + "name": "embedded4ESP3D", + "description": "Embedded files for ESP3D", + "devDependencies": { + "del": "latest", + "deprecated": "latest", + "fs": "latest", + "gulp": "^4.0.0", + "gulp-bytediff": "latest", + "gulp-cdnizer": "latest", + "gulp-clean-css": "latest", + "gulp-concat": "latest", + "gulp-gzip": "latest", + "gulp-htmlmin": "latest", + "gulp-if": "latest", + "gulp-jshint": "latest", + "gulp-ng-annotate": "latest", + "gulp-remove-code": "latest", + "gulp-replace": "latest", + "gulp-smoosher": "latest", + "gulp-uglify": "latest", + "gulp-zip": "latest", + "jshint": "latest", + "merge-stream": "latest" + }, + "repository": "https://github.com/luc-github/ESP3D", + "author": "Luc LEBOSSE", + "license": "(ISC OR GPL-3.0)" +} diff --git a/embedded/tool.html.gz b/embedded/tool.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..353c1720f2be45c47a0303396eb400cf6a0675f2 GIT binary patch literal 5916 zcmV+%7vtz3iwFP!000003hg{;bK6Fif2G}1F^MHW;^HmRbFG`J+CBbZHEK`HCD{iF0H)eSL|EyC!uuSu(p|N3;uK_>ME5KnZJg%;8 zC$WEBS%Ut`!b`@3sYiZeYex0_S%01cUZ|IFD8X`9SHm!fn6Y5Nd;x1UUAx!rTCOvk z#$lWc{^mH&aNCliZ_Y38m>Iw$Km#JdCM0!f_N_s=FuQzGk)6?i;OwaJLbul`}$7P zB5x8xsE*2;7B|^ay~8wLvIJQcDinCU%E*G-fwj6KX&eSV`J3RsB4ZMB9xn$rAhTa56t}iGmGLm~{9v69Q0FpbpTt*2y72sXGcXCx!f!I4cy&|9 zPt$B`!}|R+`a;^cxvJO-PXuH|v=y64F<0qp)HI`@sy)Y6~ql`KSc~kVE7Vqg& zoZQx^S)43kk%%X8cvIB`X-VFi$3vyJ4l*($r-Rhk(C{GRZnxWq1X;8OOK;Akp6!Yk zRPKJ_4Wg8BazK9Fs+8{>Ys3amz@bUoDGP@%FJ%VkYP{y8X|+?mfV$c`bcfgh#n51d))Z)QIX} zQ%Zg#MeUmAu;fdpEaEKLc5lnT_d_N_QoNon4D<-V&!rayt92;+%kXf|be*KYqE#FS z;P=!RA-9#607k@i$|gVnkvgn{tT`(z?^Q-5_{wtP&t2xjybl9jEI} zx?AqSW*y&iD^QfP)hlh%>sFeC1Fkd;_RJjY&fIa`dyutz9kXw4A#2(8#5D!7!E%11 zgu3veZMWUYy;s@(MUaql6$*R&iHV=Bbvsl1JldR4KjsO$eu&$sOcYoPG>?<*n92yV zv3HUA@1!UMq`*SH>c#D#nh%J`UvyTq2K$0&wdNl&cNxuskbNEuGJ5dQSg*p^^NqHpU)4UZakdoneBwq;~)XVUY%2MJY0!M{ZN=Mk-AO-z3~2el?0zt?`4Xr;CP zCE7o^Xzfnln@qF9WfsT4kt21B)irzjVBe#3*(pH&4NER#-z$n8!Lqz6o&{ITAAWbG z-)0Oy86U8sE@_Fa7R;1HWJ7zE#KzB|BKd*rtJI=1{AB1=cLGd>UDms;;++h8r8I7{ z&6W=*Wfz@4DDB(kBJkOlfLAQ!_U}vP2Og;}!Dma2?{+}q+Rc`Z%D#3FA!U=&X~I}U zAk~HW`5s-j(>*v}?BCsgGMtxqF*4@jlRMP>E%3MlPv+T5VLx?4A|!9_-aHkowghk zer(f(S#ud-utFbEaSIm1>TOfU0UEH<3t8RLiD79leC6>4@kjJc2SHd}_rh@iV%H@` zJ9K(5s%?jDxLmk>_;pDidYnEs*`ejQq-S1qEMmAlVwuooneEW%nQ-H{p#wlJY#8dg z#DVF3CxoG$5MTn3-UaqJ1b(~D1x#^pQFw4eLydqN7=HneFtU#e3m^sp4%I3bxVC{f z6bJ~!tPr6&7r45CSSZvDfx97zv&aSFAlF574M9IXw8a=0f-~$3L;&bVC=!Sx*D-q5 zh0!qyVu2VS0zH(8i+BP+SlUHIdIV9oNhcKR0{upZxIkwI*bVfgYl=0!^&+kwFWM*ad-HV=$5IbfQwa-S@y1@K)pIcT3+j`j6ck#9l66lGG zYv*04dhM=Ycz%b9D>{u%Pfav(5JnlWk*x;e>gOn^2Rk1j28d~LmH^hz;P>T{89*YC z$gToa(FM5o+k|IxOYqbZk_))%w>Q0B5BmDT#L>zv&{vxa1cm2lcTt}KsXlrL%SL+x z-q6pGai0T%cZSQPn>C?ev3MMvEV?}-i+oCTl*iwbNF8(3{}!?BT{ z4>JB!td?d_^6asGBEt2EmNMc}6(=$cN&>K}S0DcQ^4;0#+ux{~+roIRpmjlBK~bAt zr<^Shz!qsA01|aE5p^-eN?5KXYGPTOQ4)De=v-c7WBTzkCChclgB7wYyQ9E)38`?S zq_pcw)e5q{mTOc>Zc5^48V1v^BYMUnAD60YLvW2=xQ2P}#aKFi8covGQ2w5*l6ao5 zG|l998Kl7^2m^jS0LJ+&%1~b=bGf9rfmBZ9Q$l?d2uQ;c`Q0PGL#$JBmpNJH-9?)A z5LQ&^{v}>QweQz!VUY5gUh~4RR${|DHUn%Iv{2phs)%O}?U#f>sPKcNuFT#nW_CVMQh}is~;|4GgU?{JlOk|6vD&KD__^#q;x* z#~qx?-Z}ih10f7Dj{*vpnxnU1>c1%J`R6obZvUd+BvF{Q}KU#VyxS2@2}s z7vvMwK{vm|vso2@un6Wu<8Zc)#Kt_B;pN1G0DFTd18pT|Lv=nXq25eaU}1G2K+_{Z zM=%0FaZ+auoaZYx)?|pTk(_;>Xqf}GR&7WQCkmhFxFrmb4ucck3|Tbi3&R>}2Ua<- z2yN7(#)y9mJ~t5K2EA)h+K_{DSEI1dgGOVRFuqPAB2Xy%1$vt^64X3hd?#0} zF6@ACObMcQ0HaQ~00`drZk+5YK+BX6@v1=f${^i)L$v#QAvpJ;(v=`wMb8~;cZ{iFK%W0msl6Rrg zZQ0m?izzOB-Rg?pzG$*xxUDX>ZuP{aC)zp)wL`ukfl2%oC=|OeHCcbv1f_ckl?nJ4 ztXP?LQ;J885yLQ~mm?4rC5lcV0#U7i0Z|bw1)-ZO;sAz)#pSYhV^o@j%Jr(}l<_M1g}CV$A25E@1jF3}yh0mbWL)#K-}m z4c3&ibl#{A`CfiFkGU5Tc`Pp`acnp~2QpK?JY@~f$7j5qPS-2>LNuXb=*f~naz~ay z1WHpAC4}?~3(%@jtq*rHb*teDaXufi0uZ8@tP0`n{-3Ds$J6x^f zR(^HuNa|C`+F%H)n~=s>_sD`U!Gct%WU)m?1gaTgo(=w?HipU-E2*qdqPMImbgK#Y z@p`6rW%b?B>?W3gIu{r1Rzt3h$}D>Nv<`Y{Y9hx(2F4?E7@uSdNhS^BMnj{91q_#f z>#F}_{JDvHDH?zgTs(g_R@x%!Nbo;pr&IDpd;AX>f8vD;vUD3??CAbgG28o9FJ0&e%(76EVzf_yX-@>Z>tk*M~BL;?#9 zwUhG~Dsld#GUrd3`=mA0r+_O9+hWr;V}CMU8b)$J;{DZ7~!t7%@@p9WlQxer>7s(0{*x zS*RsNfm91Rk}FW2UhKY_RCax(m|GH5SO{vENouRFq87Q`JXT8%um31TIT&>COw;o2mprN<0jC3+**KKqiZW8$+We@E{}B@&6Zz&xP#isZ<0B!l z*m|u-#e);Xj&y|{wI5NWF3Jiqp^BqfkSy!;7kNW`?7}he3{_2L5MU;iG)oL(RA)Od zvpnq>SQrE96(`q6V8fUy_|n0J+E-a65d3(ZzR19d{ZGggK-SSZ48hTDfu3eC0CW@l z!4gutVukWh5o2+gXcVyx`L8#xf8~7jj$N!F)zgC!Z3&%xCSS!I0U7sKFXTxC7=tD? zn@!vdfy4~K$?e+)i>9&9-oHDob4_;2U9M6`ISDX_f0B?7q&mR!FS0&T?j2M6d`VSag!%Ka3!DsU(+uj6UmeIgx7w${C)~0(h(kGOFptdc~0i zaD5kmb}M7&u9go&C9&V%o}FXRwlQ% zI%YyZdW7T3W+aSe6lRKFf85HC&?|NPay&v5wxE`S@ki}C+_GzJ;Drpz4Ej7G49%8c z%?9e?KY>yjI3jG7Yqy29{u=n~c^IO6^SyC%cW?aZ(|p^$ISpehs0&)eOOK=KV}sTK z_o?)f%-4>PzU^Dt;i0AXBr-0mS3*Y=-w8dkN`C&he`3Lr+~eJ3yrHi=V84%Hzc^dK z*Eb<+eD&c;d6(kVhqqtrN4(adIKn`wit`O{?~h`E_%jBo6~`t=0UczB0#i_R8U_6Q zY5hnv1!ZkbW!!x>UHX+kM?XTc)HBV`5YrDF((Io6+!YYP8EwVHEZoQ)n@V`hoYdiv zIJ}GlpP+N#%~v1($&wV7`To7s`fHqWS!tCTKd^~Bap){?4@#A(8YVnw!MgB! z5~E#LS7<}UXb^q3Ah>-rTfUV;5_wOm==fRT2YhEvjM|YT`Ro4z8-19}8bQ;u9of?t6xu(UpP?-~ zp64H}&sDllUqErGy3y;%KjSUUReevy+^0|TfPKnI%Gfd`*YTR*`$Q@cAv35qi5JCS zPmnPyJ<)MkBF07WhLrOb3bt0tmdfw>AX|1ygt{rU(}cX zBLA>Ey@caXI>f_;EIL4HiBk?K_NPy&TYW6|e*dn@UgdOWqFlqY%Z5mijnb-vrWnOS zQ5g?KHic_C~`DDY_2HFZ9M*ew1Y0XSSQDRR^pS$8`tDVFFy+Eys8YZ;O5 znkSe@PC;*ze0`2NgGz-GhW3DarMiWAf*^Sx+pwH1ciy>`P((+}Ko8qz@mlDCg0r-R zh_4ks3X+Hl!#e_QSU3u`gA9T@JbJY=8ej7hNIZLg_VQi1*hMD`C`25bZ-YMl{rTD1 zhqv!u>>t8$$l3d!-<+P0u5nu~*cTCkySIjeovq;~(5C7FQ6&V`Dj_ceD*0BBmkNWd y;s;R&VJOJTgiR3n@n(thead>tr>th,.table>tbody>tr>th,.table>thead>tr>td,.table>tbody>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd;} +.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9;} +a{position:relative;display:block;padding:10px 15px;text-decoration:none;color:#cccccc;} +.active{color:#ffffff;background-color:#000000;} +.active a,a:hover,a:focus{color:#FFFFFF;} +.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05);} +.panel-body{padding:15px;} +.panel-heading{padding:10px 15px;color:#333333;background-color:#f5f5f5;border-color:#dddddd;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #dddddd;} +.form-control{display:block;width:auto;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff +;background-image:none;border:1px solid #cccccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075); +* -webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s; +* transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6); +* box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6);} +.form-group{margin-bottom:15px;} +.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;} +.btn-primary{color:#ffffff;background-color:#337ab7;border-color:#2e6da4;} +.btn-primary:focus,.btn-primary:active,.btn-primary:hover,.btn-primary.focus,.btn-primary.active,.btn-primary.hover{color:#ffffff;background-color:#286090;border-color:#122b40;} +.btnimg {cursor:hand; border-radius:6px ;border:1px solid #FFFFFF;} +.btnimg:hover{background-color:#F0F0F0;border-color:#6c6c6c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;} +.btnroundimg {cursor:hand; border-radius:30px;} +.btnroundimg:hover{background-color:#F0F0F0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #808080;} +.blacklink, .blacklink:active {color:#000000;} +.blacklink:hover{color:#000000;} +input[type="file"]::-webkit-file-upload-button{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #5bc0de;border-color: #46b8da;} +input[type="file"]::-webkit-file-upload-button:focus{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #1b6d85;} +input[type="file"]::-webkit-file-upload-button:hover{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation; touch-action:manipulation;cursor:pointer; +background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px; +* -webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none; color: #ffffff;background-color: #31b0d5;border-color: #269abc;} +.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;} +.panel-footer{padding:10px 15px;color:#31708f;background-color:#f5f5f5;border-color:#dddddd;border-top:1px solid #dddddd;} + +.modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 10000; /* Sit on top */ + padding-top: 100px; /* Location of the box */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0,0,0); /* Fallback color */ + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ +} + +/* Modal Content */ +.modal-content { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + border: 2px solid #337AB7; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + position: relative; + margin: auto; + padding: 0; + background-color: #fefefe; +} + +.modal-header { + padding: 2px 16px; + color: #0f0f0f; + background-color: #f2f2f2; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-bottom: 1px solid #cfcfcf; +} + +.modal-body {padding: 10px 16px;} + +.modal-footer { + padding: 16px 16px; + height: 4.5em; + color: #0f0f0f; + background-color: #f2f2f2; + border-top: 1px solid #cfcfcf; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} +.hide_it { + display: none; +} + +@media (min-width: 768px) { + .modal-content { + width: 580px; + } +} + +@media screen and (max-width: 767px) { + .modal-content { + width: 100%; + } +} diff --git a/embedded/www/js/script.js b/embedded/www/js/script.js new file mode 100644 index 00000000..f565dabd --- /dev/null +++ b/embedded/www/js/script.js @@ -0,0 +1,430 @@ +var currentpath = "/"; +var authentication = false; +var webupdate = false; +var filesystem = false; +var websocket_port = 0; +var async_webcommunication = false; +var page_id = ""; +var ws_source; +var log_off =false; +var websocket_started =false; +function navbar(){ + var content=""; + var tlist = currentpath.split("/"); + var path="/"; + var nb = 1; + content+=""; + while (nb < (tlist.length-1)) + { + path+=tlist[nb] + "/"; + content+=""; + nb++; + } + content+="
/"+tlist[nb] +"/
"; + return content; +} + +function trash_icon(){ + var content =""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + content +=""; + return content; +} + +function back_icon(){ + var content =""; + return content; +} + +function select_dir(directoryname){ + currentpath+=directoryname + "/"; + SendCommand('list','all'); +} + +function compareStrings(a, b) { + // case-insensitive comparison + a = a.toLowerCase(); + b = b.toLowerCase(); + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +function dispatchfilestatus(jsonresponse) +{ +var content =""; +var display_message = false; +content ="  Status: "+jsonresponse.status; +content +="  |  Total space: "+jsonresponse.total; +content +="  |  Used space: "+jsonresponse.used; +content +="  |  Occupation: "; +content +=" "+jsonresponse.occupation +"%"; +document.getElementById('status').innerHTML=content; +content =""; +if (currentpath!="/") + { + var pos = currentpath.lastIndexOf("/",currentpath.length-2); + var previouspath = currentpath.slice(0,pos+1); + content +=""+back_icon()+" Up.."; + } +jsonresponse.files.sort(function(a, b) { + return compareStrings(a.name, b.name); +}); +if (currentpath=="/") { + display_message = true; +} +for (var i1=0;i1
"; + content +=jsonresponse.files[i1].name; + if ((jsonresponse.files[i1].name == "index.html.gz")||(jsonresponse.files[i1].name == "index.html")){ + display_message = false; + } + content +="
"; + content +=jsonresponse.files[i1].size; + content +="
"; + content +=trash_icon(); + content +="
"; + } +} +//then display directories +for (var i2=0;i2 "; + content +=jsonresponse.files[i2].name; + content +=""; + content +="
"; + content +=trash_icon(); + content +="
"; + } +} + if (display_message) { + + document.getElementById('MSG').innerHTML = "File index.html.gz is missing, please upload it"; + } else { + document.getElementById('MSG').innerHTML = "Go to ESP3D interface"; + } + document.getElementById('file_list').innerHTML=content; + document.getElementById('path').innerHTML=navbar();} + +function Delete(filename){ +if (confirm("Confirm deletion of file: " + filename))SendCommand("delete",filename); +} + +function Deletedir(filename){ +if (confirm("Confirm deletion of directory: " + filename))SendCommand("deletedir",filename); +} + +function Createdir(){ +var filename = prompt("Directory name", ""); +if (filename != null) { + SendCommand("createdir",filename.trim()); + } +} +function SendCommand(action,filename){ +var xmlhttp = new XMLHttpRequest(); +var url = "/files?action="+action; +document.getElementById('MSG').innerHTML = "Connecting..."; +url += "&filename="+encodeURI(filename); +url += "&path="+encodeURI(currentpath); +xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 ) { + if(xmlhttp.status == 200) { + var jsonresponse = JSON.parse(xmlhttp.responseText); + dispatchfilestatus(jsonresponse); + } else { + if(xmlhttp.status == 401) { + RL (); + } else { + console.log(xmlhttp.status); + FWError(); + } + } + } +}; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} + +function Sendfile(){ +var files = document.getElementById('file-select').files; +if (files.length==0)return; +document.getElementById('upload-button').value = "Uploading..."; +document.getElementById('prg').style.visibility = "visible"; +var formData = new FormData(); +formData.append('path', currentpath); +for (var i3 = 0; i3 < files.length; i3++) { +var file = files[i3]; +var arg = currentpath + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); + formData.append('myfiles[]', file, currentpath+file.name);} +var xmlhttp = new XMLHttpRequest(); +xmlhttp.open('POST', '/files', true); +//progress upload event +xmlhttp.upload.addEventListener("progress", updateProgress, false); +//progress function +function updateProgress (oEvent) { + if (oEvent.lengthComputable) { + var percentComplete = (oEvent.loaded / oEvent.total)*100; + document.getElementById('prg').value=percentComplete; + document.getElementById('upload-button').value = "Uploading ..." + percentComplete.toFixed(0)+"%" ; + } else { + // Impossible because size is unknown + } +} + +xmlhttp.onload = function () { + if (xmlhttp.status === 200) { +document.getElementById('upload-button').value = 'Upload'; +document.getElementById('prg').style.visibility = "hidden"; +document.getElementById('file-select').value=""; +var jsonresponse = JSON.parse(xmlhttp.responseText); +dispatchfilestatus(jsonresponse); + } else alert('An error occurred!'); +}; +xmlhttp.send(formData); +} + +function HideAll(msg){ + //console.log("Hide all:" + msg); + log_off = true; + if(websocket_started){ + ws_source.close(); + } + document.title = document.title + "(disconnected)"; + document.getElementById('MSG').innerHTML = msg; + document.getElementById('FILESYSTEM').style.display = "none"; + document.getElementById('FWUPDATE').style.display = "none"; +} + +function FWError(){ + HideAll("Failed to communicate with FW!"); +} +function FWOk(){ + document.getElementById('MSG').innerHTML = "Connected"; + if (filesystem){ + document.getElementById('FILESYSTEM').style.display = "block"; + } + if (webupdate){ + document.getElementById('FWUPDATE').style.display = "block"; + } +} + +function InitUI(){ +var xmlhttp = new XMLHttpRequest(); +var url = "/command?cmd="+encodeURI("[ESP800]"); +authentication = false; +async_webcommunication = false; +xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4 ) { + var error = false; + if(xmlhttp.status == 200) { + var jsonresponse = JSON.parse(xmlhttp.responseText); + if ((typeof jsonresponse.FWVersion === "undefined")|| (typeof jsonresponse.Hostname === "undefined") || (typeof jsonresponse.WebUpdate === "undefined") || (typeof jsonresponse.WebSocketport === "undefined") || (typeof jsonresponse.WebCommunication === "undefined") || (typeof jsonresponse.Filesystem === "undefined") || (typeof jsonresponse.Authentication === "undefined")) { + error = true; + } else { + document.getElementById('FWVERSION').innerHTML = "v"+jsonresponse.FWVersion; + if (jsonresponse.Filesystem != "None"){ + filesystem = true; + console.log(jsonresponse.Filesystem); + } + + if (jsonresponse.WebUpdate != "Disabled"){ + webupdate = true; + console.log(jsonresponse.WebUpdate); + } + //websocket port + websocket_port = jsonresponse.WebSocketport; + console.log(websocket_port); + //async communications + if (jsonresponse.WebCommunication != "Synchronous") { + async_webcommunication = true; + console.log(jsonresponse.WebCommunication); + } + FWOk(); + startSocket(); + document.title = jsonresponse.Hostname; + if (filesystem)SendCommand('list','all'); + if (jsonresponse.Authentication != "Disabled"){ + authentication = true; + console.log(jsonresponse.Authentication); + } + } + } else if (xmlhttp.status == 401){ + RL(); + } else { + error = true; + console.log( xmlhttp.status); + } + if (error) { + FWError(); + } + } +}; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} + + +function startSocket(){ + if (websocket_started){ + ws_source.close(); + } + if(async_webcommunication){ + ws_source = new WebSocket('ws://'+document.location.host+'/ws',['arduino']); + } + else { + //console.log("Socket port is :" + websocket_port); + ws_source = new WebSocket('ws://'+document.location.hostname+':' + websocket_port,['arduino']); + } + ws_source.binaryType = "arraybuffer"; + ws_source.onopen = function(e){ + console.log("WS"); + websocket_started = true; + }; + ws_source.onclose = function(e){ + websocket_started = false; + console.log("~WS"); + //seems sometimes it disconnect so wait 3s and reconnect + //if it is not a log off + if(!log_off) setTimeout(startSocket, 3000); + }; + ws_source.onerror = function(e){ + console.log("WS", e); + }; + ws_source.onmessage = function(e){ + var msg = ""; + //bin + if(!(e.data instanceof ArrayBuffer)){ + msg = e.data; + var tval = msg.split(":"); + if (tval.length == 2) { + if (tval[0] == 'currentID') { + page_id = tval[1]; + console.log("ID " + page_id); + } + if (tval[0] == 'activeID') { + if(page_id != tval[1]) { + HideAll("It seems you are connect from another location, your are now disconnected"); + } + } + } + + } + //console.log(msg); + + }; +} + +window.onload = function() { +InitUI(); +}; + +function Uploadfile(){ +if (!confirm("Confirm Firmware Update ?"))return; +var files = document.getElementById('fw-select').files; +if (files.length==0)return; +document.getElementById('ubut').style.visibility = 'hidden'; +document.getElementById('fw-select').style.visibility = 'hidden'; +document.getElementById('msg').style.visibility = "visible"; +document.getElementById('msg').innerHTML=""; +document.getElementById('FILESYSTEM').style.display = "none"; +document.getElementById('prgfw').style.visibility = "visible"; +var formData = new FormData(); +for (var i4 = 0; i4 < files.length; i4++) { +var file = files[i4]; +var arg = "/" + file.name + "S"; + //append file size first to check updload is complete + formData.append(arg, file.size); + formData.append('myfile[]', file, "/"+file.name);} +var xmlhttp = new XMLHttpRequest(); +xmlhttp.open('POST', '/updatefw', true); +//progress upload event +xmlhttp.upload.addEventListener("progress", updateProgress, false); +//progress function +function updateProgress (oEvent) { + if (oEvent.lengthComputable) { + var percentComplete = (oEvent.loaded / oEvent.total)*100; + document.getElementById('prgfw').value=percentComplete; + document.getElementById('msg').innerHTML = "Uploading ..." + percentComplete.toFixed(0)+"%" ; + } else { + // Impossible because size is unknown + } +} +xmlhttp.onload = function () { + if (xmlhttp.status === 200) { +document.getElementById('ubut').value = 'Upload'; +document.getElementById('msg').innerHTML="Restarting, please wait...."; +document.getElementById('counter').style.visibility = "visible"; +document.getElementById('ubut').style.visibility = 'hidden'; +document.getElementById('ubut').style.width = '0px'; +document.getElementById('fw-select').value=""; +document.getElementById('fw-select').style.visibility = 'hidden'; +document.getElementById('fw-select').style.width = '0px'; + +var jsonresponse = JSON.parse(xmlhttp.responseText); +if (jsonresponse.status=='1' || jsonresponse.status=='4' || jsonresponse.status=='1')alert("Update failed"); +if (jsonresponse.status=='2')alert('Update canceled!'); +else if (jsonresponse.status=='3') +{ + var i5 = 0; + var interval; + var x = document.getElementById("prgfw"); + x.max=40; + interval = setInterval(function(){ + i5=i5+1; + var x = document.getElementById("prgfw"); + x.value=i5; + document.getElementById('counter').innerHTML=41-i5; + if (i5>40) + { + clearInterval(interval); + location.reload(); + } + },1000); +} +else alert('Update failed!'); + } else alert('An error occurred!'); +}; +xmlhttp.send(formData); +} + +function RL(){ + document.getElementById('loginpage').style.display='block'; +} + +function SLR (){ + document.getElementById('loginpage').style.display='none'; + var user = document.getElementById('lut').value.trim(); + var password = document.getElementById('lpt').value.trim(); + var url = "/login?USER="+encodeURIComponent(user) + "&PASSWORD=" + encodeURIComponent(password) + "&SUBMIT=yes" ; + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState == 4){ + if (xmlhttp.status != 200) { + if (xmlhttp.status == 401) { + RL(); + } else { + FWError(); + console.log(xmlhttp.status); + } + } else { + InitUI(); + } + } + }; +xmlhttp.open("GET", url, true); +xmlhttp.send(); +} diff --git a/embedded/www/tool.html b/embedded/www/tool.html new file mode 100644 index 00000000..cdaa4f15 --- /dev/null +++ b/embedded/www/tool.html @@ -0,0 +1,108 @@ + + + + + + ESP3D tool page + + + +
+

+
+
+
Flash Filesystem
+
+ +    +

+
+
+ + + + + + +
+ + +
+ + +
+
+
 
+
+ + + + + + + + + + + +
TypeNameSize
+
+ +
+
+
+
+
Firmware Update
+
+ + + + + + + +
+
+
+ + + + + + + + diff --git a/esp3d/esp3d.ino b/esp3d/esp3d.ino new file mode 100644 index 00000000..72d52bd8 --- /dev/null +++ b/esp3d/esp3d.ino @@ -0,0 +1,19 @@ +/* + ESP3D ino file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ diff --git a/esp3d/src/configuration.h b/esp3d/src/configuration.h new file mode 100644 index 00000000..83002c2c --- /dev/null +++ b/esp3d/src/configuration.h @@ -0,0 +1,147 @@ +/* + config.h - ESP3D configuration file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _CONFIGURATION_H +#define _CONFIGURATION_H +//FEATURES - comment to disable ////////////////////////////////////////////////////////// + +//SERIAL_COMMAND_FEATURE: allow to send command by serial +#define SERIAL_COMMAND_FEATURE + +//AUTHENTICATION_FEATURE: protect pages by login password +//#define AUTHENTICATION_FEATURE + +//WIFI_FEATURE : enable WIFI function +#define WIFI_FEATURE + +//ETH_FEATURE : enable Ethernet function +//#define ETH_FEATURE + +//BLUETOOTH_FEATURE : enable BT Serial function +//#define BLUETOOTH_FEATURE + +//HTTP_FEATURE : enable HTTP function +#define HTTP_FEATURE + +//TELNET_FEATURE : enable Telnet function +#define TELNET_FEATURE + +//WS_DATA_FEATURE: allow to connect serial from Websocket +//#define WS_DATA_FEATURE + +//ESP_OLED_FEATURE: allow oled screen output +//#define ESP_OLED_FEATURE + +//SDCARD_FEATURE: to access SD Card files directly instead of access by serial using printer Board FW +//#define SDCARD_FEATURE + +//FILESYSTEM_FEATURE: to host some files on flash +// 0 is SPIFFS +// 1 is FAT +// 2 is LittleFS //Not Yet implemented +#define FILESYSTEM_FEATURE 0 + +//DIRECT_PIN_FEATURE: allow to access pin using ESP201 command +#define DIRECT_PIN_FEATURE + +//FILESYSTEM_TIMESTAMP_FEATURE: allow to get last write time from FILESYSTEM files +//#define FILESYSTEM_TIMESTAMP_FEATURE + +//MDNS_FEATURE: this feature allow type the name defined +//in web browser by default: http:\\esp8266.local and connect +//need `bonjour` protocol on windows +#define MDNS_FEATURE + +//SSDP_FEATURE: this feature is a discovery protocol, supported on Windows out of the box +#define SSDP_FEATURE + +//CAPTIVE_PORTAL_FEATURE: In SoftAP redirect all unknow call to main page +#define CAPTIVE_PORTAL_FEATURE + +//OTA_FEATURE: this feature is arduino update over the air +#define OTA_FEATURE + +//WEB_UPDATE_FEATURE: allow to flash fw using web UI +#define WEB_UPDATE_FEATURE + + +//Extra features ///////////////////////////////////////////////////////////////////////// +/************************************ + * + * DEBUG + * + * **********************************/ +//Do not do this when connected to printer !!! +//be noted all upload may failed if enabled +//#define DEBUG_OUTPUT_SERIAL0 +//#define DEBUG_OUTPUT_SERIAL1 +//#define DEBUG_OUTPUT_SERIAL2 + +/************************************ + * + * Serial Communications + * + * **********************************/ +//which serial ESP use to communicate to printer (ESP32 has 3 serials available, ESP8266 only 2) +//Uncomment one only +#define USE_SERIAL_0 +//#define USE_SERIAL_1 +//For ESP32 Only +//#define USE_SERIAL_2 + +//Serial rx buffer size is 256 but can be extended +#define SERIAL_RX_BUFFER_SIZE 512 + +//Serial Parameters +#define ESP_SERIAL_PARAM SERIAL_8N1 + +//Serial Pins +//-1 means use default pins of your board what ever the serial you choose +// * UART 0 possible options are (1, 3), (2, 3) or (15, 13) +// * UART 1 allows only TX on 2 if UART 0 is not (2, 3) +#define ESP_RX_PIN -1 +#define ESP_TX_PIN -1 + +/************************************ + * + * Settings + * + * **********************************/ +#define SETTINGS_IN_EEPROM +//#define SETTINGS_IN_PREFERENCES + + +/************************************ + * + * Customize ESP3D + * + * **********************************/ +#if defined( ARDUINO_ARCH_ESP8266) +#define ESP_MODEL_NAME "ESP8266" +#define ESP_MODEL_URL "http://espressif.com/en/products/esp8266/" +#endif //ARDUINO_ARCH_ESP8266 +#if defined( ARDUINO_ARCH_ESP32) +#define ESP_MODEL_NAME "ESP32" +#define ESP_MODEL_URL "https://www.espressif.com/en/products/hardware/esp-wroom-32/overview" +#endif //ARDUINO_ARCH_ESP32 +#define ESP_MODEL_NUMBER "ESP3D 3.0" +#define ESP_MANUFACTURER_NAME "Espressif Systems" +#define ESP_MANUFACTURER_URL "http://espressif.com" + +#endif //_CONFIGURATION_H diff --git a/esp3d/src/core/commands.cpp b/esp3d/src/core/commands.cpp new file mode 100644 index 00000000..e24a5c78 --- /dev/null +++ b/esp3d/src/core/commands.cpp @@ -0,0 +1,378 @@ +/* + commands.cpp - ESP3D commands class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../include/esp3d_config.h" +#include "esp3d.h" +#include "commands.h" +#include "esp3doutput.h" +#include "settings_esp3d.h" + +Commands esp3d_commands; + +Commands::Commands() +{ +} +Commands::~Commands() +{ +} + +//dispatch the command +void Commands::process(uint8_t * sbuf, size_t len, ESP3DOutput * output, level_authenticate_type auth, ESP3DOutput * outputonly ) +{ + if(is_esp_command(sbuf,len)) { + + uint8_t cmd[4]; + cmd[0] = sbuf[4]; + cmd[1] = sbuf[5]; + cmd[2] = sbuf[6]; + cmd[3] = 0x0; + //log_esp3d("Authentication = %d client %d", auth, output->client()); + execute_internal_command (String((const char *)cmd).toInt(), (len > 8)?(const char*)&sbuf[8]:"", auth, (outputonly == nullptr)?output:outputonly); + } else { + //Dispatch to all clients but current or to define output + if ((output->client() == ESP_HTTP_CLIENT) && (outputonly == nullptr)) { + if (auth != LEVEL_GUEST) { + output->printMSG(""); + } else { + output->printERROR("Wrong authentication!", 401); + return; + } + } + if (outputonly == nullptr) { + output->dispatch(sbuf, len); + } else { + outputonly->write(sbuf, len); + } + } +} + +//check if current line is an [ESPXXX] command +bool Commands::is_esp_command(uint8_t * sbuf, size_t len) +{ + if (len < 8) { + return false; + } + if ((char(sbuf[0]) == '[') && (char(sbuf[1]) == 'E') && (char(sbuf[2]) == 'S') && (char(sbuf[3]) == 'P') && (char(sbuf[7]) == ']')) { + return true; + } + return false; +} + +//find space in string +//if space is has \ before it is ignored +int Commands::get_space_pos(const char * string, uint from) +{ + uint len = strlen(string); + if (len < from) { + return -1; + } + for (uint i = from; i < len; i++) { + if (string[i] == ' ') { + //if it is first char + if (i == from) { + return from; + } + //if not first one and previous char is not '\' + if (string[i-1] != '\\') { + return (i); + } + } + } + return -1; +} + +//extract parameter with corresponding label +//if label is empty give whole line without authentication label/parameter +const char * Commands::get_param (const char * cmd_params, const char * label) +{ + static String res; + res = ""; + int start = 1; + int end = -1; + String tmp = ""; + String slabel = " "; + res = cmd_params; + res.replace("\r ", ""); + res.replace("\n ", ""); + if (res.length() == 0) { + return res.c_str(); + } + res.trim(); + tmp = " " + res; + slabel += label; + if (strlen(label) > 0) { + start = tmp.indexOf(slabel); + if (start == -1) { + return res.c_str(); + } + start+=slabel.length(); + end = get_space_pos(tmp.c_str(),start); + } + if (end == -1) { + end = tmp.length(); + } + //extract parameter + res = tmp.substring (start, end); + + +#ifdef AUTHENTICATION_FEATURE + //if no label remove authentication parameters + if (strlen(label) == 0) { + + tmp = " " + res; + start = tmp.indexOf (" pwd="); + if (start != -1) { + end = get_space_pos(tmp.c_str(),start+1); + res = ""; + if (start != 0) { + res = tmp.substring(0, start); + } + if (end != -1) { + res += " " + tmp.substring(end+1, tmp.length()); + } + } + } +#endif //AUTHENTICATION_FEATURE + //remove space format + res.replace("\\ ", " "); + //be sure no extra space + res.trim(); + return res.c_str(); + +} + +bool Commands::hastag (const char * cmd_params, const char *tag) +{ + String tmp = ""; + String stag = " "; + if ((strlen(cmd_params) == 0) || (strlen(tag) == 0)) { + return false; + } + stag += tag; + tmp = cmd_params; + tmp.trim(); + tmp = " " + tmp; + if (tmp.indexOf(stag) == -1) { + return false; + } + return true; +} + + +//execute internal command +bool Commands::execute_internal_command (int cmd, const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output) +{ +#ifndef SERIAL_COMMAND_FEATURE + if (output->client() == ESP_SERIAL_CLIENT) { + output->printMSG("Feature disabled"); + return false; + } +#endif //SERIAL_COMMAND_FEATURE + bool response = true; + level_authenticate_type auth_type = auth_level; + //log_esp3d("Authentication = %d", auth_type); +//override if parameters +#ifdef AUTHENTICATION_FEATURE + + //do not overwrite previous authetication level + if (auth_type == LEVEL_GUEST) { + String pwd=get_param (cmd_params, "pwd="); + auth_type = AuthenticationService::authenticated_level(pwd.c_str()); + } +#endif //AUTHENTICATION_FEATURE + //log_esp3d("Authentication = %d", auth_type); + String parameter; + switch (cmd) { +#if defined (WIFI_FEATURE) + //STA SSID + //[ESP100][pwd=] + case 100: + response = ESP100(cmd_params, auth_type, output); + break; + //STA Password + //[ESP101][pwd=] + case 101: + response = ESP101(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + //Change STA IP mode (DHCP/STATIC) + //[ESP102]pwd= + case 102: + response = ESP102(cmd_params, auth_type, output); + break; + //Change STA IP/Mask/GW + //[ESP103]IP= MSK= GW= pwd= + case 103: + response = ESP103(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE ||ETH_FEATURE +#if defined (WIFI_FEATURE) + //AP SSID + //[ESP105][pwd=] + case 105: + response = ESP105(cmd_params, auth_type, output); + break; + //AP Password + //[ESP106][pwd=] + case 106: + response = ESP106(cmd_params, auth_type, output); + break; + //Change AP IP + //[ESP107] pwd= + case 107: + response = ESP107(cmd_params, auth_type, output); + break; + //Change AP channel + //[ESP108]pwd= + case 108: + response = ESP108(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE + +#if defined( WIFI_FEATURE) || defined( BLUETOOTH_FEATURE) || defined (ETH_FEATURE) + //Set radio state at boot which can be BT, WIFI-STA, WIFI-AP, ETH-STA, OFF + //[ESP110]pwd= + case 110: + response = ESP110(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE || BLUETOOTH_FEATURE || ETH_FEATURE) + +#if defined(WIFI_FEATURE) || defined (ETH_FEATURE) + //Get current IP + //[ESP111] + case 111: + response = ESP111(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE || ETH_FEATURE) + +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) || defined(BT_FEATURE) + //Get/Set hostname + //[ESP112] pwd= + case 112: + response = ESP112(cmd_params, auth_type, output); + break; + //Get/Set immediate Network (WiFi/BT/Ethernet) state which can be ON, OFF + //[ESP115]pwd= + case 115: + response = ESP115(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE|| ETH_FEATURE || BT_FEATURE + +#ifdef HTTP_FEATURE + //Set HTTP state which can be ON, OFF + //[ESP120]pwd= + case 120: + response = ESP120(cmd_params, auth_type, output); + break; + //Set HTTP port + //[ESP121]pwd= + case 121: + response = ESP121(cmd_params, auth_type, output); + break; +#endif HTTP_FEATURE +#ifdef TELNET_FEATURE + //Set TELNET state which can be ON, OFF + //[ESP130]pwd= + case 130: + response = ESP130(cmd_params, auth_type, output); + break; + //Set TELNET port + //[ESP131]pwd= + case 131: + response = ESP131(cmd_params, auth_type, output); + break; +#endif //TELNET_FEATURE + +#ifdef DIRECT_PIN_FEATURE + //Get/Set pin value + //[ESP201]P V [PULLUP=YES RAW=YES]pwd= + case 201: + response = ESP201(cmd_params, auth_type, output); + break; +#endif //DIRECT_PIN_FEATURE + + //Get full ESP3D settings + //[ESP400] + case 400: + response = ESP400(cmd_params, auth_type, output); + break; + //Set EEPROM setting + //[ESP401]P= T= V= pwd= + case 401: + response = ESP401(cmd_params, auth_type, output); + break; +#if defined (WIFI_FEATURE) + //Get available AP list (limited to 30) + //output is JSON or plain text according parameter + //[ESP410] + case 410: + response = ESP410(cmd_params, auth_type, output); + break; +#endif //WIFI_FEATURE + //Get ESP current status + //output is JSON or plain text according parameter + //[ESP420] + case 420: + response = ESP420(cmd_params, auth_type, output); + break; + //Set ESP State + //cmd are RESTART / RESET + //[ESP444] + case 444: + response = ESP444(cmd_params, auth_type, output); + break; +#ifdef AUTHENTICATION_FEATURE + //Change admin password + //[ESP550]pwd= + case 550: + response = ESP550(cmd_params, auth_type, output); + break; + //Change user password + //[ESP555]pwd= + case 555: + response = ESP555(cmd_params, auth_type, output); + break; +#endif //AUTHENTICATION_FEATURE +#ifdef FILESYSTEM_FEATURE + //Format ESP Filesystem + //[ESP710]FORMAT pwd= + case 710: + response = ESP710(cmd_params, auth_type, output); + break; + + //List ESP Filesystem + //[ESP720] pwd= + case 720: + response = ESP720(cmd_params, auth_type, output); + break; +#endif //FILESYSTEM_FEATURE + + //get fw version firmare target and fw version + //output is JSON or plain text according parameter + //[ESP800] + case 800: + response = ESP800(cmd_params, auth_type, output); + break; + default: + output->printERROR ("Invalid Command"); + response = false; + } + return response; +} diff --git a/esp3d/src/core/commands.h b/esp3d/src/core/commands.h new file mode 100644 index 00000000..dd8d3b33 --- /dev/null +++ b/esp3d/src/core/commands.h @@ -0,0 +1,93 @@ +/* + commands.h - ESP3D commands class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef COMMANDS_H +#define COMMANDS_H +#include +#include "../modules/authentication/authentication_service.h" +class ESP3DOutput; + +class Commands +{ +public: + Commands(); + ~Commands(); + void process(uint8_t * sbuf, size_t len, ESP3DOutput * output, level_authenticate_type auth = LEVEL_GUEST, ESP3DOutput * outputonly = nullptr); + bool is_esp_command(uint8_t * sbuf, size_t len); + bool execute_internal_command(int cmd, const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + int get_space_pos(const char * string, uint from = 0); + const char* get_param (const char * cmd_params, const char * label); + bool hastag (const char * cmd_params, const char * tag); +#if defined (WIFI_FEATURE) + bool ESP100(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP101(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + bool ESP102(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP103(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE ||ETH_FEATURE +#if defined (WIFI_FEATURE) + bool ESP105(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP106(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP107(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP108(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE +#if defined( WIFI_FEATURE) || defined( BLUETOOTH_FEATURE) || defined (ETH_FEATURE) + bool ESP110(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE || BLUETOOTH_FEATURE || ETH_FEATURE +#if defined( WIFI_FEATURE) || defined (ETH_FEATURE) + bool ESP111(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) || defined(BT_FEATURE) + bool ESP112(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP115(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE || BLUETOOTH_FEATURE || ETH_FEATURE +#if defined(HTTP_FEATURE) + bool ESP120(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP121(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //HTTP_FEATURE +#if defined(TELNET_FEATURE) + bool ESP130(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP131(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //TELNET_FEATURE +#ifdef DIRECT_PIN_FEATURE + bool ESP201(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //DIRECT_PIN_FEATURE + bool ESP400(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP401(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#if defined (WIFI_FEATURE) + bool ESP410(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //WIFI_FEATURE + bool ESP420(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP444(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#if defined (AUTHENTICATION_FEATURE) + bool ESP550(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP555(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //AUTHENTICATION_FEATURE +#if defined(FILESYSTEM_FEATURE) + bool ESP710(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); + bool ESP720(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +#endif //FILESYSTEM_FEATURE + bool ESP800(const char* cmd_params, level_authenticate_type auth_level, ESP3DOutput * output); +}; + +extern Commands esp3d_commands; + +#endif //COMMANDS_H diff --git a/esp3d/src/core/debug_esp3d.cpp b/esp3d/src/core/debug_esp3d.cpp new file mode 100644 index 00000000..f6bce210 --- /dev/null +++ b/esp3d/src/core/debug_esp3d.cpp @@ -0,0 +1,39 @@ +/* + debug_esp3d.cpp - debug esp3d functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../include/esp3d_config.h" + +#if defined(ARDUINO_ARCH_ESP8266) && (defined(DEBUG_OUTPUT_SERIAL0) || defined(DEBUG_OUTPUT_SERIAL1) || defined(DEBUG_OUTPUT_SERIAL2)) +#include +const char * pathToFileName(const char * path) +{ + size_t i = 0; + size_t pos = 0; + char * p = (char *)path; + while(*p) { + i++; + if(*p == '/' || *p == '\\') { + pos = i; + } + p++; + } + return path+pos; +} +#endif //ARDUINO_ARCH_ESP8266 + DEBUG SERIAL0/1/2 diff --git a/esp3d/src/core/debug_esp3d.h b/esp3d/src/core/debug_esp3d.h new file mode 100644 index 00000000..f3a3243c --- /dev/null +++ b/esp3d/src/core/debug_esp3d.h @@ -0,0 +1,53 @@ +/* + debug_esp3d.h - esp3d debug functions + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _DEBUG_ESP3D_H +#define _DEBUG_ESP3D_H + +#include "../include/esp3d_config.h" +#define log_esp3d(format, ...) +#define log_esp3dS(format, ...) +#define DEBUG_ESP3D_INIT +#undef DEBUG_ESP3D + +//Serial +#if defined(DEBUG_OUTPUT_SERIAL0) || defined(DEBUG_OUTPUT_SERIAL1) || defined(DEBUG_OUTPUT_SERIAL2) +#if defined(ARDUINO_ARCH_ESP8266) +extern const char * pathToFileName(const char * path); +#endif //ARDUINO_ARCH_ESP8266 +#undef DEBUG_ESP3D_INIT +#undef log_esp3d +#undef log_esp3dS +#define DEBUG_ESP3D +#ifdef DEBUG_OUTPUT_SERIAL0 +#define DEBUG_OUTPUT_SERIAL Serial +#endif //DEBUG_OUTPUT_SERIAL0 +#ifdef DEBUG_OUTPUT_SERIAL1 +#define DEBUG_OUTPUT_SERIAL Serial1 +#endif //DEBUG_OUTPUT_SERIAL1 +#ifdef DEBUG_OUTPUT_SERIAL2 +#define DEBUG_OUTPUT_SERIAL Serial2 +#endif //DEBUG_OUTPUT_SERIAL2 +#define DEBUG_ESP3D_INIT DEBUG_OUTPUT_SERIAL.begin(115200); +#define log_esp3d(format, ...) DEBUG_OUTPUT_SERIAL.printf("[ESP3D][%s:%u] %s(): " format "\r\n", pathToFileName(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define log_esp3dS(format, ...) DEBUG_OUTPUT_SERIAL.printf(format "\r\n", ##__VA_ARGS__) +#endif //defined(DEBUG_OUTPUT_SERIAL0) || defined(DEBUG_OUTPUT_SERIAL1) || defined(DEBUG_OUTPUT_SERIAL2) + +#endif //_DEBUG_ESP3D_H diff --git a/esp3d/src/core/esp3d.cpp b/esp3d/src/core/esp3d.cpp new file mode 100644 index 00000000..a83f36c4 --- /dev/null +++ b/esp3d/src/core/esp3d.cpp @@ -0,0 +1,151 @@ +/* + This file is part of ESP3D Firmware for 3D printer. + + ESP3D Firmware for 3D printer is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ESP3D Firmware for 3D printer is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this Firmware. If not, see . + + This firmware is using the standard arduino IDE with module to support ESP8266/ESP32: + https://github.com/esp8266/Arduino + https://github.com/espressif/arduino-esp32 + + Latest version of the code and documentation can be found here : + https://github.com/luc-github/ESP3D + + Main author: luc lebosse + +*/ +#include "esp3d.h" +#include "../include/esp3d_config.h" +#include "settings_esp3d.h" +#include "../modules/serial/serial_service.h" +#if defined (WIFI_FEATURE) || defined(ETH_FEATURE) +#include "../modules/network/netconfig.h" +#endif //WIFI_FEATURE || ETH FEATURE +#if defined(FILESYSTEM_FEATURE) +#include "../modules/filesystem/esp_filesystem.h" +#endif //FILESYSTEM_FEATURE + +#include "esp3doutput.h" + +bool Esp3D::restart = false; + +//Contructor +Esp3D::Esp3D() +{ + +} + +//Destructor +Esp3D::~Esp3D() +{ + end(); +} + +//Begin which setup everything +bool Esp3D::begin(uint16_t startdelayms, uint16_t recoverydelayms) +{ + Hal::begin(); + //delay() to avoid to disturb printer + delay(startdelayms); + (void)recoverydelayms; + DEBUG_ESP3D_INIT + log_esp3d("Mode %d", WiFi.getMode()); + if (!Settings_ESP3D::begin()) { + log_esp3d("Need reset settings"); + reset(); + //Restart ESP3D + restart_esp(); + } + //init output + ESP3DOutput::isOutput(ESP_ALL_CLIENTS, true); + //BT do not start automaticaly so should be OK + //Serial service + if (!serial_service.begin()) { + log_esp3d("Error with serial service"); + return false; + } + //Setup Filesystem +#if defined(FILESYSTEM_FEATURE) + if (!ESP_FileSystem::begin()) { + log_esp3d("Error with filesystem service"); + return false; + } +#endif //FILESYSTEM_FEATURE + //Setup Network +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) + if (!NetConfig::begin()) { + log_esp3d("Error setup network"); + return false; + } +#endif //WIFI_FEATURE + return true; +} + +//Process which handle all input +void Esp3D::handle() +{ + //if need restart + if (restart) { + restart_now(); + } + serial_service.handle(); +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) + NetConfig::handle(); +#endif //WIFI_FEATURE || ETH_FEATURE +} + +//End ESP3D +bool Esp3D::end() +{ +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) + NetConfig::end(); +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined(FILESYSTEM_FEATURE) + ESP_FileSystem::end(); +#endif //FILESYSTEM_FEATURE + serial_service.end(); + return true; +} + +//Reset ESP3D settings +bool Esp3D::reset() +{ + bool resetOk = true; + if (!serial_service.reset()) { + resetOk = false; + log_esp3d("Reset serial error"); + } + if (!Settings_ESP3D::reset()) { + log_esp3d("Reset settings error"); + resetOk = false; + } + return resetOk; +} + +//Set Restart flag +void Esp3D::restart_esp(bool need_restart) +{ + restart = need_restart; +} + +void Esp3D::restart_now() +{ + log_esp3d("Restarting"); + end(); + serial_service.swap(); + ESP.restart(); + while (1) { + delay (1); + }; +} + diff --git a/esp3d/src/core/esp3d.h b/esp3d/src/core/esp3d.h new file mode 100644 index 00000000..72210d2e --- /dev/null +++ b/esp3d/src/core/esp3d.h @@ -0,0 +1,42 @@ +/* + esp3d.h - esp3d class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ESP3D_H +#define _ESP3D_H +//be sure correct IDE and settings are used for ESP8266 or ESP32 +#if !(defined( ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)) +#error Oops! Make sure you have 'ESP8266 or ESP32' compatible board selected from the 'Tools -> Boards' menu. +#endif // ARDUINO_ARCH_ESP8266 + ARDUINO_ARCH_ESP32 +#include +class Esp3D +{ +public: + Esp3D(); + ~Esp3D(); + bool begin(uint16_t startdelayms = 500, uint16_t recoverydelayms = 0); + void handle(); + bool end(); + static bool reset(); + static void restart_esp(bool need_restart = true); +private: + static bool restart; + void restart_now(); +}; +#endif //_ESP3D_H diff --git a/esp3d/src/core/esp3doutput.cpp b/esp3d/src/core/esp3doutput.cpp new file mode 100644 index 00000000..868b1bff --- /dev/null +++ b/esp3d/src/core/esp3doutput.cpp @@ -0,0 +1,368 @@ +/* + serial_service.cpp - serial services functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../include/esp3d_config.h" +#include "esp3doutput.h" +#include "../modules/serial/serial_service.h" +#include "settings_esp3d.h" +#if defined (HTTP_FEATURE) || defined(WS_DATA_FEATURE) +#include "../modules/websocket/websocket_server.h" +#endif //HTTP_FEATURE || WS_DATA_FEATURE +#if defined (BLUETOOTH_FEATURE) +#include "../modules/bluetooth/BT_service.h" +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) +#include "../modules/telnet/telnet_server.h" +#endif //TELNET_FEATURE +uint8_t ESP3DOutput::_outputflags = ESP_ALL_CLIENTS; +#if defined (HTTP_FEATURE) +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#endif //HTTP_FEATURE + + + +//constructor +ESP3DOutput::ESP3DOutput(uint8_t client) +{ + _client = client; + +#ifdef HTTP_FEATURE + _code = 200; + _headerSent = false; + _footerSent = false; + _webserver = nullptr; +#endif //HTTP_FEATURE +} + +#ifdef HTTP_FEATURE +//constructor +ESP3DOutput::ESP3DOutput(WEBSERVER * webserver) +{ + _client = ESP_HTTP_CLIENT; + _code = 200; + _headerSent = false; + _footerSent = false; + _webserver = webserver; +} +#endif //HTTP_FEATURE + +//destructor +ESP3DOutput::~ESP3DOutput() +{ + flush(); +} + +bool ESP3DOutput::isOutput(uint8_t flag, bool fromsettings) +{ + if(fromsettings) { + _outputflags = Settings_ESP3D::read_byte (ESP_OUTPUT_FLAG); + } + return ((_outputflags & flag) == flag); + +} + +size_t ESP3DOutput::dispatch (uint8_t * sbuf, size_t len) +{ + log_esp3d("Dispatch %d to %d", len, _client); + if (_client != ESP_SERIAL_CLIENT) { + if (isOutput(ESP_SERIAL_CLIENT)) { + serial_service.write(sbuf, len); + } + } +#if defined (HTTP_FEATURE) //no need to block it never + if (!((_client == ESP_WEBSOCKET_TERMINAL_CLIENT) || (_client == ESP_HTTP_CLIENT))) { + if (websocket_terminal_server) { + websocket_terminal_server.write(sbuf, len); + } + } +#endif //HTTP_FEATURE +#if defined (BLUETOOTH_FEATURE) + if (_client != ESP_BT_CLIENT) { + if (isOutput(ESP_BT_CLIENT) && bt_service.started()) { + bt_service.write(sbuf, len); + } + } +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + if (_client != ESP_TELNET_CLIENT) { + if (isOutput(ESP_TELNET_CLIENT) && telnet_server.started()) { + telnet_server.write(sbuf, len); + } + } +#endif //TELNET_FEATURE + return len; +} + +//Flush +void ESP3DOutput::flush() +{ + if (!isOutput(_client)) { + return ; + } + switch (_client) { + case ESP_SERIAL_CLIENT: + serial_service.flush(); + break; + case ESP_HTTP_CLIENT: +#ifdef HTTP_FEATURE + if (_webserver) { + if (_headerSent && !_footerSent) { + _webserver->sendContent(""); + _footerSent = true; + } + } +#endif //HTTP_FEATURE + break; +#if defined (BLUETOOTH_FEATURE) + case ESP_BT_CLIENT: + bt_service.flush(); + break; +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + case ESP_TELNET_CLIENT: + telnet_server.flush(); + break; +#endif //TELNET_FEATURE + case ESP_ALL_CLIENTS: + //do nothing because there are side effects + break; + default : + break; + } +} + +size_t ESP3DOutput::printLN(const char * s) +{ + if (!isOutput(_client)) { + return 0; + } + if (_client == ESP_TELNET_CLIENT) { + print(s); + println("\r"); + return strlen(s)+2; + } else { + return println(s); + } +} + +size_t ESP3DOutput::printMSG(const char * s) +{ + if (!isOutput(_client)) { + return 0; + } + String display; + if (_client == ESP_HTTP_CLIENT) { +#ifdef HTTP_FEATURE + if (_webserver) { + if (!_headerSent && !_footerSent) { + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send (_code, "text/plain", s); + _headerSent = true; + _footerSent = true; + return strlen(s); + } + } + return 0; + } + +#endif //HTTP_FEATURE + if (_client == ESP_PRINTER_LCD_CLIENT) { + display = "M117 "; + display+= s; + return printLN(display.c_str()); + } + switch(Settings_ESP3D::GetFirmwareTarget()) { + case GRBL: + display = "[MSG:"; + display += s; + display += "]"; + break; + case MARLIN: + case MARLINKIMBRA: + display = "echo: "; + display += s; + break; + case REPETIER4DV: + case SMOOTHIEWARE: + case REPETIER: + default: + display = ";"; + display += s; + } + return printLN(display.c_str()); +} + +size_t ESP3DOutput::printERROR(const char * s, int code_error) +{ + if (!isOutput(_client)) { + return 0; + } + _code = code_error; + if (_client == ESP_HTTP_CLIENT) { +#ifdef HTTP_FEATURE + if (_webserver) { + if (!_headerSent && !_footerSent) { + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send (_code, "text/plain", s); + _headerSent = true; + _footerSent = true; + return strlen(s); + } + } + +#endif //HTTP_FEATURE + return 0; + } + String display; + switch(Settings_ESP3D::GetFirmwareTarget()) { + case GRBL: + + display = "error: "; + display += s; + break; + case MARLIN: + case MARLINKIMBRA: + display = "error: "; + display += s; + break; + case REPETIER4DV: + case SMOOTHIEWARE: + case REPETIER: + default: + display = ";error: "; + display += s; + } + return printLN(display.c_str()); +} + +int ESP3DOutput::availableforwrite() +{ + switch (_client) { + case ESP_SERIAL_CLIENT: + return serial_service.availableForWrite(); +#if defined (BLUETOOTH_FEATURE) + case ESP_BT_CLIENT: + bt_service.availableForWrite(); + break; +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + case ESP_TELNET_CLIENT: + telnet_server.availableForWrite(); + break; +#endif //TELNET_FEATURE + case ESP_ALL_CLIENTS: + return serial_service.availableForWrite(); + default : + return 0; + } +} +size_t ESP3DOutput::write(uint8_t c) +{ + if (!isOutput(_client)) { + return 0; + } + switch (_client) { + case ESP_SERIAL_CLIENT: + return serial_service.write(c); +#if defined (BLUETOOTH_FEATURE) + case ESP_BT_CLIENT: + if(bt_service.started()) { + return bt_service.write(c); + } +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + case ESP_TELNET_CLIENT: + return telnet_server.write(c); +#endif //TELNET_FEATURE + case ESP_ALL_CLIENTS: +#if defined (BLUETOOTH_FEATURE) + if(bt_service.started()) { + bt_service.write(c); + } +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + if(telnet_server.started()) { + telnet_server.write(c); + } +#endif //TELNET_FEATURE + return serial_service.write(c); + default : + return 0; + } +} + +size_t ESP3DOutput::write(const uint8_t *buffer, size_t size) +{ + if (!isOutput(_client)) { + return 0; + } + switch (_client) { + case ESP_HTTP_CLIENT: +#ifdef HTTP_FEATURE + if (_webserver) { + if (!_headerSent && !_footerSent) { + _webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + _webserver->sendHeader("Content-Type","text/html"); + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(_code); + _headerSent = true; + } + if (_headerSent && !_footerSent) { + _webserver->sendContent_P((const char*)buffer,size); + } + } +#endif //HTTP_FEATURE + break; +#if defined (BLUETOOTH_FEATURE) + case ESP_BT_CLIENT: + if(bt_service.started()) { + return bt_service.write(buffer, size); + } +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + case ESP_TELNET_CLIENT: + if(telnet_server.started()) { + return telnet_server.write(buffer, size); + } +#endif //TELNET_FEATURE + case ESP_PRINTER_LCD_CLIENT: + case ESP_SERIAL_CLIENT: + return serial_service.write(buffer, size); + case ESP_ALL_CLIENTS: +#if defined (BLUETOOTH_FEATURE) + if(bt_service.started()) { + bt_service.write(buffer, size); + } +#endif //BLUETOOTH_FEATURE +#if defined (TELNET_FEATURE) + if(telnet_server.started()) { + telnet_server.write(buffer, size); + } +#endif //TELNET_FEATURE + return serial_service.write(buffer, size); + default : + return 0; + } +} diff --git a/esp3d/src/core/esp3doutput.h b/esp3d/src/core/esp3doutput.h new file mode 100644 index 00000000..6d216fee --- /dev/null +++ b/esp3d/src/core/esp3doutput.h @@ -0,0 +1,102 @@ +/* + esp3Doutput.h - out functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#define ESP_NO_CLIENT 0 +#define ESP_SERIAL_CLIENT 1 +#define ESP_TELNET_CLIENT 2 +#define ESP_HTTP_CLIENT 4 +#define ESP_WEBSOCKET_TERMINAL_CLIENT 8 +#define ESP_PRINTER_LCD_CLIENT 16 +#define ESP_BT_CLIENT 32 +#define ESP_OLED_CLIENT 64 +#define ESP_WEBSOCKET_CLIENT 128 +#define ESP_ALL_CLIENTS 255 + +#ifndef _ESP3DOUTPUT_H +#define _ESP3DOUTPUT_H + +#include "Print.h" +#include "../include/esp3d_config.h" +#ifdef HTTP_FEATURE +#if defined (ARDUINO_ARCH_ESP32) +class WebServer; +#define WEBSERVER WebServer +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +class ESP8266WebServer; +#define WEBSERVER ESP8266WebServer +#endif //ARDUINO_ARCH_ESP8266 +#endif //HTTP_FEATURE + +class ESP3DOutput : public Print +{ +public: + ESP3DOutput(uint8_t client = 0); +#ifdef HTTP_FEATURE + ESP3DOutput(WEBSERVER * webserver); +#endif //HTTP_FEATURE + ~ESP3DOutput(); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size); + + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + uint8_t client() + { + return _client; + } + size_t dispatch (uint8_t * sbuf, size_t len); + size_t printMSG(const char * s); + size_t printERROR(const char * s, int code_error = 200); + size_t printLN(const char * s); + void flush(); + int availableforwrite(); + static bool isOutput(uint8_t flag, bool fromsettings = false); +private: + uint8_t _client; +#ifdef HTTP_FEATURE + int _code; + bool _headerSent; + bool _footerSent; + WEBSERVER * _webserver; +#endif //HTTP_FEATURE + static uint8_t _outputflags; +}; + +#endif //_ESP3DOUTPUT_H + diff --git a/esp3d/src/core/espcmd/ESP100.cpp b/esp3d/src/core/espcmd/ESP100.cpp new file mode 100644 index 00000000..f7afcce4 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP100.cpp @@ -0,0 +1,65 @@ +/* + ESP100.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//STA SSID +//[ESP100][pwd=] +bool Commands::ESP100(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG(Settings_ESP3D::read_string(ESP_STA_SSID)); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if (!WiFiConfig::isSSIDValid (parameter.c_str() ) ) { + output->printERROR ("Incorrect SSID!"); + response = false; + } else { + if(!Settings_ESP3D::write_string(ESP_STA_SSID, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP101.cpp b/esp3d/src/core/espcmd/ESP101.cpp new file mode 100644 index 00000000..de2ce7df --- /dev/null +++ b/esp3d/src/core/espcmd/ESP101.cpp @@ -0,0 +1,54 @@ +/* + ESP101.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//STA Password +//[ESP101][pwd=] +bool Commands::ESP101(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + if (!WiFiConfig::isPasswordValid (parameter.c_str() ) ) { + output->printERROR ("Incorrect password!"); + response = false; + } else { + if(!Settings_ESP3D::write_string(ESP_STA_PASSWORD, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP102.cpp b/esp3d/src/core/espcmd/ESP102.cpp new file mode 100644 index 00000000..f61e4fbd --- /dev/null +++ b/esp3d/src/core/espcmd/ESP102.cpp @@ -0,0 +1,80 @@ +/* + ESP102.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#if defined (WIFI_FEATURE) +#include "../../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) +#include "../../modules/ethernet/ethconfig.h" +#endif //ETH_FEATURE +#include "../../modules/authentication/authentication_service.h" +//Change STA IP mode (DHCP/STATIC) +//[ESP102]pwd= +bool Commands::ESP102(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + int8_t resp = Settings_ESP3D::read_byte(ESP_STA_IP_MODE); + if (resp == DHCP_MODE) { + output->printMSG("DHCP"); + } else if (resp == STATIC_IP_MODE) { + output->printMSG("STATIC"); + } else { + output->printMSG("???"); + } + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter.toUpperCase(); + if (!((parameter == "STATIC") || (parameter == "DHCP"))) { + output->printERROR ("only STATIC or DHCP mode supported!"); + return false; + } else { + uint8_t bbuf = (parameter == "DHCP")?DHCP_MODE:STATIC_IP_MODE; + if (!Settings_ESP3D::write_byte(ESP_STA_IP_MODE, bbuf)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //WIFI_FEATURE || ETH_FEATURE diff --git a/esp3d/src/core/espcmd/ESP103.cpp b/esp3d/src/core/espcmd/ESP103.cpp new file mode 100644 index 00000000..598accbd --- /dev/null +++ b/esp3d/src/core/espcmd/ESP103.cpp @@ -0,0 +1,90 @@ +/* + ESP103.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#if defined (WIFI_FEATURE) +#include "../../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) +#include "../../modules/ethernet/ethconfig.h" +#endif //ETH_FEATURE +#include "../../modules/authentication/authentication_service.h" +//Change STA IP/Mask/GW +//[ESP103]IP= MSK= GW= pwd= +bool Commands::ESP103(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + String res = "IP:"; + res += Settings_ESP3D::read_IP_String(ESP_STA_IP_VALUE); + res += ", GW:"; + res += Settings_ESP3D::read_IP_String(ESP_STA_GATEWAY_VALUE); + res += ", MSK:"; + res += Settings_ESP3D::read_IP_String(ESP_STA_MASK_VALUE); + output->printMSG (res.c_str()); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + + String IP = get_param (cmd_params, "IP="); + String GW = get_param (cmd_params, "GW="); + String MSK = get_param (cmd_params, "MSK="); + if ( !NetConfig::isValidIP(IP.c_str())) { + output->printERROR ("Incorrect IP!"); + return false; + } + if ( !NetConfig::isValidIP(GW.c_str())) { + output->printERROR ("Incorrect gateway!"); + return false; + } + if ( !NetConfig::isValidIP(MSK.c_str())) { + output->printERROR ("Incorrect mask!"); + return false; + } + if ( !Settings_ESP3D::write_IP_String(ESP_STA_IP_VALUE, IP.c_str()) || + !Settings_ESP3D::write_IP_String(ESP_STA_GATEWAY_VALUE, GW.c_str()) || + !Settings_ESP3D::write_IP_String(ESP_STA_MASK_VALUE, MSK.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //WIFI_FEATURE || ETH_FEATURE diff --git a/esp3d/src/core/espcmd/ESP105.cpp b/esp3d/src/core/espcmd/ESP105.cpp new file mode 100644 index 00000000..90429219 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP105.cpp @@ -0,0 +1,66 @@ +/* + ESP105.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//AP SSID +//[ESP105][pwd=] +bool Commands::ESP105(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG(Settings_ESP3D::read_string(ESP_AP_SSID)); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if (!WiFiConfig::isSSIDValid (parameter.c_str() ) ) { + output->printERROR ("Incorrect SSID!"); + response = false; + } else { + if(!Settings_ESP3D::write_string(ESP_AP_SSID, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP106.cpp b/esp3d/src/core/espcmd/ESP106.cpp new file mode 100644 index 00000000..109f738f --- /dev/null +++ b/esp3d/src/core/espcmd/ESP106.cpp @@ -0,0 +1,54 @@ +/* + ESP106.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//AP Password +//[ESP106][pwd=] +bool Commands::ESP106(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + if (!WiFiConfig::isPasswordValid (parameter.c_str() ) ) { + output->printERROR ("Incorrect password!"); + response = false; + } else { + if(!Settings_ESP3D::write_string(ESP_AP_PASSWORD, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP107.cpp b/esp3d/src/core/espcmd/ESP107.cpp new file mode 100644 index 00000000..372115cf --- /dev/null +++ b/esp3d/src/core/espcmd/ESP107.cpp @@ -0,0 +1,65 @@ +/* + ESP107.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/network/netconfig.h" +#include "../../modules/authentication/authentication_service.h" +//Change AP IP +//[ESP107] pwd= +bool Commands::ESP107(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG (Settings_ESP3D::read_IP_String(ESP_AP_IP_VALUE).c_str()); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if ( !NetConfig::isValidIP(parameter.c_str())) { + output->printERROR ("Incorrect IP!"); + return false; + } + if ( !Settings_ESP3D::write_IP_String(ESP_AP_IP_VALUE, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP108.cpp b/esp3d/src/core/espcmd/ESP108.cpp new file mode 100644 index 00000000..9844e2c1 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP108.cpp @@ -0,0 +1,65 @@ +/* + ESP108.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//Change AP channel +//[ESP108]pwd= +bool Commands::ESP108(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG(String(Settings_ESP3D::read_byte (ESP_AP_CHANNEL)).c_str()); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + int8_t bbuf = parameter.toInt(); + if ((bbuf > Settings_ESP3D::get_max_byte (ESP_AP_CHANNEL)) || (bbuf < Settings_ESP3D::get_min_byte (ESP_AP_CHANNEL))) { + output->printERROR ("Incorrect channel!"); + return false; + } + if (!Settings_ESP3D::write_byte (ESP_AP_CHANNEL, bbuf)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP110.cpp b/esp3d/src/core/espcmd/ESP110.cpp new file mode 100644 index 00000000..1cc0b844 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP110.cpp @@ -0,0 +1,127 @@ +/* + ESP110.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined( WIFI_FEATURE) || defined( BLUETOOTH_FEATURE) || defined (ETH_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#include "../../modules/authentication/authentication_service.h" +//Set radio state at boot which can be BT, WIFI-STA, WIFI-AP, ETH-STA, OFF +//[ESP110]pwd= +bool Commands::ESP110(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + int8_t wifiMode = Settings_ESP3D::read_byte(ESP_RADIO_MODE); + if (wifiMode == ESP_RADIO_OFF) { + output->printMSG("OFF"); + } else if (wifiMode == ESP_BT) { + output->printMSG("BT"); + } else if (wifiMode == ESP_WIFI_AP) { + output->printMSG("WIFI-AP"); + } else if (wifiMode == ESP_WIFI_STA) { + output->printMSG("WIFI-STA"); +// } else if (wifiMode == ESP_ETH_SRV) { +// output->printMSG("ETH-SRV"); + } else if (wifiMode == ESP_ETH_STA) { + output->printMSG("WIFI-STA"); + } else { + output->printMSG("??"); + } + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter.toUpperCase(); + if (!( +#if defined( BLUETOOTH_FEATURE) + (parameter == "BT") || +#endif //BLUETOOTH_FEATURE +#if defined( WIFI_FEATURE) + (parameter == "WIFI-STA") || (parameter == "WIFI-AP") || +#endif //WIFI_FEATURE +#if defined( ETH_FEATURE) + (parameter == "ETH-STA") || //(parameter == "ETH-SRV") || +#endif //ETH_FEATURE + (parameter == "OFF"))) { + + output->printERROR ("Only " +#ifdef BLUETOOTH_FEATURE + "BT or " +#endif //BLUETOOTH_FEATURE +#ifdef WIFI_FEATURE + "WIFI-STA or WIFI-AP or " +#endif //WIFI_FEATURE +#ifdef ETH_FEATURE + "ETH-STA or ETH-SRV or " +#endif //ETH_FEATURE + "OFF mode supported!"); + return false; + } + + int8_t bbuf = ESP_RADIO_OFF; +#ifdef WIFI_FEATURE + if(parameter == "WIFI-STA") { + bbuf = ESP_WIFI_STA; + } + if(parameter == "WIFI-AP") { + bbuf = ESP_WIFI_AP; + } +#endif //WIFI_FEATURE +#ifdef ETH_FEATURE + if(parameter == "ETH-STA") { + bbuf = ESP_ETH_STA; + } +// if(parameter == "ETH-SRV") { +// bbuf = ESP_ETH_SRV; +// } +#endif //ETH_FEATURE +#ifdef BLUETOOTH_FEATURE + if(parameter == "BT") { + bbuf = ESP_BT; + } +#endif //BLUETOOTH_FEATURE + if (!Settings_ESP3D::write_byte(ESP_RADIO_MODE, bbuf)) { + output->printERROR ("Set failed!"); + response = false; + } else { + if (!NetConfig::begin()) { + output->printERROR ("Cannot setup network"); + response = false; + } + } + } + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP111.cpp b/esp3d/src/core/espcmd/ESP111.cpp new file mode 100644 index 00000000..60f40b8f --- /dev/null +++ b/esp3d/src/core/espcmd/ESP111.cpp @@ -0,0 +1,61 @@ +/* + ESP111.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined( WIFI_FEATURE) || defined (ETH_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#if defined (WIFI_FEATURE) +#include "../../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) +#include "../../modules/ethernet/ethconfig.h" +#endif //ETH_FEATURE +#include "../../modules/authentication/authentication_service.h" +//Get current IP +//[ESP111] +bool Commands::ESP111(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + String res = get_param (cmd_params, ""); + String currentIP=""; +#if defined( WIFI_FEATURE) + if (WiFi.getMode() == WIFI_STA) { + currentIP = WiFi.localIP().toString(); + } else if (WiFi.getMode() == WIFI_AP) { + currentIP = WiFi.softAPIP().toString(); + } +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) + if (EthConfig::started()) { + currentIP = ETH.localIP().toString(); + } +#endif //ETH_FEATURE + if (currentIP.length() == 0) { + currentIP = "0.0.0.0"; + } + res += currentIP; + //log_esp3d("Client %d", output->client()); + output->printMSG (res.c_str()); + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP112.cpp b/esp3d/src/core/espcmd/ESP112.cpp new file mode 100644 index 00000000..2bf08dfa --- /dev/null +++ b/esp3d/src/core/espcmd/ESP112.cpp @@ -0,0 +1,65 @@ +/* + ESP112.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) || defined(BT_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#include "../../modules/authentication/authentication_service.h" +//Get/Set hostname +//[ESP112] pwd= +bool Commands::ESP112(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //Get hostname + if (parameter.length() == 0) { + output->printMSG (Settings_ESP3D::read_string(ESP_HOSTNAME)); + } else { //set host name +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if (!NetConfig::isHostnameValid (parameter.c_str())) { + output->printERROR ("Incorrect hostname!"); + response = false; + } else { + if (!Settings_ESP3D::write_string (ESP_HOSTNAME, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //WIFI_FEATURE || ETH_FEATURE || BT_FEATURE diff --git a/esp3d/src/core/espcmd/ESP115.cpp b/esp3d/src/core/espcmd/ESP115.cpp new file mode 100644 index 00000000..ee2293c0 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP115.cpp @@ -0,0 +1,71 @@ +/* + ESP115.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) || defined(BT_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#include "../../modules/authentication/authentication_service.h" +//Get/Set immediate Network (WiFi/BT/Ethernet) state which can be ON, OFF +//[ESP115]pwd= +bool Commands::ESP115(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + if (parameter.length() == 0) { + if (NetConfig::started()) { + output->printMSG ("ON"); + } else { + output->printMSG ("OFF"); + } + return true; + } +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter.toUpperCase(); + if((parameter == "ON") || (parameter == "OFF")) { + if (parameter == "ON") { + if (!NetConfig::begin()) { + output->printERROR ("Cannot setup network"); + response = false; + } + } else { + NetConfig::end(); + } + } else { + output->printERROR ("Only mode ON and OFF are supported"); + response = false; + } + return response; +} + +#endif //WIFI_FEATURE || ETH_FEATURE || BT_FEATURE diff --git a/esp3d/src/core/espcmd/ESP120.cpp b/esp3d/src/core/espcmd/ESP120.cpp new file mode 100644 index 00000000..9c700fb6 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP120.cpp @@ -0,0 +1,65 @@ +/* + ESP120.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Set HTTP state which can be ON, OFF +//[ESP120]pwd= +bool Commands::ESP120(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG((Settings_ESP3D::read_byte(ESP_HTTP_ON) == 0)?"OFF":"ON"); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter.toUpperCase(); + if (!((parameter == "ON") || (parameter == "OFF"))) { + output->printERROR("Only ON or OFF mode supported!"); + return false; + } else { + if (!Settings_ESP3D::write_byte (ESP_HTTP_ON, (parameter == "ON")?1:0)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //HTTP_FEATURE diff --git a/esp3d/src/core/espcmd/ESP121.cpp b/esp3d/src/core/espcmd/ESP121.cpp new file mode 100644 index 00000000..dc6a5a6c --- /dev/null +++ b/esp3d/src/core/espcmd/ESP121.cpp @@ -0,0 +1,64 @@ +/* + ESP121.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Set HTTP port +//[ESP121]pwd= +bool Commands::ESP121(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG(String(Settings_ESP3D::read_uint32(ESP_HTTP_PORT)).c_str()); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + uint ibuf = parameter.toInt(); + if ((ibuf > Settings_ESP3D::get_max_int32_value(ESP_HTTP_PORT)) || (ibuf < Settings_ESP3D::get_min_int32_value(ESP_HTTP_PORT))) { + output->printERROR ("Incorrect port!"); + return false; + } + if (!Settings_ESP3D::write_uint32 (ESP_HTTP_PORT, ibuf)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //HTTP_FEATURE diff --git a/esp3d/src/core/espcmd/ESP130.cpp b/esp3d/src/core/espcmd/ESP130.cpp new file mode 100644 index 00000000..2c6e4ffa --- /dev/null +++ b/esp3d/src/core/espcmd/ESP130.cpp @@ -0,0 +1,65 @@ +/* + ESP130.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (TELNET_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Set TELNET state which can be ON, OFF +//[ESP130]pwd= +bool Commands::ESP130(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG((Settings_ESP3D::read_byte(ESP_TELNET_ON) == 0)?"OFF":"ON"); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter.toUpperCase(); + if (!((parameter == "ON") || (parameter == "OFF"))) { + output->printERROR("Only ON or OFF mode supported!"); + return false; + } else { + if (!Settings_ESP3D::write_byte (ESP_TELNET_ON, (parameter == "ON")?1:0)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + } + return response; +} + +#endif //TELNET_FEATURE diff --git a/esp3d/src/core/espcmd/ESP131.cpp b/esp3d/src/core/espcmd/ESP131.cpp new file mode 100644 index 00000000..6851966b --- /dev/null +++ b/esp3d/src/core/espcmd/ESP131.cpp @@ -0,0 +1,64 @@ +/* + ESP131.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (TELNET_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Set TELNET port +//[ESP131]pwd= +bool Commands::ESP131(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + parameter = get_param (cmd_params, ""); + //get + if (parameter.length() == 0) { + output->printMSG(String(Settings_ESP3D::read_uint32(ESP_TELNET_PORT)).c_str()); + } else { //set +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + uint ibuf = parameter.toInt(); + if ((ibuf > Settings_ESP3D::get_max_int32_value(ESP_TELNET_PORT)) || (ibuf < Settings_ESP3D::get_min_int32_value(ESP_TELNET_PORT))) { + output->printERROR ("Incorrect port!"); + return false; + } + if (!Settings_ESP3D::write_uint32 (ESP_TELNET_PORT, ibuf)) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } + return response; +} + +#endif //TELNET_FEATURE diff --git a/esp3d/src/core/espcmd/ESP201.cpp b/esp3d/src/core/espcmd/ESP201.cpp new file mode 100644 index 00000000..0f622fdd --- /dev/null +++ b/esp3d/src/core/espcmd/ESP201.cpp @@ -0,0 +1,122 @@ +/* + ESP201.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (DIRECT_PIN_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../hal.h" +#include "../../modules/authentication/authentication_service.h" +//Get/Set pin value +//[ESP201]P V [PULLUP=YES RAW=YES ANALOG=NO ANALOG_RANGE=255 CLEARCHANNELS=NO]pwd= +//Range can be 255 / 1024 / 2047 / 4095 / 8191 +bool Commands::ESP201(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + + //check if have pin + parameter = get_param (cmd_params, "P"); + log_esp3d("Pin %s", parameter.c_str()); + if (parameter == "") { + output->printERROR ("Invalid parameter!"); + return false; + } + int pin = parameter.toInt(); + //check pin is valid and not serial used pins + if ( Hal::is_pin_usable(pin)) { + bool isdigital = true; + parameter = get_param (cmd_params, "ANALOG="); + if (parameter == "YES") { + log_esp3d ("Set as analog"); + isdigital=false; + parameter = get_param (cmd_params, "CLEARCHANNELS="); + if (parameter == "YES") { + Hal::clearAnalogChannels(); + } + } + //check if is set or get + parameter = get_param (cmd_params, "V"); + //it is a get + if (parameter == "") { + //this is to not set pin mode + int value = 0; + if(isdigital) { + parameter = get_param (cmd_params, "RAW="); + if (parameter != "YES") { + parameter = get_param (cmd_params, "PULLUP="); + if (parameter != "YES") { + Hal::pinMode (pin, INPUT); + } else { + Hal::pinMode (pin, INPUT_PULLUP); + } + } + value = digitalRead (pin); + } else { + value = Hal::analogRead(pin); + } + + output->printMSG (String(value).c_str()); + } else { + //it is a set + int value = parameter.toInt(); + Hal::pinMode (pin, OUTPUT); + if (isdigital) { + //verify it is a '0' or a '1' + if ( (value == 0) || (value == 1) ) { + digitalWrite (pin, (value == 0) ? LOW : HIGH); + output->printMSG ("ok"); + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + } else { + int analog_range= 255; + parameter = get_param (cmd_params, "ANALOG_RANGE="); + if (parameter.length() > 0) { + analog_range = parameter.toInt(); + } + if ( (value >= 0) || (value <= analog_range+1) ) { + Hal::analogWriteRange(analog_range); + Hal::analogWriteFreq(1000); + if (!Hal::analogWrite(pin, value)) { + output->printERROR ("Invalid value!"); + response = false; + } + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + } + } + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + return response; +} + +#endif //DIRECT_PIN_FEATURE diff --git a/esp3d/src/core/espcmd/ESP400.cpp b/esp3d/src/core/espcmd/ESP400.cpp new file mode 100644 index 00000000..f07b8f9f --- /dev/null +++ b/esp3d/src/core/espcmd/ESP400.cpp @@ -0,0 +1,340 @@ +/* + ESP400.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/serial/serial_service.h" +#include "../../modules/authentication/authentication_service.h" +//Get full ESP3D settings +//[ESP400] +bool Commands::ESP400(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + //Start JSON + output->printLN ("{\"Settings\":["); + //1- Baud Rate + output->print ("{\"F\":\"printer\",\"P\":\""); + output->print (ESP_BAUD_RATE); + output->print ("\",\"T\":\"I\",\"V\":\""); + output->print (Settings_ESP3D::read_uint32(ESP_BAUD_RATE)); + output->print ("\",\"H\":\"Baud Rate\",\"O\":["); + uint8_t count = 0; + const long *bl = serial_service.get_baudratelist(&count); + for (uint8_t i = 0 ; i < count ; i++) { + if (i > 0) { + output->print (","); + } + output->printf("{\"%ld\":\"%ld\"}", bl[i], bl[i]); + } + output->printLN ("]}"); + +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) || defined(BT_FEATURE) + //2 - Hostname + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_HOSTNAME); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (Settings_ESP3D::read_string(ESP_HOSTNAME)); + output->print ("\",\"H\":\"Hostname\" ,\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_HOSTNAME)); + output->print ("\", \"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_HOSTNAME)); + output->printLN ("\"}"); +#endif //WIFI_FEATURE || ETH_FEATURE || BT_FEATURE + //3 - radio mode + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_RADIO_MODE); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_RADIO_MODE)); + output->print ("\",\"H\":\"Radio mode\",\"O\":[{\"None\":\"0\"}"); +#ifdef WIFI_FEATURE + output->print (",{\"STA\":\"1\"},{\"AP\":\"2\"}"); +#endif //WIFI_FEATURE +#ifdef BLUETOOTH_FEATURE + output->print (",{\"BT\":\"3\"}"); +#endif //BLUETOOTH_FEATURE +#ifdef ETH_FEATURE + output->print (",{\"ETH-STA\":\"4\"}"); +#endif //ETH_FEATURE + output->printLN ("]}"); +#ifdef WIFI_FEATURE + //4 - STA SSID + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_SSID); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (Settings_ESP3D::read_string(ESP_STA_SSID)); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_STA_SSID)); + output->print ("\",\"H\":\"Station SSID\",\"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_STA_SSID)); + output->printLN ("\"}"); + + //5 - STA password + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_PASSWORD); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (HIDDEN_PASSWORD); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_STA_PASSWORD)); + output->print ("\",\"H\":\"Station Password\",\"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_STA_PASSWORD)); + output->printLN ("\"}"); + + //6-Station Network Mode + //output->print (",{\"F\":\"network\",\"P\":\""); + //output->print (ESP_STA_PHY_MODE); + //output->print ("\",\"T\":\"B\",\"V\":\""); + //output->print (Settings_ESP3D::read_byte(ESP_STA_PHY_MODE)); + //output->print ("\",\"H\":\"Station Network Mode\",\"O\":[{\"11b\":\""); + //output->print (WIFI_PHY_MODE_11B); + //output->print ("\"},{\"11g\":\""); + //output->print (WIFI_PHY_MODE_11G); + //output->print ("\"},{\"11n\":\""); + //output->print (WIFI_PHY_MODE_11N); + //output->printLN ("\"}]}"); +#endif //WIFI_FEATURE +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) + // 7 - STA IP mode + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_IP_MODE); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_STA_IP_MODE)); + output->printLN ("\",\"H\":\"Station IP Mode\",\"O\":[{\"DHCP\":\"0\"},{\"Static\":\"1\"}]}"); + + //8-STA static IP + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_IP_VALUE); + output->print ("\",\"T\":\"A\",\"V\":\""); + output->print (Settings_ESP3D::read_IP_String(ESP_STA_IP_VALUE)); + output->printLN ("\",\"H\":\"Station Static IP\"}"); + + //9-STA static Gateway + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_GATEWAY_VALUE); + output->print ("\",\"T\":\"A\",\"V\":\""); + output->print (Settings_ESP3D::read_IP_String(ESP_STA_GATEWAY_VALUE)); + output->printLN ("\",\"H\":\"Station Static Gateway\"}"); + + //10-STA static Mask + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_STA_MASK_VALUE); + output->print ("\",\"T\":\"A\",\"V\":\""); + output->print (Settings_ESP3D::read_IP_String(ESP_STA_MASK_VALUE)); + output->printLN ("\",\"H\":\"Station Static Mask\"}"); +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined(WIFI_FEATURE) + //11 - AP SSID + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_AP_SSID); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (Settings_ESP3D::read_string(ESP_AP_SSID)); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_AP_SSID)); + output->print ("\",\"H\":\"AP SSID\",\"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_AP_SSID)); + output->printLN ("\"}"); + + //12 - AP password + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_AP_PASSWORD); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (HIDDEN_PASSWORD); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_AP_PASSWORD)); + output->print ("\",\"H\":\"AP Password\",\"M\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_AP_PASSWORD)); + output->printLN ("\"}"); + + //13 - AP static IP + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_AP_IP_VALUE); + output->print ("\",\"T\":\"A\",\"V\":\""); + output->print (Settings_ESP3D::read_IP_String(ESP_AP_IP_VALUE)); + output->printLN ("\",\"H\":\"AP Static IP\"}"); + + //14 - AP Channel + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_AP_CHANNEL); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_AP_CHANNEL)); + output->print ("\",\"H\":\"AP Channel\",\"O\":["); + for (uint8_t i = Settings_ESP3D::get_min_byte(ESP_AP_CHANNEL); i <= Settings_ESP3D::get_max_byte(ESP_AP_CHANNEL) ; i++) { + if (i > 1) { + output->print (","); + } + output->printf("{\"%d\":\"%d\"}", i, i); + } + output->printLN ("]}"); +#endif //WIFI_FEATURE + //15 - AP Network Mode (PHY) + //output->print (",{\"F\":\"network\",\"P\":\""); + //output->print (ESP_AP_PHY_MODE); + //output->print ("\",\"T\":\"B\",\"V\":\""); + //output->print (Settings_ESP3D::read_byte(ESP_AP_PHY_MODE)); + //output->print ("\",\"H\":\"AP Network Mode\",\"O\":[{\"11b\":\""); + //output->print (WIFI_PHY_MODE_11B); + //output->print ("\"},{\"11g\":\""); + //output->print (WIFI_PHY_MODE_11G); + //output->printLN ("\"}]}"); + + //16-AP Authentication + //output->print (",{\"F\":\"network\",\"P\":\""); + //output->print (ESP_AP_AUTH_TYPE); + //output->print ("\",\"T\":\"B\",\"V\":\""); + //output->print (Settings_ESP3D::read_byte(ESP_AP_AUTH_TYPE)); + //output->print ("\",\"H\":\"Authentication\",\"O\":[{\"Open\":\""); + //output->print (AUTH_OPEN); + //output->print ("\"},{\"WPA\":\""); + //output->print (AUTH_WPA_PSK); + //output->print ("\"},{\"WPA2\":\""); + //output->print (AUTH_WPA2_PSK); + //output->print ("\"},{\"WPA/WPA2\":\""); + //output->print (AUTH_WPA_WPA2_PSK); + //output->printLN ("\"}]}"); + + //17-AP SSID visibility + //output->print (",{\"F\":\"network\",\"P\":\""); + //output->print (ESP_SSID_VISIBLE); + //output->print ("\",\"T\":\"B\",\"V\":\""); + //output->print (Settings_ESP3D::read_byte(ESP_SSID_VISIBLE)); + //output->printLN ("\",\"H\":\"SSID Visible\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"); +#ifdef HTTP_FEATURE + //18-HTTP On + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_HTTP_ON); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_HTTP_ON)); + output->printLN ("\",\"H\":\"Enable HTTP\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"); + + //19-HTTP Port + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_HTTP_PORT); + output->print ("\",\"T\":\"I\",\"V\":\""); + output->print (Settings_ESP3D::read_uint32(ESP_HTTP_PORT)); + output->print ("\",\"H\":\"HTTP Port\",\"S\":\""); + output->print (Settings_ESP3D::get_max_int32_value(ESP_HTTP_PORT)); + output->print ("\",\"M\":\""); + output->print (Settings_ESP3D::get_min_int32_value(ESP_HTTP_PORT)); + output->printLN ("\"}"); +#endif //HTTP_FEATURE + +#ifdef TELNET_FEATURE + //20-TELNET On + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_TELNET_ON); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_TELNET_ON)); + output->printLN ("\",\"H\":\"Enable Telnet\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"); + + //21-TELNET Port + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_TELNET_PORT); + output->print ("\",\"T\":\"I\",\"V\":\""); + output->print (Settings_ESP3D::read_uint32(ESP_TELNET_PORT)); + output->print ("\",\"H\":\"HTTP Port\",\"S\":\""); + output->print (Settings_ESP3D::get_max_int32_value(ESP_TELNET_PORT)); + output->print ("\",\"M\":\""); + output->print (Settings_ESP3D::get_min_int32_value(ESP_TELNET_PORT)); + output->printLN ("\"}"); +#endif //TELNET + +#ifdef AUTHENTICATION_FEATURE + //22-Admin password + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_ADMIN_PWD); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (HIDDEN_PASSWORD); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_ADMIN_PWD)); + output->print ("\",\"H\":\"Admin Password\",\"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_ADMIN_PWD)); + output->printLN ("\"}"); + + //23-User password + output->print (",{\"F\":\"network\",\"P\":\""); + output->print (ESP_USER_PWD); + output->print ("\",\"T\":\"S\",\"V\":\""); + output->print (HIDDEN_PASSWORD); + output->print ("\",\"S\":\""); + output->print (Settings_ESP3D::get_max_string_size(ESP_USER_PWD)); + output->print ("\",\"H\":\"User Password\",\"M\":\""); + output->print (Settings_ESP3D::get_min_string_size(ESP_USER_PWD)); + output->printLN ("\"}"); +#endif //AUTHENTICATION_FEATURE + + //Target FW + output->print (",{\"F\":\"printer\",\"P\":\""); + output->print (ESP_TARGET_FW); + output->print ("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_TARGET_FW)); + output->print ("\",\"H\":\"Target FW\",\"O\":[{\"Repetier\":\""); + output->print (REPETIER); + output->print ("\"},{\"Repetier for Davinci\":\""); + output->print (REPETIER4DV); + output->print ("\"},{\"Marlin\":\""); + output->print (MARLIN); + output->print ("\"},{\"Marlin Kimbra\":\""); + output->print (MARLINKIMBRA); + output->print ("\"},{\"Smoothieware\":\""); + output->print (SMOOTHIEWARE); + output->print ("\"},{\"Grbl\":\""); + output->print (GRBL); + output->print ("\"},{\"Unknown\":\""); + output->print (UNKNOWN_FW); + output->printLN ("\"}]}"); + + //Output flag + output->print (",{\"F\":\"printer\",\"P\":\""); + output->print (ESP_OUTPUT_FLAG); + output->print ("\",\"T\":\"F\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_TARGET_FW)); + output->printf ("\",\"H\":\"Output msg\",\"O\":[{\"M117\":\"%d\"}", ESP_PRINTER_LCD_CLIENT); +#ifdef ESP_OLED_FEATURE + output->printf (",{\"Oled\":\"%d\"}", ESP_OLED_CLIENT); +#endif //ESP_OLED_FEATURE + output->printf (",{\"Serial\":\"%d\"}", ESP_SERIAL_CLIENT); +#ifdef WS_DATA_FEATURE + output->printf (",{\"Web Socket\":\"%d\"}", ESP_WEBSOCKET_CLIENT); +#endif //WS_DATA_FEATURE +#ifdef BLUETOOTH_FEATURE + output->printf (",{\"Bluetooth\":\"%d\"}", ESP_BT_CLIENT); +#endif //BLUETOOTH_FEATURE +#ifdef TELNET_FEATURE + output->printf (",{\"Telnet\":\"%d\"}", ESP_TELNET_CLIENT); +#endif //TELNET_FEATURE + output->printLN ("]}"); +#ifdef SDCARD_FEATURE + //Direct SD + output->print(",{\"F\":\"printer\",\"P\":\""); + output->print(ESP_IS_DIRECT_SD); + output->print("\",\"T\":\"B\",\"V\":\""); + output->print (Settings_ESP3D::read_byte(ESP_IS_DIRECT_SD)); + output->print("\",\"H\":\"Direct SD access\",\"O\":[{\"No\":\"0\"},{\"Yes\":\"1\"}]}"); +#endif //SDCARD_FEATURE + + output->printLN ("]}"); + + return true; +} diff --git a/esp3d/src/core/espcmd/ESP401.cpp b/esp3d/src/core/espcmd/ESP401.cpp new file mode 100644 index 00000000..01f1f255 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP401.cpp @@ -0,0 +1,110 @@ +/* + ESP401.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Set EEPROM setting +//[ESP401]P= T= V= pwd= +bool Commands::ESP401(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + //check validity of parameters + String spos = get_param (cmd_params, "P="); + String styp = get_param (cmd_params, "T="); + String sval = get_param (cmd_params, "V="); + if (spos.length() == 0) { + response = false; + } + if (! (styp == "B" || styp == "S" || styp == "A" || styp == "I" || styp == "F") ) { + response = false; + } + + if (response) { + //Byte value + if ((styp == "B") || (styp == "F")) { + if (!Settings_ESP3D::write_byte (spos.toInt(), sval.toInt())) { + response = false; + } else { + //dynamique refresh is better than restart the boards + switch(spos.toInt()) { + case ESP_TARGET_FW: + Settings_ESP3D::GetFirmwareTarget(true); + break; + default: + break; + } + } + } + //Integer value + if (styp == "I") { + if (!Settings_ESP3D::write_uint32 (spos.toInt(), sval.toInt())) { + response = false; + } else { + //dynamique refresh is better than restart the board + //TBD + } + } + //String value + if (styp == "S") { + if (!Settings_ESP3D::write_string (spos.toInt(), sval.c_str())) { + response = false; + } else { + //dynamique refresh is better than restart the board + switch(spos.toInt()) { +#ifdef AUTHENTICATION_FEATURE + case ESP_ADMIN_PWD: + case ESP_USER_PWD: + AuthenticationService::update(); + break; +#endif //AUTHENTICATION_FEATURE + default: + break; + } + } + } +#if defined (WIFI_FEATURE) + //IP address + if (styp == "A") { + if (!Settings_ESP3D::write_IP_String (spos.toInt(), sval.c_str())) { + response = false; + } else { + //dynamique refresh is better than restart the board + //TBD + } + } +#endif //WIFI_FEATURE + } + if (!response) { + output->printERROR ("Incorrect command!"); + } else { + output->printMSG("ok"); + } + + return response; +} diff --git a/esp3d/src/core/espcmd/ESP410.cpp b/esp3d/src/core/espcmd/ESP410.cpp new file mode 100644 index 00000000..3c8c6120 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP410.cpp @@ -0,0 +1,104 @@ +/* + ESP410.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/wifi/wificonfig.h" +#include "../../modules/authentication/authentication_service.h" +//Get available AP list (limited to 30) +//output is JSON or plain text according parameter +//[ESP410] +bool Commands::ESP410(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + //Backup current mode + uint8_t currentmode = WiFi.getMode(); + bool plain = hastag(cmd_params,"plain"); + int n = 0; + if (plain) { + output->printLN ("Start Scan"); + output->flush(); + } + n = WiFi.scanNetworks (); + if (!plain) { + output->print ("{\"AP_LIST\":["); + } + for (int i = 0; i < n; ++i) { + if (i > 0) { + if (!plain) { + output->print (","); + } else { + output->printLN (""); + } + } + if (!plain) { + output->print ("{\"SSID\":\""); + } + output->print (WiFi.SSID (i).c_str()); + if (!plain) { + output->print ("\",\"SIGNAL\":\""); + } else { + output->print ("\t"); + } + output->print (String(WiFiConfig::getSignal (WiFi.RSSI (i) ))); + if (plain) { + output->print("%"); + } + if (!plain) { + output->print ("\",\"IS_PROTECTED\":\""); + } + if (WiFi.encryptionType (i) == ENC_TYPE_NONE) { + if (!plain) { + output->print ("0"); + } else { + output->print ("\tOpen"); + } + } else { + if (!plain) { + output->print ("1"); + } else { + output->print ("\tSecure"); + } + } + if (!plain) { + output->print ("\"}"); + } + } + WiFi.scanDelete(); + if (!plain) { + output->printLN ("]}"); + } else { + output->printLN (""); + output->printLN ("End Scan"); + } + WiFi.mode((WiFiMode_t)currentmode); + return response; +} + +#endif //WIFI_FEATURE diff --git a/esp3d/src/core/espcmd/ESP420.cpp b/esp3d/src/core/espcmd/ESP420.cpp new file mode 100644 index 00000000..0d6c76d7 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP420.cpp @@ -0,0 +1,964 @@ +/* + ESP420.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +#include "../../modules/serial/serial_service.h" +#ifdef FILESYSTEM_FEATURE +#include "../../modules/filesystem/esp_filesystem.h" +#endif //FILESYSTEM_FEATURE +#if defined (WIFI_FEATURE) || defined(ETH_FEATURE) ||defined(BLUETOOTH_FEATURE) +#include "../../modules/network/netconfig.h" +#if defined (WIFI_FEATURE) +#include "../../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) +#include "../../modules/ethernet/ethconfig.h" +#endif //ETH_FEATURE +#if defined (BLUETOOTH_FEATURE) +#include "../../modules/bluetooth/BT_service.h" +#endif //BLUETOOTH_FEATURE +#endif //WIFI_FEATURE || ETH_FEATURE || BLUETOOTH_FEATURE +#ifdef HTTP_FEATURE +#include "../../modules/http/http_server.h" +#endif //HTTP_FEATURE +#ifdef TELNET_FEATURE +#include "../../modules/telnet/telnet_server.h" +#endif //TELNET_FEATURE +//Get ESP current status +//output is JSON or plain text according parameter +//[ESP420] +bool Commands::ESP420(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + bool plain = hastag(cmd_params,"plain"); + //TODO add plain / JSON support + if (!plain) { + output->print ("{\"Status\":["); + } + //Chip ID + if (!plain) { + output->print ("{\"id\":\""); + } + output->print ("Chip ID"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf("%d",Hal::getChipID()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //CPU freq + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("CPU Frequency"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf("%d Mhz",ESP.getCpuFreqMHz()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //CPU temp + if (Hal::has_temperature_sensor()) { + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("CPU Temperature"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf("%s %s",String (Hal::temperature(), 1).c_str(), (output->client() == ESP_HTTP_CLIENT)?"°C":"C"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } + //Free Memory + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Free memory"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print(ESP_FileSystem::formatBytes (ESP.getFreeHeap()).c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //SDK version + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("SDK"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf("%s", ESP.getSdkVersion()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Flash size + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Flash Size"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print(ESP_FileSystem::formatBytes (ESP.getFlashChipSize()).c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + +#if (defined (WIFI_FEATURE) || defined (ETH_FEATURE)) && (defined(OTA_FEATURE) || defined(WEB_UPDATE_FEATURE)) + //update space + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Available Size for update"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print(ESP_FileSystem::formatBytes (ESP_FileSystem::max_update_size()).c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined(FILESYSTEM_FEATURE) + //FileSystem type + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Filesystem type"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ESP_FileSystem::FilesystemName()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //FileSystem capacity + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Filesystem usage"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ESP_FileSystem::formatBytes (ESP_FileSystem::usedBytes()).c_str()); + output->print ("/"); + output->print (ESP_FileSystem::formatBytes (ESP_FileSystem::totalBytes()).c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#endif //FILESYSTEM_FEATURE + //baud rate + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Baud rate"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%ld", serial_service.baudRate()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#if defined (WIFI_FEATURE) + if (WiFi.getMode() != WIFI_OFF) { + //sleep mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Sleep mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::getSleepModeString ()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } +#endif //WIFI_FEATURE +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + //Wifi enabled + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("WiFi"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((WiFi.getMode() == WIFI_OFF)?"Disabled":"Enabled"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#if defined (ETH_FEATURE) + //Ethernet enabled + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Ethernet"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((EthConfig::started())?"Enabled":"Disabled"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#endif //ETH_FEATURE +#if defined (BLUETOOTH_FEATURE) + //BT enabled + if (!plain) { + output->print (",{\"id\":\""); + } + output->print("Bluetooth"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((bt_service.started())?"Enabled":"Disabled"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#endif //BLUETOOTH_FEATURE + //Hostname + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Hostname"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (NetConfig::hostname()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } +#if defined (HTTP_FEATURE) + if (HTTP_Server::started()) { + //http port + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("HTTP port"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%ld",HTTP_Server::port()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } +#endif //HTTP_FEATURE +#if defined (TELNET_FEATURE) + if (telnet_server.started()) { + //telnet port + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Telnet port"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%ld",telnet_server.port()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } + if (telnet_server.isConnected()) { + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Telnet Client"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%s",telnet_server.clientIPAddress()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } +#endif //TELNET_FEATURE +#if defined (BLUETOOTH_FEATURE) + if (bt_service.started()) { + //BT mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Current BT mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ("Serial ("); + output->print (BTService::macAddress()); + output->print(")"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //BT status + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("BT Status"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((bt_service.isConnected())?"Connected":"Disconnected"); + if (bt_service.isConnected()) { + output->print (" (client: "); + output->print (BTService::clientmacAddress()); + output->print (")"); + } + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } +#endif //BLUETOOTH_FEATURE +#if defined (ETH_FEATURE) + if (EthConfig::started()) { + //Ethernet mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Current Ethernet mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ("Client("); + output->print (ETH.macAddress().c_str()); + output->printLN(")"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Ethernet cable + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Ethernet cable"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((ETH.linkUp())?"Connected":"Disconnected"); + if(ETH.linkUp()) { + output->print (" ("); + output->print (ETH.linkSpeed()); + output->printLN("Mbps)"); + } + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //IP mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("IP Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((NetConfig::isIPModeDHCP(ESP_ETH_STA))?"DHCP":"Static"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //IP value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("IP"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ETH.localIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //GW value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Gateway"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ETH.gatewayIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Mask value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Mask"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ETH.subnetMask().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //DNS value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("DNS"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (ETH.dnsIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } +#endif //ETH_FEATURE +#if defined (WIFI_FEATURE) + if (WiFi.getMode() != WIFI_OFF) { + //WiFi Mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Current WiFi Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + if (WiFi.getMode() == WIFI_STA) { + output->print ("STA ("); + output->print ( WiFi.macAddress().c_str()); + output->print (")"); + } else if (WiFi.getMode() == WIFI_AP) { + output->print ("AP ("); + output->print (WiFi.softAPmacAddress().c_str()); + output->print (")"); + } else if (WiFi.getMode() == WIFI_AP_STA) { //we should not be in this state but just in case .... + output->print ("Mixed"); + output->printLN(""); + output->print ("STA ("); + output->print (WiFi.macAddress().c_str()); + output->print (")"); + output->printLN(""); + output->print ("AP ("); + output->print (WiFi.softAPmacAddress().c_str()); + output->print (")"); + } else { + output->print ("???"); + } + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //WiFi Station + if (WiFi.getMode() == WIFI_STA) { + //Connected to SSID + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Connected to"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + if (WiFi.isConnected()) { + output->print (WiFi.SSID().c_str()); + } + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + if (WiFi.isConnected()) { //in case query come from serial + //Signal strength + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Signal"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%d %%",WiFiConfig::getSignal(WiFi.RSSI())); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Phy Mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Phy Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::getPHYModeString (WIFI_STA)); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Channel + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Channel"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->printf ("%d",WiFi.channel()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //IP Mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("IP Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((NetConfig::isIPModeDHCP(ESP_WIFI_STA))?"DHCP":"Static"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //IP value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("IP"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFi.localIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Gateway value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Gateway"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFi.gatewayIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Mask value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Mask"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFi.subnetMask().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //DNS value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("DNS"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFi.dnsIP().toString().c_str()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } + //Disabled Mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Disabled Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ("AP ("); + output->print (WiFi.softAPmacAddress().c_str()); + output->print (")"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } else if (WiFi.getMode() == WIFI_AP) { + //AP SSID + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("SSID"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::AP_SSID()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //AP Visibility + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Visible"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((WiFiConfig::is_AP_visible()) ? "Yes" : "No"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //AP Authentication + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Authentication"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::AP_Auth_String()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //DHCP Server + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("DHCP Server"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ((NetConfig::isDHCPServer (ESP_WIFI_AP))?"Started":"Stopped"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //IP Value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("IP"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFi.softAPIP().toString()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Gateway Value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Gateway"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::AP_Gateway_String()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Mask Value + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Mask"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (WiFiConfig::AP_Mask_String()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //Connected clients + const char * entry = NULL; + uint8_t nb = 0; + entry = WiFiConfig::getConnectedSTA(&nb, true); + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Connected clients"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (nb); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + for (uint8_t i = 0; i < nb; i++) { + //Client + if (!plain) { + output->print (",{\"id\":\""); + } + output->printf ("Client %d",i); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (entry); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + //get next + entry = WiFiConfig::getConnectedSTA(); + } + //Disabled Mode + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("Disabled Mode"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print ("STA ("); + output->print (WiFi.macAddress().c_str()); + output->print (")"); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + } + } +#endif //WIFI_FEATURE +#endif //WIFI_FEATURE || ETH FEATURE + //FW version + if (!plain) { + output->print (",{\"id\":\""); + } + output->print ("FW version"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (FW_VERSION); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + if (!plain) { + output->print (",{\"id\":\""); + } + //FW architecture + output->print ("FW architecture"); + if (!plain) { + output->print ("\",\"value\":\""); + } else { + output->print (": "); + } + output->print (Settings_ESP3D::TargetBoard()); + if (!plain) { + output->print ("\"}"); + } else { + output->printLN(""); + } + + if (!plain) { + output->print ("]}"); + output->printLN (""); + } + return response; +} diff --git a/esp3d/src/core/espcmd/ESP444.cpp b/esp3d/src/core/espcmd/ESP444.cpp new file mode 100644 index 00000000..296bf401 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP444.cpp @@ -0,0 +1,52 @@ +/* + ESP444.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "../esp3d.h" +#include "../commands.h" +#include "../esp3doutput.h" +#include "../../modules/authentication/authentication_service.h" +//Set ESP State +//cmd are RESTART / RESET +//[ESP444] +bool Commands::ESP444(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if (hastag(cmd_params,"RESTART")) { + output->printMSG ("Restart ongoing"); + Esp3D::restart_esp(); + } else if (hastag(cmd_params,"RESET")) { + if (Esp3D::reset()) { + output->printMSG ("Reset done"); + } else { + output->printERROR ("Reset failed"); + } + } else { + response = false; + output->printERROR ("Invalid parameter!"); + } + return response; +} diff --git a/esp3d/src/core/espcmd/ESP550.cpp b/esp3d/src/core/espcmd/ESP550.cpp new file mode 100644 index 00000000..97441b6b --- /dev/null +++ b/esp3d/src/core/espcmd/ESP550.cpp @@ -0,0 +1,54 @@ +/* + ESP550.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (AUTHENTICATION_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Change admin password +//[ESP550]pwd= +bool Commands::ESP550(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + if (auth_type == LEVEL_GUEST) { + parameter = get_param (cmd_params, ""); + if (parameter.length() != 0) { + if (Settings_ESP3D::isLocalPasswordValid (parameter.c_str() ) ) { + if (!Settings_ESP3D::write_string (ESP_ADMIN_PWD, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + } + } else { + output->printERROR("Wrong authentication!", 401); + response = false; + } + return response; +} + +#endif //AUTHENTICATION_FEATURE diff --git a/esp3d/src/core/espcmd/ESP555.cpp b/esp3d/src/core/espcmd/ESP555.cpp new file mode 100644 index 00000000..11b88403 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP555.cpp @@ -0,0 +1,54 @@ +/* + ESP555.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (AUTHENTICATION_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +//Change user password +//[ESP555]pwd= +bool Commands::ESP555(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + if (auth_type != LEVEL_GUEST) { + parameter = get_param (cmd_params, ""); + if (parameter.length() != 0) { + if (Settings_ESP3D::isLocalPasswordValid (parameter.c_str() ) ) { + if (!Settings_ESP3D::write_string (ESP_USER_PWD, parameter.c_str())) { + output->printERROR ("Set failed!"); + response = false; + } else { + output->printMSG ("ok"); + } + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + } + } else { + output->printERROR("Wrong authentication!", 401); + response = false; + } + return response; +} + +#endif //AUTHENTICATION_FEATURE diff --git a/esp3d/src/core/espcmd/ESP710.cpp b/esp3d/src/core/espcmd/ESP710.cpp new file mode 100644 index 00000000..b15bea0c --- /dev/null +++ b/esp3d/src/core/espcmd/ESP710.cpp @@ -0,0 +1,53 @@ +/* + ESP710.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (FILESYSTEM_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +#include "../../modules/filesystem/esp_filesystem.h" +//Format ESP Filesystem +//[ESP710]FORMAT pwd= +bool Commands::ESP710(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + parameter = get_param (cmd_params, ""); +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + response = false; + } else +#endif //AUTHENTICATION_FEATURE + { + if (parameter == "FORMAT") { + output->printMSG("Start Formating"); + ESP_FileSystem::format(); + output->printMSG("Format Done"); + } else { + output->printERROR ("Invalid parameter!"); + response = false; + } + } + return response; +} + +#endif //FILESYSTEM_FEATURE diff --git a/esp3d/src/core/espcmd/ESP720.cpp b/esp3d/src/core/espcmd/ESP720.cpp new file mode 100644 index 00000000..ca728054 --- /dev/null +++ b/esp3d/src/core/espcmd/ESP720.cpp @@ -0,0 +1,99 @@ +/* + ESP720.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#if defined (FILESYSTEM_FEATURE) +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/authentication/authentication_service.h" +#include "../../modules/filesystem/esp_filesystem.h" +//List ESP Filesystem +//[ESP720] pwd= +bool Commands::ESP720(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; + parameter = get_param (cmd_params, ""); +#ifdef AUTHENTICATION_FEATURE + if (auth_type != LEVEL_ADMIN) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + if (parameter.length() == 0) { + parameter = "/"; + } + output->printf("Directory : %s", parameter.c_str()); + output->printLN(""); + if (ESP_FileSystem::exists(parameter.c_str())) { + ESP_File f = ESP_FileSystem::open(parameter.c_str(), ESP_FILE_READ); + uint countf = 0; + uint countd = 0; + if (f) { + //Check directories + ESP_File sub = f.openNextFile(); + while (sub) { + if (sub.isDirectory()) { + countd++; + output->print(" \t"); + output->print(sub.name()); + output->print(" \t"); + output->printLN(""); + } + sub.close(); + sub = f.openNextFile(); + } + f.close(); + f = ESP_FileSystem::open(parameter.c_str(), ESP_FILE_READ); + //Check files + sub = f.openNextFile(); + while (sub) { + if (!sub.isDirectory()) { + countf++; + output->print(" \t"); + output->print(sub.name()); + output->print(" \t"); + output->print(ESP_FileSystem::formatBytes(sub.size()).c_str()); + output->print(" \t"); +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + time_t t = sub.getLastWrite(); + struct tm * tmstruct = localtime(&t); + output->printf("%d-%02d-%02d %02d:%02d:%02d",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); + output->print(" \t"); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + output->printLN(""); + } + sub.close(); + sub = f.openNextFile(); + } + f.close(); + output->printf("%d file%s, %d dir%s", countf, (countf > 1)?"(s)":"", countd, (countd > 1)?"(s)":""); + output->printLN(""); + } else { + output->printERROR ("Invalid directory!"); + } + } else { + output->printERROR ("Invalid directory!"); + response = false; + } + return response; +} + +#endif //FILESYSTEM_FEATURE diff --git a/esp3d/src/core/espcmd/ESP800.cpp b/esp3d/src/core/espcmd/ESP800.cpp new file mode 100644 index 00000000..3bd8c0fe --- /dev/null +++ b/esp3d/src/core/espcmd/ESP800.cpp @@ -0,0 +1,214 @@ +/* + ESP800.cpp - ESP3D command class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "../commands.h" +#include "../esp3doutput.h" +#include "../settings_esp3d.h" +#include "../../modules/network/netconfig.h" +#include "../../modules/authentication/authentication_service.h" +#ifdef FILESYSTEM_FEATURE +#include "../../modules/filesystem/esp_filesystem.h" +#endif //FILESYSTEM_FEATURE +#if defined (WIFI_FEATURE) || defined(ETH_FEATURE) ||defined(BLUETOOTH_FEATURE) +#include "../../modules/network/netconfig.h" +#if defined (WIFI_FEATURE) +#include "../../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#endif //WIFI_FEATURE || ETH_FEATURE || BLUETOOTH_FEATURE +#ifdef HTTP_FEATURE +#include "../../modules/http/http_server.h" +#endif //HTTP_FEATURE +//get fw version firmare target and fw version +//output is JSON or plain text according parameter +//[ESP800] +bool Commands::ESP800(const char* cmd_params, level_authenticate_type auth_type, ESP3DOutput * output) +{ + bool response = true; + String parameter; +#ifdef AUTHENTICATION_FEATURE + if (auth_type == LEVEL_GUEST) { + output->printERROR("Wrong authentication!", 401); + return false; + } +#endif //AUTHENTICATION_FEATURE + bool plain = hastag(cmd_params,"plain"); + //FW version + if (plain) { + output->print("FW version:"); + } else { + output->print("{\"FWVersion\":\""); + } + output->print(FW_VERSION); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + + //FW target + if (plain) { + output->print("FW target:"); + } else { + output->print(",\"FWTarget\":\""); + } + output->print(Settings_ESP3D::GetFirmwareTargetShortName()); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + //SD connection + if (plain) { + output->print("SD connection:"); + } else { + output->print(",\"SDConnection\":\""); + } + output->print((Settings_ESP3D::isDirectSD())?"Direct":"Serial"); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + if (Settings_ESP3D::isDirectSD()) { + //TODO + //Primary SD + + //TODO + //Secondary SD + } + //Authentication + if (plain) { + output->print("Authentication:"); + } else { + output->print(",\"Authentication\":\""); + } +#ifdef AUTHENTICATION_FEATURE + output->print("Enabled"); +#else + output->print("Disabled"); +#endif //AUTHENTICATION_FEATURE + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } +#if (defined(WIFI_FEATURE) || defined(ETH_FEATURE)) && defined(HTTP_FEATURE) + //Web Communication + if (plain) { + output->print("Web Communication:"); + } else { + output->print(",\"WebCommunication\":\""); + } +#if defined (ASYNCWEBSERVER_FEATURE) + output->print("Asynchronous"); +#else + output->print("Synchronous"); +#endif //ASYNCWEBSERVER_FEATURE + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + //WebSocket Port + if (plain) { + output->print("Web Socket port:"); + } else { + output->print(",\"WebSocketport\":\""); + } + output->print(HTTP_Server::port() +1); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } +#endif // (WIFI_FEATURE) || ETH_FEATURE) && HTTP_FEATURE) +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) || defined(BT_FEATURE) + //Hostname + if (plain) { + output->print("Hostname:"); + } else { + output->print(",\"Hostname\":\""); + } + output->print(NetConfig::hostname()); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } +#endif //WIFI_FEATURE|| ETH_FEATURE || BT_FEATURE +#if defined(WIFI_FEATURE) + if (WiFiConfig::started()) { + //WiFi mode + if (plain) { + output->print("WiFi mode:"); + } else { + output->print(",\"WiFiMode\":\""); + } + output->print((WiFi.getMode() == WIFI_AP)?"AP":"STA"); + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + } +#endif //WIFI_FEATURE +#if defined(WIFI_FEATURE) || defined(ETH_FEATURE) + //Update + if (plain) { + output->print("Web Update:"); + } else { + output->print(",\"WebUpdate\":\""); + } +#ifdef WEB_UPDATE_FEATURE + output->print("Enabled"); +#else + output->print("Disabled"); +#endif //WEB_UPDATE_FEATURE + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } +#endif //WIFI_FEATURE|| ETH_FEATURE + +//Hostname + if (plain) { + output->print("Filesystem:"); + } else { + output->print(",\"Filesystem\":\""); + } +#if defined(FILESYSTEM_FEATURE) + output->print(ESP_FileSystem::FilesystemName()); +#else + output->print("None"); +#endif //FILESYSTEM_FEATURE + if(plain) { + output->printLN(""); + } else { + output->print("\""); + } + + //final + if(!plain) { + output->printLN("}"); + } + return response; +} + diff --git a/esp3d/src/core/genLinkedList.h b/esp3d/src/core/genLinkedList.h new file mode 100644 index 00000000..e708c3e6 --- /dev/null +++ b/esp3d/src/core/genLinkedList.h @@ -0,0 +1,223 @@ +/* + genlinkedlist.h - ESP3D simple generic linked list class + * inspired by great https://github.com/ivanseidel/LinkedList + + Copyright (c) 2018 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _GENLINKEDLIST_H +#define _GENLINKEDLIST_H + +template struct GenNode { + T data; + GenNode *next; + GenNode *prev; +}; + +template class GenLinkedList +{ +public: + GenLinkedList(); + ~GenLinkedList(); + //Clear list + void clear(); + //number of GenNodes + size_t count(); + //add data at the end of list + bool push(T data); + //get data at end of list and remove from list + T pop(); + //add data at the begining of list + bool shift(T data); + //get data from begining of list and remove from list + T unshift(); + //get data from index position + T get(size_t index); + //get head data + T getFirst(); + //get tail data + T getLast(); + +private: + //number of GenNode + size_t _nb; + //First GenNode + GenNode *_head; + //Last GenNode + GenNode *_tail; +}; + +//Constructor +template GenLinkedList::GenLinkedList() +{ + _nb = 0; + _head = nullptr; + _tail = nullptr; +} + +//Destructor +template GenLinkedList::~GenLinkedList() +{ + clear(); +} + +//clear list of all GenNodes +templatevoid GenLinkedList::clear() +{ + GenNode *current; + //delete from first to last + while (_head != nullptr) { + current = _head; + _head = _head->next; + current->data = T(); + delete current; + } + _tail = _head; + _nb = 0; +} + +//Number of GenNodes +templatesize_t GenLinkedList::count() +{ + return _nb; +} + +//add to end of list +template bool GenLinkedList::push (T data) +{ + GenNode *ptr = new GenNode(); + if (!ptr) { + return false; + } + ptr->data = data; + ptr->next = nullptr; + ptr->prev = nullptr; + _nb++; + //Check if already have element in list + if (_head) { + _tail->next = ptr; + ptr->prev = _tail; + _tail = ptr; + } else { // no element _head = _tail + _head = ptr; + _tail = _head; + } + return true; +} +//take out from end of list +templateT GenLinkedList::pop() +{ + if (_head == nullptr) { + return T(); + } + T data = _tail->data; + + _nb--; + if (_head == _tail) { + _head->data = T(); + delete (_head); + _head = nullptr; + _tail = _head; + } else { + GenNode *ptr = _tail; + _tail = _tail->prev; + _tail->next = nullptr; + ptr->data = T(); + delete (ptr); + } + return data; +} + +//add to head of list +template bool GenLinkedList::shift (T data) +{ + + GenNode *ptr = new GenNode(); + if (!ptr) { + return false; + } + ptr->data = data; + ptr->next = nullptr; + ptr->prev = nullptr; + _nb++; + //Check if already have element in list + if (_head) { + ptr->next = _head; + _head->prev = ptr; + _head = ptr; + } else { // no element _head = _tail + _head = ptr; + _tail = _head; + } + return true; +} + +//take out from Head +templateT GenLinkedList::unshift() +{ + if (_head == nullptr) { + return T(); + } + T data = _head->data; + _nb--; + if (_head == _tail) { + _head->data = T(); + delete (_head); + _head = nullptr; + _tail = _head; + } else { + GenNode *ptr = _head; + _head = _head->next; + _head->prev = nullptr; + ptr->data = T(); + delete (ptr); + } + return data; +} + +//get data from position (do not remove from list) +templateT GenLinkedList::get(size_t index) +{ + if ((_head == nullptr) || (index > _nb)) { + return T(); + } + GenNode *ptr = _head; + for (size_t pos = 0; pos < index; pos++) { + ptr = ptr->next; + } + return ptr->data; +} + + +//get head data (do not remove from list) +templateT GenLinkedList::getFirst() +{ + if (_head == nullptr) { + return T(); + } + return _head->data; +} + +//get tail data (do not remove from list) +templateT GenLinkedList::getLast() +{ + if (_head == nullptr) { + return T(); + } + return _tail->data; +} + +#endif //_GENLINKEDLIST_H diff --git a/esp3d/src/core/hal.cpp b/esp3d/src/core/hal.cpp new file mode 100644 index 00000000..e56f5386 --- /dev/null +++ b/esp3d/src/core/hal.cpp @@ -0,0 +1,244 @@ +/* + hal.cpp - ESP3D hal class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../include/esp3d_config.h" + +#if defined(ARDUINO_ARCH_ESP8266) +#include "ESP8266WiFi.h" +#endif //ARDUINO_ARCH_ESP8266 +#if defined(ARDUINO_ARCH_ESP32) +#include "WiFi.h" +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus +esp_err_t esp_task_wdt_reset(); +#ifdef __cplusplus +} +#endif //__cplusplus +#endif //ARDUINO_ARCH_ESP32 + +#include "esp3doutput.h" + +#if defined(ARDUINO_ARCH_ESP32) +int ChannelAttached2Pin[16]= {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; +#endif //ARDUINO_ARCH_ESP32 + +uint32_t Hal::_analogWriteRange = 255; +uint32_t Hal::_analogWriteFreq = 1000; + +//Contructor +Hal::Hal() +{ + +} + +//Destructor +Hal::~Hal() +{ +} + +void Hal::clearAnalogChannels() +{ +#ifdef ARDUINO_ARCH_ESP32 + for (uint8_t p = 0; p < 16; p++) { + if(ChannelAttached2Pin[p] != -1) { + ledcDetachPin(ChannelAttached2Pin[p]); + ChannelAttached2Pin[p] = -1; + } + } +#endif //ARDUINO_ARCH_ESP32 +} + +void Hal::pinMode(uint8_t pin, uint8_t mode) +{ +#if defined (ARDUINO_ARCH_ESP8266) + if ((pin == 16) && (mode == INPUT_PULLUP)) { + pinMode(pin, INPUT_PULLDOWN_16); + return; + } +#endif + pinMode(pin, mode); +} + +int Hal::analogRead(uint8_t pin) +{ +#ifdef ARDUINO_ARCH_ESP8266 //only one ADC on ESP8266 A0 + return analogRead (A0); +#else + return analogRead (pin); +#endif +} + +bool Hal::analogWrite(uint8_t pin, int value) +{ + if (value > (_analogWriteRange-1)) { + return false; + } +#ifdef ARDUINO_ARCH_ESP8266 + analogWrite(pin, value); +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + int channel = -1; + for (uint8_t p = 0; p < 16; p++) { + if(ChannelAttached2Pin[p] == pin) { + channel = p; + } + } + if (channel==-1) { + for (uint8_t p = 0; p < 16; p++) { + if(ChannelAttached2Pin[p] = -1) { + channel = p; + ChannelAttached2Pin[p] = pin; + p = 16; + } + } + } + if (channel==-1) { + return false; + } + uint8_t resolution = 0; + switch(_analogWriteRange) { + case 8191: + resolution=13; + break; + case 1024: + resolution=10; + break; + case 2047: + resolution=11; + break; + case 4095: + resolution=12; + break; + default: + resolution=8; + _analogWriteRange = 255; + break; + } + + ledcSetup(channel, _analogWriteFreq, resolution); + ledcAttachPin(pin, channel); + ledcWrite(channel, value); +#endif //ARDUINO_ARCH_ESP32 + return true; +} +void Hal::analogWriteFreq(uint32_t freq) +{ + _analogWriteFreq = freq; +#ifdef ARDUINO_ARCH_ESP8266 + analogWriteFreq(_analogWriteFreq); +#endif //ARDUINO_ARCH_ESP8266 +} +void Hal::analogWriteRange(uint32_t range) +{ + _analogWriteRange = range; +#ifdef ARDUINO_ARCH_ESP8266 + analogWriteRange(_analogWriteRange); +#endif //ARDUINO_ARCH_ESP8266 +} + +//Setup +bool Hal::begin() +{ + //Clear all wifi state + WiFi.persistent(false); + WiFi.disconnect(true); + WiFi.enableSTA (false); + WiFi.enableAP (false); + WiFi.mode (WIFI_OFF); + return true; +} + +//End ESP3D +void Hal::end() +{ +} + +//Watchdog feeder +void Hal::wdtFeed() +{ +#ifdef ARDUINO_ARCH_ESP8266 + ESP.wdtFeed(); +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + void esp_task_wdt_feed(); +#endif //ARDUINO_ARCH_ESP32 +} + +//wait function +void Hal::wait (uint32_t milliseconds) +{ +#if defined(ASYNCWEBSERVER) || defined(ARDUINO_ARCH_ESP32) + uint32_t timeout = millis(); + while ( (millis() - timeout) < milliseconds) { + wdtFeed(); + } +#else // !(ASYNCWEBSERVER + ARDUINO_ARCH_ESP32) + delay(milliseconds); +#endif // !ASYNCWEBSERVER & !ARDUINO_ARCH_ESP32 +} + +uint16_t Hal::getChipID() +{ +#ifdef ARDUINO_ARCH_ESP8266 + return ESP.getChipId(); +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + return (uint16_t) (ESP.getEfuseMac() >> 32); +#endif //ARDUINO_ARCH_ESP32 +} + +bool Hal::has_temperature_sensor() +{ +#ifdef ARDUINO_ARCH_ESP8266 + return false; +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + return true; +#endif //ARDUINO_ARCH_ESP32 +} + +float Hal::temperature() +{ +#ifdef ARDUINO_ARCH_ESP8266 + return 0.0; +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + return temperatureRead(); +#endif //ARDUINO_ARCH_ESP32 +} + +bool Hal::is_pin_usable(uint pin) +{ +#ifdef ARDUINO_ARCH_ESP8266 + if ((pin <= 5) || ((pin >= 12) && (pin <= 16))) { + return true; + } else { + return false; + } +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + if ((pin <= 5) || ((pin >= 12) && (pin <= 39))) { + return true; + } else { + return false; + } +#endif //ARDUINO_ARCH_ESP32 +} diff --git a/esp3d/src/core/hal.h b/esp3d/src/core/hal.h new file mode 100644 index 00000000..66be9d69 --- /dev/null +++ b/esp3d/src/core/hal.h @@ -0,0 +1,58 @@ +/* + hal.h - esp3d hal class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ESP3D_HAL_H +#define _ESP3D_HAL_H +//be sure correct IDE and settings are used for ESP8266 or ESP32 +#if !(defined( ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)) +#error Oops! Make sure you have 'ESP8266 or ESP32' compatible board selected from the 'Tools -> Boards' menu. +#endif // ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32 +#if defined(ARDUINO_ARCH_ESP8266) +#include "ESP8266WiFi.h" +#endif //ARDUINO_ARCH_ESP8266 +#if defined(ARDUINO_ARCH_ESP32) +#include "WiFi.h" +#endif //ARDUINO_ARCH_ESP32 +#include + +class Hal +{ +public: + Hal(); + ~Hal(); + static bool begin(); + static void end(); + static void wait (uint32_t milliseconds); + static uint16_t getChipID(); + static bool has_temperature_sensor(); + static float temperature(); + static bool is_pin_usable(uint pin); + static void clearAnalogChannels(); + static void pinMode(uint8_t pin, uint8_t mode); + static int analogRead(uint8_t pin); + static bool analogWrite(uint8_t pin, int value); + static void analogWriteFreq(uint32_t freq); + static void analogWriteRange(uint32_t range); +private: + static void wdtFeed(); + static uint32_t _analogWriteRange; + static uint32_t _analogWriteFreq; +}; +#endif //_ESP3D_HAL_H diff --git a/esp3d/src/core/settings_esp3d.cpp b/esp3d/src/core/settings_esp3d.cpp new file mode 100644 index 00000000..108b8b73 --- /dev/null +++ b/esp3d/src/core/settings_esp3d.cpp @@ -0,0 +1,937 @@ +/* + settings_esp3d.cpp - settings esp3d functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../include/esp3d_config.h" +#include "settings_esp3d.h" +#include "esp3doutput.h" +#if defined(SETTINGS_IN_EEPROM) +#include +//EEPROM SIZE (Up to 4096) +#define EEPROM_SIZE 1024 //max is 1024 +#endif //SETTINGS_IN_EEPROM + +#if defined (WIFI_FEATURE) || defined(ETH_FEATURE) +#include "../modules/network/netconfig.h" +#if defined (WIFI_FEATURE) +#include "../modules/wifi/wificonfig.h" +#endif //WIFI_FEATURE +#endif //WIFI_FEATURE || ETH_FEATURE + + +#if defined(SETTINGS_IN_PREFERENCES) +#include +#define NAMESPACE "ESP3D" +#endif // SETTINGS_IN_PREFERENCES + +//Current Settings Version +#define CURRENT_SETTINGS_VERSION "ESP3D03" + +//boundaries +#define MAX_DHT_INTERVAL 1000 +#define MIN_DHT_INTERVAL 0 +#define MAX_LOCAL_PASSWORD_LENGTH 20 +#define MIN_LOCAL_PASSWORD_LENGTH 1 +#define MAX_VERSION_LENGTH 7 //ESP3DXX + + +//default byte values +#ifdef WIFI_FEATURE +#define DEFAULT_ESP_RADIO_MODE ESP_WIFI_AP +#else //WIFI_FEATURE +#ifdef BLUETOOTH_FEATURE +#define DEFAULT_ESP_RADIO_MODE ESP_BT +#else //BLUETOOTH_FEATURE +#ifdef ETH_FEATURE +#define DEFAULT_ESP_RADIO_MODE ESP_ETH_STA +#else //BLUETOOTH_FEATURE +#define DEFAULT_ESP_RADIO_MODE NO_NETWORK +#endif //ETH_FEATURE +#endif //BLUETOOTH_FEATURE +#endif //WIFI_FEATURE + + +#define DEFAULT_ESP_BYTE 0 +#define DEFAULT_ESP_STRING_SIZE 0 +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) +#define DEFAULT_STA_IP_MODE DHCP_MODE +#endif //WIFI_FEATURE || ETH_FEATURE +//#define DEFAULT_PHY_MODE WIFI_PHY_MODE_11G +//#define DEFAULT_SLEEP_MODE WIFI_MODEM_SLEEP +#define DEFAULT_AP_CHANNEL 11 +#define DEFAULT_AUTH_TYPE AUTH_WPA_PSK +#define DEFAULT_SSID_VISIBLE 1 +#define DEFAULT_OUTPUT_FLAG ESP_ALL_CLIENTS +#define DEFAULT_SDREADER_SPEED 4 +#define DEFAULT_FW UNKNOWN_FW +#define DEFAULT_TIME_ZONE 0 +#define DEFAULT_TIME_DST 0 +#define DEFAULT_PRIMARY_SD 2 +#define DEFAULT_SECONDARY_SD 1 +#define DEFAULT_DIRECT_SD_CHECK 0 +#define DEFAULT_SD_CHECK_UPDATE_AT_BOOT 1 +#define DEFAULT_DHT_TYPE 255 +#define DEFAULT_IS_DIRECT_SD 0 +#define DEFAULT_HTTP_ON 1 +#define DEFAULT_TELNET_ON 1 + + +//default int values +#define DEFAULT_ESP_INT 0L +#define DEFAULT_BAUD_RATE 115200L +#define DEFAULT_HTTP_PORT 80L +#define DEFAULT_TELNET_PORT 23L +#define DEFAULT_DHT_INTERVAL 30L + +//default string values +const char DEFAULT_AP_SSID [] PROGMEM = "ESP3D"; +const char DEFAULT_AP_PASSWORD [] PROGMEM = "12345678"; +const char DEFAULT_STA_SSID [] PROGMEM = "ESP3D"; +const char DEFAULT_STA_PASSWORD [] PROGMEM = "12345678"; +const char DEFAULT_HOSTNAME [] PROGMEM = "esp3d"; +const char DEFAULT_ESP_STRING [] = ""; +const char DEFAULT_ADMIN_PWD [] PROGMEM = "admin"; +const char DEFAULT_USER_PWD [] PROGMEM = "user"; +const char DEFAULT_TIME_SERVER1 [] PROGMEM = "time.nist.gov"; +const char DEFAULT_TIME_SERVER2 [] PROGMEM = "0.pool.ntp.org"; +const char DEFAULT_TIME_SERVER3 [] PROGMEM = "1.pool.ntp.org"; +const char DEFAULT_SETTINGS_VERSION [] PROGMEM = "ESP3D"; + +//default IP values +const uint8_t DEFAULT_IP_VALUE[] = {192, 168, 0, 1}; +const uint8_t DEFAULT_MASK_VALUE[] = {255, 255, 255, 0}; +#define DEFAULT_GATEWAY_VALUE DEFAULT_IP_VALUE +const uint8_t DEFAULT_ADDRESS_VALUE[] = {0, 0, 0, 0}; + +uint8_t Settings_ESP3D::_FirmwareTarget = UNKNOWN_FW; +bool Settings_ESP3D::_directSD = false; + +Settings_ESP3D::Settings_ESP3D() +{ +} +Settings_ESP3D::~Settings_ESP3D() +{ +} + +bool Settings_ESP3D::begin() +{ + if(GetSettingsVersion() == -1) { + return false; + } + //get target FW + Settings_ESP3D::GetFirmwareTarget(true); + //is direct SD + Settings_ESP3D::isDirectSD(true); + return true; +} + +uint8_t Settings_ESP3D::GetFirmwareTarget(bool fromsettings) +{ + if(fromsettings) { + _FirmwareTarget = read_byte (ESP_TARGET_FW); + } + return _FirmwareTarget; +} + +bool Settings_ESP3D::isDirectSD(bool fromsettings) +{ + if(fromsettings) { +#ifdef SDCARD_FEATURE + _directSD = read_byte (ESP_IS_DIRECT_SD); +#else // !SDCARD_FEATURE + _directSD = false; +#endif //SDCARD_FEATURE + } + return _directSD; +} + +const char* Settings_ESP3D::GetFirmwareTargetShortName() +{ + static String response; + if ( _FirmwareTarget == REPETIER4DV) { + response = F ("repetier4davinci"); + } else if ( _FirmwareTarget == REPETIER) { + response = F ("repetier"); + } else if ( _FirmwareTarget == MARLIN) { + response = F ("marlin"); + } else if ( _FirmwareTarget == MARLINKIMBRA) { + response = F ("marlinkimbra"); + } else if ( _FirmwareTarget == SMOOTHIEWARE) { + response = F ("smoothieware"); + } else if ( _FirmwareTarget == GRBL) { + response = F ("grbl"); + } else { + response = F ("???"); + } + return response.c_str(); +} + +//Default value for a byte setting +uint8_t Settings_ESP3D::get_default_byte_value(int pos) +{ + uint8_t res; + switch(pos) { + case ESP_RADIO_MODE: + res = DEFAULT_ESP_RADIO_MODE; + break; +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_STA_IP_MODE: + res = DEFAULT_STA_IP_MODE; + break; +#endif //WIFI_FEATURE || ETH_FEATURE + //case ESP_AP_PHY_MODE: + //case ESP_STA_PHY_MODE: + // res = DEFAULT_PHY_MODE; + // break; + //case ESP_SLEEP_MODE: + // res = DEFAULT_SLEEP_MODE; + // break; +#if defined (WIFI_FEATURE) + case ESP_AP_CHANNEL: + res = DEFAULT_AP_CHANNEL; + break; + //case ESP_AP_AUTH_TYPE: + // res = DEFAULT_AUTH_TYPE; + // break; + //case ESP_SSID_VISIBLE: + // res = DEFAULT_SSID_VISIBLE; + // break; +#endif //WIFI_FEATURE + case ESP_OUTPUT_FLAG: + res = DEFAULT_OUTPUT_FLAG; + break; + case ESP_HTTP_ON: + res = DEFAULT_HTTP_ON; + break; + case ESP_TELNET_ON: + res = DEFAULT_TELNET_ON; + break; + case ESP_SD_SPEED_DIV: + res = DEFAULT_SDREADER_SPEED; + break; + case ESP_TARGET_FW: + res = DEFAULT_FW; + break; + case ESP_TIMEZONE: + res = DEFAULT_TIME_ZONE; + break; + case ESP_TIME_IS_DST: + res = DEFAULT_TIME_DST; + break; + case ESP_IS_DIRECT_SD: + res = DEFAULT_IS_DIRECT_SD; + break; + case ESP_PRIMARY_SD: + res = DEFAULT_PRIMARY_SD; + break; + case ESP_SECONDARY_SD: + res = DEFAULT_SECONDARY_SD; + break; + case ESP_DIRECT_SD_CHECK: + res = DEFAULT_DIRECT_SD_CHECK; + break; + case ESP_DHT_TYPE: + res = DEFAULT_DHT_TYPE; + break; + case ESP_SD_CHECK_UPDATE_AT_BOOT: + res = DEFAULT_SD_CHECK_UPDATE_AT_BOOT; + break; + default: + res = DEFAULT_ESP_BYTE; + } + return res; +} + +//Default value for a int32 setting +uint32_t Settings_ESP3D::get_default_int32_value(int pos) +{ + uint32_t res; + switch(pos) { + case ESP_BAUD_RATE: + res = DEFAULT_BAUD_RATE; + break; +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_AP_IP_VALUE: + case ESP_STA_IP_VALUE: + res = IPAddress(DEFAULT_IP_VALUE); + break; + case ESP_STA_MASK_VALUE: + res = IPAddress(DEFAULT_MASK_VALUE); + break; + case ESP_STA_GATEWAY_VALUE: + res = IPAddress(DEFAULT_GATEWAY_VALUE); + break; + case ESP_HTTP_PORT: + res = DEFAULT_HTTP_PORT; + break; + case ESP_TELNET_PORT: + res = DEFAULT_TELNET_PORT; + break; +#endif //WIFI_FEATURE || ETH_FEATURE + case ESP_DHT_INTERVAL: + res = DEFAULT_DHT_INTERVAL; + break; + default: + res = DEFAULT_ESP_INT; + } + return res; +} + +//Max value for a int32 setting +uint32_t Settings_ESP3D::get_max_int32_value(int pos) +{ + uint32_t res; + switch(pos) { +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_HTTP_PORT: + res = MAX_HTTP_PORT; + break; + case ESP_TELNET_PORT: + res = MAX_TELNET_PORT; + break; +#endif //WIFI_FEATURE || ETH_FEATURE + case ESP_DHT_INTERVAL: + res = MAX_DHT_INTERVAL; + break; + default: + res = DEFAULT_ESP_INT; + } + return res; +} + +//Min value for a int32 setting +uint32_t Settings_ESP3D::get_min_int32_value(int pos) +{ + uint32_t res; + switch(pos) { +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_HTTP_PORT: + res = MIN_HTTP_PORT; + break; + case ESP_TELNET_PORT: + res = MIN_TELNET_PORT; + break; +#endif //WIFI_FEATURE || ETH_FEATURE + case ESP_DHT_INTERVAL: + res = MIN_DHT_INTERVAL; + break; + default: + res = DEFAULT_ESP_INT; + } + return res; +} + +uint8_t Settings_ESP3D::get_max_byte(int pos) +{ + uint8_t res; + switch(pos) { +#if defined (WIFI_FEATURE) + case ESP_AP_CHANNEL: + res = MAX_CHANNEL; + break; +#endif //WIFI_FEATURE + default: + res = 255; + } + return res; +} +uint8_t Settings_ESP3D::get_min_byte(int pos) +{ + uint8_t res; + switch(pos) { +#if defined (WIFI_FEATURE) + case ESP_AP_CHANNEL: + res = MIN_CHANNEL; + break; +#endif //WIFI_FEATURE || ETH_FEATURE + default: + res = 0; + } + return res; +} + + +//Default value for a ip setting +uint32_t Settings_ESP3D::get_default_IP_value(int pos) +{ + return get_default_int32_value(pos); +} + +//Default value for a byte setting +const String & Settings_ESP3D::get_default_string_value(int pos) +{ + static String res; + switch(pos) { +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_HOSTNAME: + res = DEFAULT_HOSTNAME; + break; + case ESP_TIME_SERVER1: + res = DEFAULT_TIME_SERVER1; + break; + case ESP_TIME_SERVER2: + res = DEFAULT_TIME_SERVER2; + break; + case ESP_TIME_SERVER3: + res = DEFAULT_TIME_SERVER3; + break; +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined (WIFI_FEATURE) + case ESP_STA_SSID: + res = DEFAULT_STA_SSID; + break; + case ESP_AP_SSID: + res = DEFAULT_AP_SSID; + break; + case ESP_STA_PASSWORD: + res = DEFAULT_STA_PASSWORD; + break; + case ESP_AP_PASSWORD: + res = DEFAULT_AP_PASSWORD; + break; +#endif //WIFI_FEATURE + case ESP_ADMIN_PWD: + res = DEFAULT_ADMIN_PWD; + break; + case ESP_USER_PWD: + res = DEFAULT_USER_PWD; + break; + + case ESP_SETTINGS_VERSION: + res = DEFAULT_SETTINGS_VERSION; + break; + default: + res = DEFAULT_ESP_STRING; + } + return res; +} + +//Max size of for a string setting +uint8_t Settings_ESP3D::get_max_string_size(int pos) +{ + uint8_t res; + switch(pos) { +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + + case ESP_HOSTNAME: + res = MAX_HOSTNAME_LENGTH; + break; + case ESP_TIME_SERVER1: + case ESP_TIME_SERVER2: + case ESP_TIME_SERVER3: + res = MAX_SERVER_ADDRESS_LENGTH; + break; +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined (WIFI_FEATURE) + case ESP_STA_SSID: + case ESP_AP_SSID: + res = MAX_SSID_LENGTH; + break; + case ESP_STA_PASSWORD: + case ESP_AP_PASSWORD: + res = MAX_PASSWORD_LENGTH; + break; +#endif //WIFI_FEATURE + case ESP_ADMIN_PWD: + case ESP_USER_PWD: + res = MAX_LOCAL_PASSWORD_LENGTH; + break; + + case ESP_SETTINGS_VERSION: + res = MAX_VERSION_LENGTH; + break; + default: + res = DEFAULT_ESP_STRING_SIZE; + } + return res; +} + +//Min size of for a string setting +uint8_t Settings_ESP3D::get_min_string_size(int pos) +{ + uint8_t res; + switch(pos) { +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + case ESP_HOSTNAME: + res = MIN_HOSTNAME_LENGTH; + break; + case ESP_TIME_SERVER1: + case ESP_TIME_SERVER2: + case ESP_TIME_SERVER3: + res = MIN_SERVER_ADDRESS_LENGTH; + break; +#endif //WIFI_FEATURE || ETH_FEATURE +#if defined (WIFI_FEATURE) + case ESP_STA_SSID: + case ESP_AP_SSID: + res = MIN_SSID_LENGTH; + break; + case ESP_STA_PASSWORD: + case ESP_AP_PASSWORD: + res = MIN_PASSWORD_LENGTH; + break; +#endif + case ESP_ADMIN_PWD: + case ESP_USER_PWD: + res = MIN_LOCAL_PASSWORD_LENGTH; + break; + + default: + res = DEFAULT_ESP_STRING_SIZE; + } + return res; +} + +uint8_t Settings_ESP3D::read_byte (int pos, bool * haserror) +{ + if(haserror) { + *haserror = true; + } + uint8_t value = get_default_byte_value(pos); +#if defined( SETTINGS_IN_EEPROM) +//check if parameters are acceptable + if ((pos + 1 > EEPROM_SIZE) ) { + log_esp3d("Error read byte %d", pos); + return value; + } +//read byte + EEPROM.begin (EEPROM_SIZE); + value = EEPROM.read (pos); + EEPROM.end(); +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, true)) { + log_esp3d("Error opening %s", NAMESPACE); + return value; + } + String p = "P_" + String(pos); + value = prefs.getChar(p.c_str(), get_default_byte_value(pos)); + prefs.end(); +#endif //SETTINGS_IN_PREFERENCES + if(haserror) { + *haserror = false; + } + return value; +} + +//write a flag / byte +bool Settings_ESP3D::write_byte (int pos, const uint8_t value) +{ +#if defined( SETTINGS_IN_EEPROM) + //check if parameters are acceptable + if (pos + 1 > EEPROM_SIZE) { + log_esp3d("Error read byte %d", pos); + return false; + } + EEPROM.begin (EEPROM_SIZE); + EEPROM.write (pos, value); + if (!EEPROM.commit()) { + log_esp3d("Error commit %d", pos); + return false; + } + EEPROM.end(); +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, false)) { + log_esp3d("Error opening %s", NAMESPACE); + return false; + } + String p = "P_" + String(pos); + uint8_t r = prefs.putChar(p.c_str(), value); + prefs.end(); + if (r == 0) { + log_esp3d("Error commit %s", p.c_str()); + return false; + } +#endif //SETTINGS_IN_PREFERENCES + return true; +} + +//read a string +//a string is multibyte + \0, this is won't work if 1 char is multibyte like chinese char +const char * Settings_ESP3D::read_string (int pos, bool *haserror) +{ + uint8_t size_max = get_max_string_size(pos); + if (haserror) { + *haserror = true; + } + if (size_max == 0) { + log_esp3d("Error size string %d", pos); + return DEFAULT_ESP_STRING; + } +#if defined(SETTINGS_IN_EEPROM) + static char * byte_buffer = NULL; + size_max++;//do not forget the 0x0 for the end + if (byte_buffer) { + free (byte_buffer); + byte_buffer = NULL; + } + //check if parameters are acceptable + if (pos + size_max + 1 > EEPROM_SIZE) { + log_esp3d("Error read string %d", pos); + return DEFAULT_ESP_STRING; + } + byte_buffer = (char *)malloc(size_max+1); + if (!byte_buffer) { + log_esp3d("Error mem read string %d", pos); + return DEFAULT_ESP_STRING; + } + EEPROM.begin (EEPROM_SIZE); + byte b = 1; // non zero for the while loop below + int i = 0; + + //read until max size is reached or \0 is found + while (i < size_max && b != 0) { + b = EEPROM.read (pos + i); + byte_buffer[i] = b; + i++; + } + + // Be sure there is a 0 at the end. + if (b != 0) { + byte_buffer[i - 1] = 0x00; + } + EEPROM.end(); + if (haserror) { + *haserror = false; + } + return byte_buffer; +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + static String res; + + if (!prefs.begin(NAMESPACE, true)) { + log_esp3d("Error opening %s", NAMESPACE); + return ""; + } + String p = "P_" + String(pos); + res = prefs.getString(p.c_str(), get_default_string_value(pos)); + prefs.end(); + if(haserror) { + *haserror = false; + } + if (res.length() > size_max) { + log_esp3d("String too long %d vs %d", res.length(), size_max); + res = res.substring(0,size_max-1); + } + return res.c_str(); +#endif //SETTINGS_IN_PREFERENCES +} + +//write a string (array of byte with a 0x00 at the end) +bool Settings_ESP3D::write_string (int pos, const char * byte_buffer) +{ + int size_buffer = strlen (byte_buffer); + uint8_t size_max = get_max_string_size(pos); + //check if parameters are acceptable + if (size_max == 0) { + log_esp3d("Error unknow entry %d", pos); + return false; + } + if (size_max < size_buffer) { + log_esp3d("Error string too long %d, %d", pos, size_buffer); + return false; + } +#if defined(SETTINGS_IN_EEPROM) + if ( pos + size_buffer + 1 > EEPROM_SIZE || byte_buffer == NULL) { + log_esp3d("Error write string %d", pos); + return false; + } + //copy the value(s) + EEPROM.begin (EEPROM_SIZE); + for (int i = 0; i < size_buffer; i++) { + EEPROM.write (pos + i, byte_buffer[i]); + } + //0 terminal + EEPROM.write (pos + size_buffer, 0x00); + if (!EEPROM.commit()) { + log_esp3d("Error commit %d", pos); + return false; + } + EEPROM.end(); +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, false)) { + log_esp3d("Error opening %s", NAMESPACE); + return false; + } + String p = "P_" + String(pos); + uint8_t r = prefs.putString(p.c_str(), byte_buffer); + prefs.end(); + if (r == 0) { + log_esp3d("Error commit %s", p.c_str()); + return false; + } +#endif //SETTINGS_IN_PREFERENCES + return true; +} + +//read a uint32 +uint32_t Settings_ESP3D::read_uint32(int pos, bool * haserror) +{ + if (haserror) { + *haserror = true; + } + uint32_t res = get_default_int32_value(pos); +#if defined(SETTINGS_IN_EEPROM) + //check if parameters are acceptable + uint8_t size_buffer = sizeof(uint32_t); + if ( pos + size_buffer > EEPROM_SIZE ) { + log_esp3d("Error read int %d", pos); + return res; + } + uint8_t i = 0; + EEPROM.begin (EEPROM_SIZE); + //read until max size is reached + while (i < size_buffer ) { + ((uint8_t *)(&res))[i] = EEPROM.read (pos + i); + i++; + } + EEPROM.end(); +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, true)) { + log_esp3d("Error opening %s", NAMESPACE); + return res; + } + String p = "P_" + String(pos); + res = prefs.getUInt(p.c_str(), res); + prefs.end(); +#endif //SETTINGS_IN_PREFERENCES + if (haserror) { + *haserror = false; + } + return res; +} + +//read an IP +uint32_t Settings_ESP3D::read_IP(int pos, bool * haserror) +{ + return read_uint32(pos,haserror); +} + +//read an IP +String Settings_ESP3D::read_IP_String(int pos, bool * haserror) +{ + return IPtoString(read_uint32(pos,haserror)); +} + +//write a uint32 +bool Settings_ESP3D::write_uint32(int pos, const uint32_t value) +{ +#if defined(SETTINGS_IN_EEPROM) + uint8_t size_buffer = sizeof(uint32_t); + //check if parameters are acceptable + if (pos + size_buffer > EEPROM_SIZE) { + log_esp3d("Error invalid entry %d", pos); + return false; + } + EEPROM.begin (EEPROM_SIZE); + //copy the value(s) + for (int i = 0; i < size_buffer; i++) { + EEPROM.write (pos + i, ((uint8_t *)(&value))[i]); + } + if (!EEPROM.commit()) { + log_esp3d("Error commit %d", pos); + return false; + } + EEPROM.end(); +#endif //SETTINGS_IN_EEPROM +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, false)) { + log_esp3d("Error opening %s", NAMESPACE); + return false; + } + String p = "P_" + String(pos); + uint8_t r = prefs.putUInt(p.c_str(), value); + prefs.end(); + if (r == 0) { + log_esp3d("Error commit %s", p.c_str()); + return false; + } +#endif //SETTINGS_IN_PREFERENCES + return true; +} + +//write a IP +bool Settings_ESP3D::write_IP(int pos, const uint32_t value) +{ + return write_uint32(pos, value); +} + +//clear all entries +bool Settings_ESP3D::reset() +{ + bool res = true; + log_esp3d("Reset Settings"); +#if defined(SETTINGS_IN_PREFERENCES) + Preferences prefs; + if (!prefs.begin(NAMESPACE, false)) { + return false; + } + res = prefs.clear(); + prefs.end(); +#endif //SETTINGS_IN_PREFERENCES +//for EEPROM need to overwrite all settings +#if defined(SETTINGS_IN_EEPROM) +#if defined (WIFI_FEATURE) || defined (BLUETOOTH_FEATURE) || defined (ETH_FEATURE) + //Hostname + Settings_ESP3D::write_string(ESP_HOSTNAME,Settings_ESP3D::get_default_string_value(ESP_HOSTNAME).c_str()); +#endif //WIFI_FEATURE || BLUETOOTH_FEATURE + //radio mode + Settings_ESP3D::write_byte(ESP_RADIO_MODE,Settings_ESP3D::get_default_byte_value(ESP_RADIO_MODE)); +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) + //STA SSID + Settings_ESP3D::write_string(ESP_STA_SSID,Settings_ESP3D::get_default_string_value(ESP_STA_SSID).c_str()); + //STA pwd + Settings_ESP3D::write_string(ESP_STA_PASSWORD,Settings_ESP3D::get_default_string_value(ESP_STA_PASSWORD).c_str()); + //STA Network Mode + //Settings_ESP3D::write_byte(ESP_STA_PHY_MODE,Settings_ESP3D::get_default_byte_value(ESP_STA_PHY_MODE)); + //STA IP mode + Settings_ESP3D::write_byte(ESP_STA_IP_MODE,Settings_ESP3D::get_default_byte_value(ESP_STA_IP_MODE)); + //STA static IP + Settings_ESP3D::write_IP(ESP_STA_IP_VALUE, Settings_ESP3D::get_default_IP_value(ESP_STA_IP_VALUE)); + //STA static Gateway + Settings_ESP3D::write_IP(ESP_STA_GATEWAY_VALUE, Settings_ESP3D::get_default_IP_value(ESP_STA_GATEWAY_VALUE)); + //STA static Mask + Settings_ESP3D::write_IP(ESP_STA_MASK_VALUE, Settings_ESP3D::get_default_IP_value(ESP_STA_MASK_VALUE)); + //AP SSID + Settings_ESP3D::write_string(ESP_AP_SSID,Settings_ESP3D::get_default_string_value(ESP_AP_SSID).c_str()); + //AP password + Settings_ESP3D::write_string(ESP_AP_PASSWORD,Settings_ESP3D::get_default_string_value(ESP_AP_PASSWORD).c_str()); + //AP static IP + Settings_ESP3D::write_IP(ESP_AP_IP_VALUE, Settings_ESP3D::get_default_IP_value(ESP_AP_IP_VALUE)); + //AP Channel + Settings_ESP3D::write_byte(ESP_AP_CHANNEL,Settings_ESP3D::get_default_byte_value(ESP_AP_CHANNEL)); + //AP Network Mode (PHY) + //Settings_ESP3D::write_byte(ESP_AP_PHY_MODE,Settings_ESP3D::get_default_byte_value(ESP_AP_PHY_MODE)); + //AP Authentication + //Settings_ESP3D::write_byte(ESP_AP_AUTH_TYPE,Settings_ESP3D::get_default_byte_value(ESP_AP_AUTH_TYPE)); + //AP SSID visibility + //Settings_ESP3D::write_byte(ESP_SSID_VISIBLE,Settings_ESP3D::get_default_byte_value(ESP_SSID_VISIBLE)); +#ifdef HTTP_FEATURE + //HTTP On + Settings_ESP3D::write_byte(ESP_HTTP_ON,Settings_ESP3D::get_default_byte_value(ESP_HTTP_ON)); + //HTTP Port + Settings_ESP3D::write_uint32 (ESP_HTTP_PORT, Settings_ESP3D::get_default_int32_value(ESP_HTTP_PORT)); +#endif //HTTP_FEATURE + +#ifdef TELNET_FEATURE + //TELNET On + Settings_ESP3D::write_byte(ESP_TELNET_ON,Settings_ESP3D::get_default_byte_value(ESP_TELNET_ON)); + //TELNET Port + Settings_ESP3D::write_uint32 (ESP_TELNET_PORT, Settings_ESP3D::get_default_int32_value(ESP_TELNET_PORT)); +#endif //TELNET +#endif //WIFI_FEATURE +#ifdef AUTHENTICATION_FEATURE + //Admin password + Settings_ESP3D::write_string(ESP_ADMIN_PWD,Settings_ESP3D::get_default_string_value(ESP_ADMIN_PWD).c_str()); + //User password + Settings_ESP3D::write_string(ESP_USER_PWD,Settings_ESP3D::get_default_string_value(ESP_USER_PWD).c_str()); +#endif //AUTHENTICATION_FEATURE + //Target FW + Settings_ESP3D::write_byte(ESP_TARGET_FW,Settings_ESP3D::get_default_byte_value(ESP_TARGET_FW)); + //Output flag + Settings_ESP3D::write_byte(ESP_OUTPUT_FLAG,Settings_ESP3D::get_default_byte_value(ESP_OUTPUT_FLAG)); +#ifdef SDCARD_FEATURE + //Direct SD + Settings_ESP3D::write_byte(ESP_IS_DIRECT_SD,Settings_ESP3D::get_default_byte_value(ESP_IS_DIRECT_SD)); +#endif //SDCARD_FEATURE +#endif //SETTINGS_IN_EEPROM + //set version in settings + if (res) { + res = Settings_ESP3D::write_string(ESP_SETTINGS_VERSION, CURRENT_SETTINGS_VERSION); + } + return res; +} + +//Get Settings Version +// * -1 means no version detected +// * 00 / 01 Not used +// * 03 and up is version +int8_t Settings_ESP3D::GetSettingsVersion() +{ + int8_t v = -1; + String version = Settings_ESP3D::read_string(ESP_SETTINGS_VERSION); + if ((version == "ESP3D") ||( version.length() != 7) || (version.indexOf("ESP3D")!=0)) { + log_esp3d("Invalid Settings Version %s",version.c_str()); + return v; + } + v = version.substring(5).toInt(); + log_esp3d("Settings Version %d", v); + return v; +} + +//write a IP from string +bool Settings_ESP3D::write_IP_String(int pos, const char * value) +{ + return write_uint32(pos, StringtoIP(value)); +} + +//Helper to convert IP string to int +uint32_t Settings_ESP3D::StringtoIP(const char *s) +{ + uint32_t ip_int = 0; + IPAddress ipaddr; + if (ipaddr.fromString(s)) { + ip_int = ipaddr; + } + return ip_int; +} + +// Helper to convert int to IP string +String Settings_ESP3D::IPtoString(uint32_t ip_int) +{ + static IPAddress ipaddr; + ipaddr = ip_int; + return ipaddr.toString(); +} + +const char * Settings_ESP3D::TargetBoard() +{ +#ifdef ARDUINO_ARCH_ESP32 + return "ESP32"; +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + return "ESP82XX"; +#endif //ARDUINO_ARCH_ESP8266 +} + +bool Settings_ESP3D::isLocalPasswordValid (const char * password) +{ + char c; + //limited size + if ( (strlen (password) > MAX_LOCAL_PASSWORD_LENGTH) || (strlen (password) <= MIN_LOCAL_PASSWORD_LENGTH) ) { + return false; + } + //no space allowed + for (uint8_t i = 0; i < strlen (password); i++) { + c = password[i]; + if (c == ' ') { + return false; + } + } + return true; +} diff --git a/esp3d/src/core/settings_esp3d.h b/esp3d/src/core/settings_esp3d.h new file mode 100644 index 00000000..73e2b2da --- /dev/null +++ b/esp3d/src/core/settings_esp3d.h @@ -0,0 +1,130 @@ +/* + settings_esp3d.h - settings esp3d functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _SETTINGS_ESP3D_H +#define _SETTINGS_ESP3D_H + +//Supported FW ///////////////////////////////////////////////////////////// +#define UNKNOWN_FW 0 +#define REPETIER4DV 1 +#define MARLIN 2 +#define MARLINKIMBRA 3 +#define SMOOTHIEWARE 4 +#define REPETIER 5 +#define GRBL 6 +#define MAX_FW_ID 6 + +#define NO_NETWORK 0 +//position in EEPROM / preferences will use `P_` + to make a string : P_0 for 0 +#define ESP_RADIO_MODE 0 //1 byte = flag +#define ESP_STA_SSID 1 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese +#define ESP_STA_PASSWORD 34 //65 bytes 64 +1 = string ;warning does not support multibyte char like chinese +#define ESP_STA_IP_MODE 99 //1 byte = flag +#define ESP_STA_IP_VALUE 100 //4 bytes xxx.xxx.xxx.xxx +#define ESP_STA_MASK_VALUE 104 //4 bytes xxx.xxx.xxx.xxx +#define ESP_STA_GATEWAY_VALUE 108 //4 bytes xxx.xxx.xxx.xxx +#define ESP_BAUD_RATE 112 //4 bytes = int +//#define ESP_STA_PHY_MODE 116 //1 byte = flag +//#define ESP_SLEEP_MODE 117 //1 byte = flag +#define ESP_AP_CHANNEL 118 //1 byte = flag +//#define ESP_AP_AUTH_TYPE 119 //1 byte = flag +//#define ESP_SSID_VISIBLE 120 //1 byte = flag +#define ESP_HTTP_PORT 121 //4 bytes = int +#define ESP_TELNET_PORT 125 //4 bytes = int +#define ESP_OUTPUT_FLAG 129 //1 bytes = flag +#define ESP_HOSTNAME 130 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese +#define ESP_DHT_INTERVAL 164 //4 bytes = int +#define ESP_SETTINGS_VERSION 168 //8 bytes = 7+1 = string ESP3D + 2 digits +#define ESP_ADMIN_PWD 176 //21 bytes 20+1 = string ; warning does not support multibyte char like chinese +#define ESP_USER_PWD 197 //21 bytes 20+1 = string ; warning does not support multibyte char like chinese +#define ESP_AP_SSID 218 //33 bytes 32+1 = string ; warning does not support multibyte char like chinese +#define ESP_AP_PASSWORD 251 //65 bytes 64 +1 = string ;warning does not support multibyte char like chinese +#define ESP_AP_IP_VALUE 316 //4 bytes xxx.xxx.xxx.xxx +//#define EP_FREE_INT4 320 //4 bytes xxx.xxx.xxx.xxx +//#define EP_FREE_INT5 324 //4 bytes xxx.xxx.xxx.xxx +#define ESP_HTTP_ON 328 //1 byte = flag +#define ESP_TELNET_ON 329 //1 byte = flag +//#define ESP_AP_PHY_MODE 330 //1 byte = flag +#define ESP_SD_SPEED_DIV 331 //1 byte = flag +//#define EP_FREE_STRING1 332//128 bytes 127+1 = string ; warning does not support multibyte char like chinese +#define ESP_DHT_TYPE 460//1 bytes = flag +#define ESP_TARGET_FW 461 //1 bytes = flag +#define ESP_TIMEZONE 462//1 bytes = flag +#define ESP_TIME_IS_DST 463//1 bytes = flag +#define ESP_TIME_SERVER1 464//129 bytes 128+1 = string ; warning does not support multibyte char like chinese +#define ESP_TIME_SERVER2 593 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese +#define ESP_TIME_SERVER3 722 //129 bytes 128+1 = string ; warning does not support multibyte char like chinese +#define ESP_IS_DIRECT_SD 850//1 bytes = flag +#define ESP_PRIMARY_SD 851//1 bytes = flag +#define ESP_SECONDARY_SD 852//1 bytes = flag +#define ESP_DIRECT_SD_CHECK 853//1 bytes = flag +#define ESP_SD_CHECK_UPDATE_AT_BOOT 854//1 bytes = flag + +//Hidden password +#define HIDDEN_PASSWORD "********" + + +#include + +class Settings_ESP3D +{ +public: + Settings_ESP3D(); + ~Settings_ESP3D(); + static bool begin(); + static uint8_t get_default_byte_value(int pos); + static uint32_t get_default_int32_value(int pos); + static uint32_t get_default_IP_value(int pos); + static const String & get_default_string_value(int pos); + static uint8_t get_max_string_size(int pos); + static uint8_t get_min_string_size(int pos); + static uint32_t get_max_int32_value(int pos); + static uint32_t get_min_int32_value(int pos); + static uint8_t get_max_byte(int pos); + static uint8_t get_min_byte(int pos); + static uint8_t read_byte (int pos, bool * haserror = NULL); + static uint32_t read_uint32(int pos, bool * haserror = NULL); + static uint32_t read_IP(int pos, bool * haserror = NULL); + static String read_IP_String(int pos, bool * haserror = NULL); + static const char * read_string (int pos, bool *haserror = NULL); + static bool write_byte (int pos, const uint8_t value); + static bool write_string (int pos, const char * byte_buffer); + static bool write_uint32 (int pos, const uint32_t value); + static bool write_IP (int pos, const uint32_t value); + static bool write_IP_String (int pos, const char * value); + static bool reset(); + static int8_t GetSettingsVersion(); + static uint8_t GetFirmwareTarget(bool fromsettings = false); + static bool isDirectSD(bool fromsettings = false); + static const char* GetFirmwareTargetShortName(); + static String IPtoString(uint32_t ip_int); + static uint32_t StringtoIP(const char *s); + static const char * TargetBoard(); + static bool isLocalPasswordValid (const char * password); +private: + static uint8_t _FirmwareTarget; + static bool _directSD; +}; + + +#endif //_SETTINGS_ESP3D_H + diff --git a/esp3d/src/include/esp3d_config.h b/esp3d/src/include/esp3d_config.h new file mode 100644 index 00000000..b4f1a0f6 --- /dev/null +++ b/esp3d/src/include/esp3d_config.h @@ -0,0 +1,29 @@ +/* + config.h - ESP3D configuration file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _ESP3D_CONFIG_H +#define _ESP3D_CONFIG_H +#include +#include "../configuration.h" +#include "../include/sanity_esp3d.h" +#include "../core/hal.h" +#include "../core/debug_esp3d.h" +#include "../include/version.h" +#endif //_ESP3D_CONFIG_H diff --git a/esp3d/src/include/sanity_esp3d.h b/esp3d/src/include/sanity_esp3d.h new file mode 100644 index 00000000..fd8d79b0 --- /dev/null +++ b/esp3d/src/include/sanity_esp3d.h @@ -0,0 +1,86 @@ +/* + sanity_esp3d.h - esp3d sanity check functions + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _SANITY_ESP3D_H +#define _SANITY_ESP3D_H + +#include "esp3d_config.h" + +/************************** + * Settings + * ***********************/ +#if defined (SETTINGS_IN_PREFERENCES) && defined( ARDUINO_ARCH_ESP8266) +#error Preferences library is not available for ESP8266 +#endif + +#if defined (SETTINGS_IN_PREFERENCES) && defined( SETTINGS_IN_EEPROM) +#error Choose Preferences or EEPROM for settings not both +#endif + +/************************** + * Serial + * ***********************/ +#if (defined(DEBUG_OUTPUT_SERIAL0) && (defined (DEBUG_OUTPUT_SERIAL1) || defined (DEBUG_OUTPUT_SERIAL2))) || (defined (DEBUG_OUTPUT_SERIAL1) && defined (DEBUG_OUTPUT_SERIAL2)) +#error You can only use one serial for debug +#endif + +#if (defined(USE_SERIAL_0) && (defined (USE_SERIAL_1) || defined (USE_SERIAL_2))) || (defined (USE_SERIAL_1) && defined (USE_SERIAL_2)) +#error You can only use one serial output +#endif + +#if (defined(DEBUG_OUTPUT_SERIAL0) &&defined(USE_SERIAL_0)) || (defined(DEBUG_OUTPUT_SERIAL1) &&defined(USE_SERIAL_1)) || (defined(DEBUG_OUTPUT_SERIAL2) &&defined(USE_SERIAL_2)) +#warning You use same serial for output and debug +#endif + +#if ((defined (USE_SERIAL_2) || defined (DEBUG_OUTPUT_SERIAL2))&& defined( ARDUINO_ARCH_ESP8266)) +#error Serial 2 is not available in ESP8266 +#endif + + +/************************** + * Bluetooth + * ***********************/ +#if defined (BLUETOOTH_FEATURE) && defined( ARDUINO_ARCH_ESP8266) +#error Bluetooth is not available in ESP8266 +#endif + + +/************************** + * Ethernet + * ***********************/ +#if defined (ETH_FEATURE) && defined( ARDUINO_ARCH_ESP8266) +#error Ethernet is not available in ESP8266 +#endif + +/************************** + * Time + * ***********************/ +#if defined(FILESYSTEM_TIMESTAMP_FEATURE) && defined( ARDUINO_ARCH_ESP8266) +#error SPIFFS time is not available in ESP8266 +#endif + +/************************** + * Filesystem + * ***********************/ +#if FILESYSTEM_FEATURE == 1 && defined( ARDUINO_ARCH_ESP8266) +#error Fat FS is not available in ESP8266 +#endif + +#endif //SANITY_ESP3D_H diff --git a/esp3d/src/include/version.h b/esp3d/src/include/version.h new file mode 100644 index 00000000..a10dd38f --- /dev/null +++ b/esp3d/src/include/version.h @@ -0,0 +1,28 @@ +/* + version.h - ESP3D version file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VERSION_ESP3D_H +#define _VERSION_ESP3D_H + +//version and sources location +#define FW_VERSION "3.0.0.a3" +#define REPOSITORY "https://github.com/luc-github/ESP3D" + +#endif //_VERSION_ESP3D_H diff --git a/esp3d/src/main.cpp b/esp3d/src/main.cpp new file mode 100644 index 00000000..d76053b7 --- /dev/null +++ b/esp3d/src/main.cpp @@ -0,0 +1,36 @@ +/* + main esp3d core code + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "core/esp3d.h" + +//global variable +Esp3D myesp3d; + +//Setup +void setup() +{ + myesp3d.begin(); +} + +//main loop +void loop() +{ + myesp3d.handle(); +} diff --git a/esp3d/src/modules/authentication/authentication_service.cpp b/esp3d/src/modules/authentication/authentication_service.cpp new file mode 100644 index 00000000..e6a2983a --- /dev/null +++ b/esp3d/src/modules/authentication/authentication_service.cpp @@ -0,0 +1,336 @@ +/* + authentication_service.cpp - ESP3D authentication service class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "authentication_service.h" +#include "../../core/esp3doutput.h" +#include "../../core/settings_esp3d.h" + +#if defined (AUTHENTICATION_FEATURE) +#if defined (HTTP_FEATURE) +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +Authwebserver * AuthenticationService::_webserver = nullptr; +#endif //HTTP_FEATURE +#endif //AUTHENTICATION_FEATURE + +#if defined (AUTHENTICATION_FEATURE) +String AuthenticationService::_adminpwd=""; +String AuthenticationService::_userpwd=""; +#if defined (HTTP_FEATURE) +uint32_t AuthenticationService::_sessionTimeout = 360000; +auth_ip * AuthenticationService::_head = nullptr; +uint8_t AuthenticationService::_current_nb_ip = 0; +#endif //HTTP_FEATURE +#endif //AUTHENTICATION_FEATURE + +#define MAX_AUTH_IP 10 +//#define ALLOW_MULTIPLE_SESSIONS + +AuthenticationService::AuthenticationService() +{ +} +AuthenticationService::~AuthenticationService() +{ +#if defined (HTTP_FEATURE) && defined (AUTHENTICATION_FEATURE) + end(); +#endif //HTTP_FEATURE && AUTHENTICATION_FEATURE +} + +//check authentification +level_authenticate_type AuthenticationService::authenticated_level(const char * pwd) +{ +#ifdef AUTHENTICATION_FEATURE + level_authenticate_type auth_type = LEVEL_GUEST; + if(pwd != nullptr) { + + if (isadmin(pwd)) { + auth_type = LEVEL_ADMIN; + } + if (isuser (pwd) && (auth_type != LEVEL_ADMIN)) { + auth_type = LEVEL_USER; + } + } else { +#if defined (HTTP_FEATURE) + if (_webserver) { + if (_webserver->hasHeader ("Authorization") ) { + //log_esp3d("Check authorization %",(_webserver->uri()).c_str()); + if (_webserver->authenticate(DEFAULT_ADMIN_LOGIN, _adminpwd.c_str())) { + auth_type = LEVEL_ADMIN; + } else { + if (_webserver->authenticate(DEFAULT_USER_LOGIN, _userpwd.c_str())) { + auth_type = LEVEL_USER; + } + } + } + if (_webserver->hasHeader ("Cookie") ) { + //log_esp3d("Check Cookie %s",(_webserver->uri()).c_str()); + String cookie = _webserver->header ("Cookie"); + int pos = cookie.indexOf ("ESPSESSIONID="); + if (pos != -1) { + int pos2 = cookie.indexOf (";", pos); + String sessionID = cookie.substring (pos + strlen ("ESPSESSIONID="), pos2); + IPAddress ip = _webserver->client().remoteIP(); + //check if cookie can be reset and clean table in same time + auth_type = ResetAuthIP (ip, sessionID.c_str() ); + //log_esp3d("Authentication = %d", auth_type); + } + } + } +#endif //HTTP_FEATURE + } + return auth_type; +#else + return LEVEL_ADMIN; +#endif //AUTHENTICATION_FEATURE +} +#ifdef AUTHENTICATION_FEATURE + +#if defined (HTTP_FEATURE) +uint32_t AuthenticationService::setSessionTimeout(uint32_t timeout) +{ + if (timeout >= 0) { + _sessionTimeout = timeout; + } + return _sessionTimeout; +} +uint32_t AuthenticationService::getSessionTimeout() +{ + return _sessionTimeout; +} +#endif //HTTP_FEATURE + +bool AuthenticationService::begin(Authwebserver * webserver) +{ + end(); + update(); +#if defined (HTTP_FEATURE) + _webserver = webserver; +#endif //HTTP_FEATURE +} +void AuthenticationService::end() +{ +#if defined (HTTP_FEATURE) + _webserver = nullptr; + ClearAllSessions(); +#endif //HTTP_FEATURE +} + +void AuthenticationService::update() +{ + _adminpwd=Settings_ESP3D::read_string (ESP_ADMIN_PWD); + _userpwd=Settings_ESP3D::read_string (ESP_USER_PWD); +} + +void AuthenticationService::handle() +{ +} + +//check admin password +bool AuthenticationService::isadmin (const char *pwd) +{ + if (strcmp(_adminpwd.c_str(), pwd) !=0 ) { + return false; + } else { + return true; + } +} + +//check user password - admin password is also valid +bool AuthenticationService::isuser (const char *pwd) +{ + //it is not user password + if (strcmp(_userpwd.c_str(), pwd)!=0) { + //check admin password + return isadmin (pwd); + } else { + return true; + } +} + +#if defined (HTTP_FEATURE) +//add the information in the linked list if possible +bool AuthenticationService::AddAuthIP (auth_ip * item) +{ + if (_current_nb_ip > MAX_AUTH_IP) { + return false; + } + item->_next = _head; + _head = item; + _current_nb_ip++; + return true; +} + +//Session ID based on IP and time using 16 char +char * AuthenticationService::create_session_ID() +{ + static char sessionID[17]; +//reset SESSIONID + for (int i = 0; i < 17; i++) { + sessionID[i] = '\0'; + } +//get time + uint32_t now = millis(); +//get remote IP + IPAddress remoteIP = _webserver->client().remoteIP(); +//generate SESSIONID + if (0 > sprintf (sessionID, "%02X%02X%02X%02X%02X%02X%02X%02X", remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3], (uint8_t) ( (now >> 0) & 0xff), (uint8_t) ( (now >> 8) & 0xff), (uint8_t) ( (now >> 16) & 0xff), (uint8_t) ( (now >> 24) & 0xff) ) ) { + strcpy (sessionID, "NONE"); + } + return sessionID; +} + +bool AuthenticationService::ClearAllSessions() +{ + while (_head) { + auth_ip * current = _head; + _head = _head->_next; + delete current; + } + _current_nb_ip = 0; + _head = nullptr; + + return true; +} + +bool AuthenticationService::ClearCurrentSession() +{ + String cookie = _webserver->header("Cookie"); + int pos = cookie.indexOf("ESPSESSIONID="); + String sessionID; + if (pos!= -1) { + int pos2 = cookie.indexOf(";",pos); + sessionID = cookie.substring(pos+strlen("ESPSESSIONID="),pos2); + } + return ClearAuthIP(_webserver->client().remoteIP(), sessionID.c_str()); +} + +bool AuthenticationService::CreateSession(level_authenticate_type auth_level, const char * username, const char* session_ID) +{ + auth_ip * current_auth = new auth_ip; + current_auth->level = auth_level; + current_auth->ip=_webserver->client().remoteIP(); + strcpy(current_auth->sessionID, session_ID); + strcpy(current_auth->userID,username); + current_auth->last_time=millis(); +#ifndef ALLOW_MULTIPLE_SESSIONS + //if not multiple session no need to keep all session, current one is enough + ClearAllSessions(); +#endif //ALLOW_MULTIPLE_SESSIONS + if (AddAuthIP(current_auth)) { + return true; + } else { + delete current_auth; + return false; + } +} + +bool AuthenticationService::ClearAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + bool done = false; + while (current) { + if ( (ip == current->ip) && (strcmp (sessionID, current->sessionID) == 0) ) { + //remove + done = true; + if (current == _head) { + _head = current->_next; + _current_nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _current_nb_ip--; + delete current; + current = previous->_next; + } + } else { + previous = current; + current = current->_next; + } + } + return done; +} + +//Get info +auth_ip * AuthenticationService::GetAuth (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + //auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //found + return current; + } + } + //previous = current; + current = current->_next; + } + return NULL; +} + +//Review all IP to reset timers +level_authenticate_type AuthenticationService::ResetAuthIP (IPAddress ip, const char * sessionID) +{ + auth_ip * current = _head; + auth_ip * previous = NULL; + //get time + //uint32_t now = millis(); + while (current) { + //if time out is reached and time out is not disabled + //if IP is not current one and time out is disabled + if ( (((millis() - current->last_time) > _sessionTimeout) && (_sessionTimeout!=0)) || ((ip != current->ip) && (_sessionTimeout==0)) ) { + //remove + if (current == _head) { + _head = current->_next; + _current_nb_ip--; + delete current; + current = _head; + } else { + previous->_next = current->_next; + _current_nb_ip--; + delete current; + current = previous->_next; + } + } else { + if (ip == current->ip) { + if (strcmp (sessionID, current->sessionID) == 0) { + //reset time + current->last_time = millis(); + return (level_authenticate_type) current->level; + } + } + previous = current; + current = current->_next; + } + } + return LEVEL_GUEST; +} +#endif //HTTP_FEATURE + + +#endif //AUTHENTICATION_FEATURE diff --git a/esp3d/src/modules/authentication/authentication_service.h b/esp3d/src/modules/authentication/authentication_service.h new file mode 100644 index 00000000..a1ceef39 --- /dev/null +++ b/esp3d/src/modules/authentication/authentication_service.h @@ -0,0 +1,96 @@ +/* + authentication_service.h - authentication functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _AUTHENTICATION_SERVICE_H +#define _AUTHENTICATION_SERVICE_H +typedef enum { + LEVEL_GUEST = 0, + LEVEL_USER = 1, + LEVEL_ADMIN = 2 +} level_authenticate_type; + +const char DEFAULT_ADMIN_LOGIN [] = "admin"; +const char DEFAULT_USER_LOGIN [] = "user"; + +#include "../../include/esp3d_config.h" +#if defined (AUTHENTICATION_FEATURE) +#if defined (HTTP_FEATURE) +#include +struct auth_ip { + IPAddress ip; + level_authenticate_type level; + char userID[17]; + char sessionID[17]; + uint32_t last_time; + auth_ip * _next; +}; +#if defined (ARDUINO_ARCH_ESP32) +class WebServer; +typedef WebServer Authwebserver; +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +class ESP8266WebServer; +typedef ESP8266WebServer Authwebserver; +#endif //ARDUINO_ARCH_ESP8266 +#else +typedef void Authwebserver; +#endif // HTTP_FEATURE +#endif //AUTHENTICATION_FEATURE +class AuthenticationService +{ +public: + AuthenticationService(); + ~AuthenticationService(); + static level_authenticate_type authenticated_level(const char * pwd = nullptr); +#ifdef AUTHENTICATION_FEATURE + static bool begin(Authwebserver * webserver); + static void end(); + static void handle(); + static bool isadmin (const char *pwd); + static void update(); + static bool isuser (const char *pwd); +#if defined (HTTP_FEATURE) + static uint32_t setSessionTimeout(uint32_t timeout); + static uint32_t getSessionTimeout(); + static char * create_session_ID(); + static bool ClearCurrentSession (); + static bool ClearAllSessions (); + static bool CreateSession(level_authenticate_type auth_level, const char * username, const char* session_ID); +#endif //HTTP_FEATURE +private: + static String _adminpwd; + static String _userpwd; +#if defined (HTTP_FEATURE) + static bool AddAuthIP (auth_ip * item); + static bool ClearAuthIP (IPAddress ip, const char * sessionID); + static auth_ip * GetAuth (IPAddress ip, const char * sessionID); + static level_authenticate_type ResetAuthIP (IPAddress ip, const char * sessionID); + static Authwebserver * _webserver; + static uint32_t _sessionTimeout; + static auth_ip * _head; + static uint8_t _current_nb_ip; +#endif //HTTP_FEATURE +#endif //AUTHENTICATION_FEATURE +}; + +#endif //_ESP3DSECURITY_H + diff --git a/esp3d/src/modules/bluetooth/BT_service.cpp b/esp3d/src/modules/bluetooth/BT_service.cpp new file mode 100644 index 00000000..f4ce5fc4 --- /dev/null +++ b/esp3d/src/modules/bluetooth/BT_service.cpp @@ -0,0 +1,297 @@ +/* + BT_service.cpp - Bluetooth service functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef ARDUINO_ARCH_ESP32 + +#include "../../include/esp3d_config.h" + +#ifdef BLUETOOTH_FEATURE +#include "BluetoothSerial.h" +#include "../../core/esp3doutput.h" +#include "../../core/settings_esp3d.h" +#include "../../core/commands.h" +#include "../network/netconfig.h" +#include "BT_service.h" +BluetoothSerial SerialBT; +#ifdef __cplusplus +extern "C" { +#endif +const uint8_t *esp_bt_dev_get_address(void); +#ifdef __cplusplus +} +#endif + +#define TIMEOUT_BT_FLUSH 1500 + +BTService bt_service; + +String BTService::_btname = ""; +String BTService::_btclient = ""; + +BTService::BTService() +{ + _buffer_size = 0; +} + +BTService::~BTService() +{ + end(); +} + +bool BTService::isConnected() +{ + return ((_btclient.length() > 0)?true:false); +} + +void BTService::setClientAddress(const char * saddress) +{ + _btclient = saddress; +} + +static void my_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) +{ + ESP3DOutput output(ESP_ALL_CLIENTS); + switch (event) { + case ESP_SPP_SRV_OPEN_EVT: { //Server connection open + char str[18]; + str[17]='\0'; + uint8_t * addr = param->srv_open.rem_bda; + sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + BTService::setClientAddress(str); + String stmp = "BT Connected with "; + stmp += str; + output.printMSG(stmp.c_str()); + } + break; + + case ESP_SPP_CLOSE_EVT://Client connection closed + output.printMSG("BT Disconnected"); + BTService::setClientAddress(""); + break; + default: + break; + } +} + +const char* BTService::macAddress() +{ + const uint8_t* point = esp_bt_dev_get_address(); + static char str[18]; + str[17]='\0'; + sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", (int)point[0], (int)point[1], (int)point[2], (int)point[3], (int)point[4], (int)point[5]); + return str; +} + +const char* BTService::clientmacAddress() +{ + return _btclient.c_str(); +} + +/** + * begin BT setup + */ +bool BTService::begin() +{ + ESP3DOutput output(ESP_ALL_CLIENTS); + bool res = true; + _buffer_size = 0; + _lastflush = millis(); + //stop BT Serial if active + end(); + //Get hostname + //this allow to adjust if necessary + _btname = Settings_ESP3D::read_string(ESP_HOSTNAME); + if (Settings_ESP3D::read_byte(ESP_RADIO_MODE) == ESP_BT) { + if (!SerialBT.begin(_btname)) { + output.printERROR("BT failed start"); + res = false; + } else { + SerialBT.register_callback(&my_spp_cb); + String stmp = "BT Started with: '" + _btname + "'"; + output.printMSG(stmp.c_str()); + } + } + return res; +} + +/** + * End BT + */ +void BTService::end() +{ + flush(); + SerialBT.end(); + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printMSG("Bluetooth Off"); + _buffer_size = 0; +} + +/** + * Reset BT + */ +bool BTService::reset() +{ + //nothing to reset + return true; +} + +/** + * Check if BT is on and working + */ +bool BTService::started() +{ + return btStarted(); +} + +/** + * Handle not critical actions that must be done in sync environement + */ +void BTService::handle() +{ + //Do we have some data waiting + size_t len = SerialBT.available(); + if (len > 0) { + //if yes read them + uint8_t * sbuf = (uint8_t *)malloc(len); + if(sbuf) { + size_t count = readBytes(sbuf, len); + //push to buffer + if (count > 0) { + push2buffer(sbuf, count); + } + //freen buffer + free(sbuf); + } + } + //we cannot left data in buffer too long + //in case some commands "forget" to add \n + if (((millis() - _lastflush) > TIMEOUT_BT_FLUSH) && (_buffer_size > 0)) { + flushbuffer(); + } +} + +void BTService::flushbuffer() +{ + ESP3DOutput output(ESP_BT_CLIENT); + _buffer[_buffer_size] = 0x0; + //dispatch command + esp3d_commands.process(_buffer, _buffer_size, &output); + _lastflush = millis(); + _buffer_size = 0; +} + +//push collected data to buffer and proceed accordingly +void BTService::push2buffer(uint8_t * sbuf, size_t len) +{ + for (size_t i = 0; i < len; i++) { + _lastflush = millis(); + //command is defined + if (char(sbuf[i]) == '\n') { + if (_buffer_size < ESP3D_BT_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + flushbuffer(); + } else if (isPrintable (char(sbuf[i]) ) || char(sbuf[i]) == '\r') { + if (_buffer_size < ESP3D_BT_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } else { + flushbuffer(); + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + } else { //it is not printable char + //clean buffer first + if (_buffer_size > 0) { + flushbuffer(); + } + //process char + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + flushbuffer(); + } + } +} + +size_t BTService::write(uint8_t c) +{ + return SerialBT.write(c); +} + +size_t BTService::write(const uint8_t *buffer, size_t size) +{ + if (availableForWrite() >= size) { + return SerialBT.write(buffer, size); + } else { + size_t sizetosend = size; + size_t sizesent = 0; + uint8_t *buffertmp=(uint8_t *)buffer; + uint32_t starttime = millis(); + //loop until all is sent or timeout + while (sizetosend>0 && ((millis() - starttime) < 100)) { + size_t available = availableForWrite(); + if(available>0) { + //in case less is sent + available = SerialBT.write(&buffertmp[sizesent], (available >= sizetosend)?sizetosend:available); + sizetosend-=available; + sizesent+=available; + starttime=millis(); + } else { + Hal::wait(5); + } + } + return sizesent; + } +} + +int BTService::availableForWrite() +{ + return 128;//SerialBT.availableForWrite(); +} + +int BTService::available() +{ + return SerialBT.available(); +} + +int BTService::read() +{ + return SerialBT.read(); +} + +size_t BTService::readBytes(uint8_t * sbuf, size_t len) +{ + return SerialBT.readBytes(sbuf, len); +} + +void BTService::flush() +{ + SerialBT.flush(); +} + +const char * BTService::hostname() +{ + return _btname.c_str(); +} + +#endif // BLUETOOTH_FEATURE + +#endif // ARDUINO_ARCH_ESP32 diff --git a/esp3d/src/modules/bluetooth/BT_service.h b/esp3d/src/modules/bluetooth/BT_service.h new file mode 100644 index 00000000..4a8da408 --- /dev/null +++ b/esp3d/src/modules/bluetooth/BT_service.h @@ -0,0 +1,82 @@ +/* + BT_service.h - Bluetooth service functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _BT_SERVICE_H +#define _BT_SERVICE_H + +#include "Print.h" + +#define ESP3D_BT_BUFFER_SIZE 512 + +class BTService : public Print +{ +public: + BTService(); + ~BTService(); + static void BTEvent(uint8_t event); + const char * hostname(); + static const char* macAddress(); + static const char* clientmacAddress(); + bool begin(); + void end(); + void handle(); + bool reset(); + bool started(); + static void setClientAddress(const char * saddress); + bool isConnected(); + void flush(); + int availableForWrite(); + int available(); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size); + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + int read(); + size_t readBytes (uint8_t * sbuf, size_t len); +private : + static String _btname; + static String _btclient; + uint32_t _lastflush; + uint8_t _buffer[ESP3D_BT_BUFFER_SIZE + 1]; //keep space of 0x0 terminal + size_t _buffer_size; + void push2buffer(uint8_t * sbuf, size_t len); + void flushbuffer(); +}; + +extern BTService bt_service; + +#endif //_BT_SERVICE_H diff --git a/esp3d/src/modules/ethernet/ethconfig.cpp b/esp3d/src/modules/ethernet/ethconfig.cpp new file mode 100644 index 00000000..ab412673 --- /dev/null +++ b/esp3d/src/modules/ethernet/ethconfig.cpp @@ -0,0 +1,171 @@ +/* + ethconfig.cpp - ethernet functions class + + Copyright (c) 2018 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" +#if defined (ETH_FEATURE) +#ifdef ARDUINO_ARCH_ESP32 +#include "esp_eth.h" +#include "dhcpserver/dhcpserver_options.h" +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#endif //ARDUINO_ARCH_ESP8266 +#include "esp3doutput.h" +#include "settings_esp3d.h" +#include "netconfig.h" +#include "ethconfig.h" +bool EthConfig::_started = false; +const uint8_t DEFAULT_AP_MASK_VALUE[] = {255, 255, 255, 0}; +EthConfig::EthConfig() +{ +} + +EthConfig::~EthConfig() +{ + end(); +} + +bool EthConfig::StartSTA() +{ + bool res = true; + if ((Settings_ESP3D::read_byte(ESP_STA_IP_MODE) != DHCP_MODE)) { + int32_t IP = Settings_ESP3D::read_IP(ESP_STA_IP_VALUE); + int32_t GW = Settings_ESP3D::read_IP(ESP_STA_GATEWAY_VALUE); + int32_t MK = Settings_ESP3D::read_IP(ESP_STA_MASK_VALUE); + IPAddress ip(IP), mask(MK), gateway(GW); + res = ETH.config(ip, gateway,mask); + } + return res; +} +/*bool EthConfig::StartSRV() +{ + bool res = true; + //static IP + int32_t IP = Settings_ESP3D::read_IP(ESP_AP_IP_VALUE); + IPAddress ip(IP), mask(DEFAULT_AP_MASK_VALUE), gateway(IP); + if (!ETH.config(ip, gateway,mask)) { + res = false; + log_esp3d("Set static IP error"); + } + //start DHCP server + if(res) { + dhcps_lease_t lease; + lease.enable = true; + lease.start_ip.addr = static_cast(IP) + (1 << 24); + lease.end_ip.addr = static_cast(IP) + (11 << 24); + tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_ETH); + tcpip_adapter_dhcps_option( + (tcpip_adapter_option_mode_t)TCPIP_ADAPTER_OP_SET, + (tcpip_adapter_option_id_t)REQUESTED_IP_ADDRESS, + (void*)&lease, sizeof(dhcps_lease_t) + ); + + if (tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_ETH) != ESP_OK){ + res = false; + log_esp3d("Start DHCP server failed"); + } + } + return res; +}*/ + +/** + * begin WiFi setup + */ +bool EthConfig::begin() +{ + bool res = false; + ESP3DOutput output(ESP_ALL_CLIENTS); + end(); + _started = ETH.begin(); + if (_started) { + output.printMSG("Starting Ethernet"); + res=true; + } else { + output.printERROR("Failed Starting Ethernet"); + } + if (!ETH.setHostname(NetConfig::hostname(true))) { + res = false; + log_esp3d("Set Hostname error"); + } + + int8_t espMode =Settings_ESP3D::read_byte(ESP_RADIO_MODE); + //DHCP is only for Client + if (espMode == ESP_ETH_STA) { + if(!StartSTA()) { + res = false; + output.printMSG ("Failed Starting Client"); + } else { + output.printMSG ("Client started"); + } + + } else { + //if(!StartSRV()){ + // res = false; + // output.printMSG ("Failed Starting Server"); + //} else { + // output.printMSG ("Server started"); + //} + } + + //if ((Settings_ESP3D::read_byte(ESP_STA_IP_MODE) != DHCP_MODE) || (espMode == ESP_ETH_SRV)){ + if ((Settings_ESP3D::read_byte(ESP_STA_IP_MODE) != DHCP_MODE)) { + //as no event to display static IP + output.printMSG (ETH.localIP().toString().c_str()); + } + + //Let wait cable is connected + uint32_t start = millis(); + String stmp ="Checking connection"; + output.printMSG (stmp.c_str()); + while (!ETH.linkUp() && ((millis()-start) < 10000)) { + Hal::wait(1000); + stmp +="."; + output.printMSG (stmp.c_str()); + } + if (!ETH.linkUp()) { + output.printMSG ("Cable disconnected"); + } + return res; +} + +/** + * End WiFi + */ + +void EthConfig::end() +{ + esp_eth_disable(); + _started = false; +} + +bool EthConfig::started() +{ + return _started; +} +/** + * Handle not critical actions that must be done in sync environement + */ + +void EthConfig::handle() +{ +} + + +#endif // ETH_FEATURE + diff --git a/esp3d/src/modules/ethernet/ethconfig.h b/esp3d/src/modules/ethernet/ethconfig.h new file mode 100644 index 00000000..bf458314 --- /dev/null +++ b/esp3d/src/modules/ethernet/ethconfig.h @@ -0,0 +1,48 @@ +/* + ethconfig.h - ethernet functions class + + Copyright (c) 2018 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _ETH_CONFIG_H +#define _ETH_CONFIG_H +#include +#ifdef ARDUINO_ARCH_ESP32 +#include "ETH.h" +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#endif //ARDUINO_ARCH_ESP8266 + +class EthConfig +{ +public: + EthConfig(); + ~EthConfig(); + static bool begin(); + static bool StartSTA(); + //static bool StartSRV(); + static void end(); + static void handle(); + static bool started(); +private : + static bool _started; + +}; + +#endif //_ETH_CONFIG_H diff --git a/esp3d/src/modules/filesystem/esp_filesystem.cpp b/esp3d/src/modules/filesystem/esp_filesystem.cpp new file mode 100644 index 00000000..fb20523c --- /dev/null +++ b/esp3d/src/modules/filesystem/esp_filesystem.cpp @@ -0,0 +1,768 @@ +/* + esp_filesystem.cpp - ESP3D filesystem configuration class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../include/esp3d_config.h" +#include "esp_filesystem.h" +#include "../../core/genLinkedList.h" +#ifdef FILESYSTEM_TIMESTAMP_FEATURE +#include +#endif //FILESYSTEM_TIMESTAMP_FEATURE +#include +#ifdef ARDUINO_ARCH_ESP32 +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +#include +#endif //ESP_FAT_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) +#include "FFat.h" +#endif //ESP_FAT_FILESYSTEM +#include +#endif //ARDUINO_ARCH_ESP32 + +#define ESP_MAX_OPENHANDLE 4 + +File tFile_handle[ESP_MAX_OPENHANDLE]; +#ifdef ARDUINO_ARCH_ESP8266 +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +Dir tDir_handle[ESP_MAX_OPENHANDLE]; +#endif //ESP_SPIFFS_FILESYSTEM +#endif //ARDUINO_ARCH_ESP8266 + +//constructor +ESP_FileSystem::ESP_FileSystem() +{ +} + +//destructor +ESP_FileSystem::~ESP_FileSystem() +{ +} + +//helper to format size to readable string +String & ESP_FileSystem::formatBytes (uint32_t bytes) +{ + static String res; + if (bytes < 1024) { + res = String (bytes) + " B"; + } else if (bytes < (1024 * 1024) ) { + res = String (bytes / 1024.0) + " KB"; + } else if (bytes < (1024 * 1024 * 1024) ) { + res = String (bytes / 1024.0 / 1024.0) + " MB"; + } else { + res = String (bytes / 1024.0 / 1024.0 / 1024.0) + " GB"; + } + return res; +} + +bool ESP_FileSystem::begin() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +#if defined(ARDUINO_ARCH_ESP8266) + return SPIFFS.begin(); +#endif //ARDUINO_ARCH_ESP8266 +#if defined(ARDUINO_ARCH_ESP32) + return SPIFFS.begin(true); +#endif //ARDUINO_ARCH_ESP32 +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return FFat.begin(); +#endif //ESP_FAT_FILESYSTEM +} +void ESP_FileSystem::end() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + SPIFFS.end(); +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + FFat.end(); +#endif //ESP_FAT_FILESYSTEM +} + +size_t ESP_FileSystem::totalBytes() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +#if defined (ARDUINO_ARCH_ESP8266) + fs::FSInfo info; + SPIFFS.info (info); + return info.totalBytes; +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + return SPIFFS.totalBytes(); +#endif //ARDUINO_ARCH_ESP32 +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return FFat.totalBytes(); +#endif //ESP_FAT_FILESYSTEM +} + +size_t ESP_FileSystem::usedBytes() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +#if defined (ARDUINO_ARCH_ESP8266) + fs::FSInfo info; + SPIFFS.info (info); + return info.usedBytes; +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + return SPIFFS.usedBytes(); +#endif //ARDUINO_ARCH_ESP32 +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return (FFat.totalBytes() - FFat.freeBytes()); +#endif //ESP_FAT_FILESYSTEM +} + +size_t ESP_FileSystem::max_update_size() +{ + size_t flashsize = 0; +#if defined (ARDUINO_ARCH_ESP8266) + flashsize = ESP.getFlashChipSize(); + //if higher than 1MB take out SPIFFS + if (flashsize > 1024 * 1024) { + flashsize = (1024 * 1024)-ESP.getSketchSize()-1024; + } else { + fs::FSInfo info; + SPIFFS.info (info); + flashsize = flashsize - ESP.getSketchSize()-info.totalBytes-1024; + } +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + //Is OTA available ? + if (esp_ota_get_running_partition()) { + flashsize = ESP.getFreeSketchSpace() + ESP.getSketchSize(); + } else { + flashsize = 0; + } +#endif //ARDUINO_ARCH_ESP32 + return flashsize; +} + +const char * ESP_FileSystem::FilesystemName() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + return "SPIFFS"; +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return "FAT"; +#endif //ESP_FAT_FILESYSTEM + return "None"; +} + +bool ESP_FileSystem::format() +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + return SPIFFS.format(); +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + /* FFat.end();*/ + return FFat.format(); +#endif //ESP_FAT_FILESYSTEM + return false; +} + +ESP_File ESP_FileSystem::open(const char* path, uint8_t mode) +{ + //do some check + if(((strcmp(path,"/") == 0) && ((mode == ESP_FILE_WRITE) || (mode == ESP_FILE_APPEND))) || (strlen(path) == 0)) { + return ESP_File(); + } + // path must start by '/' + if (path[0] != '/') { + return ESP_File(); + } +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + if (mode != ESP_FILE_READ) { + //check container exists + String p = path; + p.remove(p.lastIndexOf('/') +1); + if (!exists(p.c_str())) { + //log_esp3d("Error opening: %s", path); + return ESP_File(); + } + } +#endif +#if defined (ARDUINO_ARCH_ESP8266) +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + File ftmp = SPIFFS.open(path, (mode == ESP_FILE_READ)?"r":(mode == ESP_FILE_WRITE)?"w":"a"); + if(ftmp) { + //log_esp3d("Success openening: %s", path); + ESP_File esptmp(&ftmp, false,(mode == ESP_FILE_READ)?false:true, path); + return esptmp; + } + (void)mode; + Dir dtmp = SPIFFS.openDir(path); + ESP_File esptmp(&dtmp, true, false, path); + return esptmp; +#endif //ESP_SPIFFS_FILESYSTEM +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + //TODO add support if path = /DIR1/ <- with last / + File tmp = SPIFFS.open(path, (mode == ESP_FILE_READ)?FILE_READ:(mode == ESP_FILE_WRITE)?FILE_WRITE:FILE_APPEND); +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + File tmp = FFat.open(path, (mode == ESP_FILE_READ)?FILE_READ:(mode == ESP_FILE_WRITE)?FILE_WRITE:FILE_APPEND); +#endif //ESP_FAT_FILESYSTEM + ESP_File esptmp(&tmp, tmp.isDirectory(),(mode == ESP_FILE_READ)?false:true, path); + return esptmp; +#endif //ARDUINO_ARCH_ESP32 +} + +bool ESP_FileSystem::exists(const char* path) +{ + bool res = false; + //log_esp3d("Check %s", path); + //root should always be there + if (strcmp(path, "/") == 0) { + return true; + } +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + res = FFat.exists(path); + if (!res) { + ESP_File root = ESP_FileSystem::open(path, ESP_FILE_READ); + if (root) { + res = root.isDirectory(); + } + } +#endif //ESP_FAT_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + String spath = path; + spath.trim(); + if (spath[spath.length()-1] == '/') { + if (spath!="/") { + spath.remove(spath.length()-1); + } + } + res = SPIFFS.exists(spath.c_str()); + if (!res) { + String newpath = spath; + if (newpath[newpath.length()-1] != '/') { + newpath+="/"; + } + newpath+="."; + //log_esp3d("Check %s", newpath.c_str()); + res = SPIFFS.exists(newpath); + if (!res) { + ESP_File f = ESP_FileSystem::open(path, ESP_FILE_READ); + if (f) { + //Check directories + ESP_File sub = f.openNextFile(); + if (sub) { + sub.close(); + res = true; + } + f.close(); + } + } + } +#endif //ESP_SPIFFS_FILESYSTEM + return res; +} + +bool ESP_FileSystem::remove(const char *path) +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + return SPIFFS.remove(path); +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return FFat.remove(path); +#endif //ESP_FAT_FILESYSTEM +} + +bool ESP_FileSystem::mkdir(const char *path) +{ +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + //Use file named . to simulate directory + String p = path; + if (p[p.length()-1] != '/') { + p+="/"; + } + p+="."; + //log_esp3d("Dir create : %s", p.c_str()); + ESP_File f = open(p.c_str(), ESP_FILE_WRITE); + if (f) { + f.close(); + return true; + } else { + return false; + } +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + return FFat.mkdir(path); +#endif //ESP_FAT_FILESYSTEM +} + +bool ESP_FileSystem::rmdir(const char *path) +{ +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + if (!exists(path)) { + return false; + } + bool res = true; + GenLinkedList pathlist; + String p = path; + pathlist.push(p); + while (pathlist.count() > 0) { + File dir = FFat.open(pathlist.getLast().c_str()); + File f = dir.openNextFile(); + bool candelete = true; + while (f) { + if (f.isDirectory()) { + candelete = false; + String newdir = f.name(); + pathlist.push(newdir); + f.close(); + f = File(); + } else { + FFat.remove(f.name()); + f.close(); + f = dir.openNextFile(); + } + } + if (candelete) { + if (pathlist.getLast() !="/") { + res = FFat.rmdir(pathlist.getLast().c_str()); + } + pathlist.pop(); + } + dir.close(); + } + p = String(); + log_esp3d("count %d", pathlist.count()); + return res; +#endif //ESP_FAT_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) +#if defined (ARDUINO_ARCH_ESP8266) + Dir dtmp = SPIFFS.openDir(path); + while (dtmp.next()) { + if (!SPIFFS.remove(dtmp.fileName().c_str())) { + return false; + } + } + return true; +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + String spath = path; + spath.trim(); + if (spath[spath.length()-1] == '/') { + if (spath!="/") { + spath.remove(spath.length()-1); + } + } + log_esp3d("Deleting : %s",spath.c_str()); + File ftmp = SPIFFS.open(spath.c_str()); + if (ftmp) { + File pfile = ftmp.openNextFile(); + while (pfile) { + //log_esp3d("File: %s",pfile.name()); + if (!SPIFFS.remove(pfile.name())) { + pfile.close(); + return false; + } + pfile.close(); + pfile = ftmp.openNextFile(); + } + ftmp.close(); + return true; + } else { + return false; + } +#endif //ARDUINO_ARCH_ESP32 +#endif //ESP_SPIFFS_FILESYSTEM +} + +void ESP_FileSystem::closeAll() +{ + for (uint8_t i = 0; i < ESP_MAX_OPENHANDLE; i++) { +#if defined (ARDUINO_ARCH_ESP8266) + tDir_handle[i] = Dir(); +#endif //ARDUINO_ARCH_ESP8266 + tFile_handle[i].close(); + tFile_handle[i] = File(); + } +} + +ESP_File::ESP_File(const char * name, const char * filename, bool isdir) +{ + _isdir = isdir; + _dirlist = ""; + _isfakedir = isdir; + _index = -1; + _filename = filename; + _name = name; +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + memset (&_lastwrite,0,sizeof(time_t)); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + _iswritemode = false; + _size = 0; +} + +ESP_File::ESP_File(void* handle, bool isdir, bool iswritemode, const char * path) +{ + _isdir = isdir; + _dirlist = ""; + _isfakedir = false; + _index = -1; + _filename = ""; + _name = ""; +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + memset (&_lastwrite,0,sizeof(time_t)); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + _iswritemode = iswritemode; + _size = 0; + if (!handle) { + return ; + } + bool set =false; +#if defined (ARDUINO_ARCH_ESP8266) + if (_isdir) { + for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) && !set; i++) { + if (tDir_handle[i].fileName().length() == 0) { + tDir_handle[i] = *((Dir *)handle); + _index = i; + //Path = filename + if (path) { + _filename = path; + if (_filename == "/") { + _filename = "/."; + } + if (_filename[_filename.length()-1] != '.') { + if (_filename[_filename.length()-2] != '/') { + _filename+="/"; + } + _filename+="."; + } + //log_esp3d("Filename: %s", _filename.c_str()); + //Name + if (_filename == "/.") { + _name = "/"; + } else { + _name = _filename; + if (_name.length() >=2) { + if ((_name[_name.length() - 1] == '.') && (_name[_name.length() - 2] == '/')) { + _name.remove( _name.length() - 2,2); + } + } + _name.remove( 0, _name.lastIndexOf('/')+1); + } + } + //log_esp3d("Name: %s index: %d", _name.c_str(), _index); + set = true; + } + } + return; + } +#endif //ARDUINO_ARCH_ESP8266 + + for (uint8_t i=0; (i < ESP_MAX_OPENHANDLE) && !set; i++) { + if (!tFile_handle[i]) { + tFile_handle[i] = *((File*)handle); + //filename + _filename = tFile_handle[i].name(); + + //if root + if (_filename == "/") { + _filename = "/."; + } + if (_isdir) { + if (_filename[_filename.length()-1] != '.') { + if (_filename[_filename.length()-2] != '/') { + _filename+="/"; + } + _filename+="."; + } + } + //name + if (_filename == "/.") { + _name = "/"; + } else { + _name = _filename; + if (_name.endsWith("/.")) { + _name.remove( _name.length() - 2,2); + _isfakedir = true; + _isdir = true; + } + if (_name[0] == '/') { + _name.remove( 0, 1); + } + int pos = _name.lastIndexOf('/'); + if (pos != -1) { + _name.remove( 0, pos+1); + } + } + //size + _size = tFile_handle[i].size(); + //time +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + _lastwrite = tFile_handle[i].getLastWrite(); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + _index = i; + //log_esp3d("Opening File at index %d",_index); + set = true; + } + } +} + +void ESP_File::close() +{ + if (_index != -1) { +#if defined (ARDUINO_ARCH_ESP8266) + if (_isdir && !_isfakedir) { + //log_esp3d("Closing Dir at index %d", _index); + tDir_handle[_index] = Dir(); + _index = -1; + return; + } +#endif //ARDUINO_ARCH_ESP8266 + //log_esp3d("Closing File at index %d", _index); + tFile_handle[_index].close(); + //reopen if mode = write + //udate size + date + if (_iswritemode && !_isdir) { +#if defined (ARDUINO_ARCH_ESP8266) + File ftmp = SPIFFS.open(_filename.c_str(), "r"); +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) +#if (FILESYSTEM_FEATURE == ESP_SPIFFS_FILESYSTEM) + File ftmp = SPIFFS.open(_filename.c_str()); +#endif //ESP_SPIFFS_FILESYSTEM +#if (FILESYSTEM_FEATURE == ESP_FAT_FILESYSTEM) + File ftmp = FFat.open(_filename.c_str()); +#endif //ESP_FAT_FILESYSTEM +#endif //ARDUINO_ARCH_ESP32 + + if (ftmp) { + _size = ftmp.size(); +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + _lastwrite = ftmp.getLastWrite(); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + ftmp.close(); + } + } +#if defined (ARDUINO_ARCH_ESP32) + tFile_handle[_index] = File(); +#endif //ARDUINO_ARCH_ESP32 + //log_esp3d("Closing File at index %d",_index); + _index = -1; + } +} + +ESP_File ESP_File::openNextFile() +{ + if ((_index == -1) || !_isdir) { + log_esp3d("openNextFile failed"); + return ESP_File(); + } +#if defined (ARDUINO_ARCH_ESP8266) + if(tDir_handle[_index].next()) { + //log_esp3d("Getting next file from %s", _filename.c_str()); + File tmp = tDir_handle[_index].openFile("r"); + while (tmp) { + ESP_File esptmp(&tmp); + esptmp.close(); + String sub = esptmp.filename(); + sub.remove(0,_filename.length()-1); + int pos = sub.indexOf("/"); + if (pos!=-1) { + //is subdir + sub = sub.substring(0,pos); + //log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str()); + String tag = "*" + sub + "*"; + //test if already in directory list + if (_dirlist.indexOf(tag) == -1) {//not in list so add it and return the info + _dirlist+= tag; + String fname = _filename.substring(0,_filename.length()-1) + sub + "/."; + //log_esp3d("Found dir name: %s filename:%s", sub.c_str(), fname.c_str()); + esptmp = ESP_File(sub.c_str(), fname.c_str()); + return esptmp; + } else { //already in list so ignore it + //log_esp3d("Dir name: %s already in list", sub.c_str()); + if(!tDir_handle[_index].next()) { + return ESP_File(); + } else { + tmp = tDir_handle[_index].openFile("r"); + } + } + } else { //is file + //log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str()); + if (sub == ".") { + //log_esp3d("Dir tag, ignore it"); + if(!tDir_handle[_index].next()) { + return ESP_File(); + } else { + tmp = tDir_handle[_index].openFile("r"); + } + } else { + return esptmp; + } + } + } + } + return ESP_File(); +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + File tmp = tFile_handle[_index].openNextFile(); + while (tmp) { + //log_esp3d("tmp name :%s %s", tmp.name(), (tmp.isDirectory())?"isDir":"isFile"); + ESP_File esptmp(&tmp, tmp.isDirectory()); + esptmp.close(); + String sub = esptmp.filename(); + sub.remove(0,_filename.length()-1); + int pos = sub.indexOf("/"); + if (pos!=-1) { + //is subdir + sub = sub.substring(0,pos); + //log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str()); + String tag = "*" + sub + "*"; + //test if already in directory list + if (_dirlist.indexOf(tag) == -1) {//not in list so add it and return the info + _dirlist+= tag; + String fname = _filename.substring(0,_filename.length()-1) + sub + "/."; + //log_esp3d("Found dir name: %s filename:%s", sub.c_str(), fname.c_str()); + esptmp = ESP_File(sub.c_str(), fname.c_str()); + return esptmp; + } else { //already in list so ignore it + //log_esp3d("Dir name: %s already in list", sub.c_str()); + tmp = tFile_handle[_index].openNextFile(); + } + } else { //is file + //log_esp3d("file name:%s name: %s %s sub:%s root:%s", esptmp.filename(), esptmp.name(), (esptmp.isDirectory())?"isDir":"isFile", sub.c_str(), _filename.c_str()); + if (sub == ".") { + //log_esp3d("Dir tag, ignore it"); + tmp = tFile_handle[_index].openNextFile(); + } else { + return esptmp; + } + } + + } + return ESP_File(); +#endif //ARDUINO_ARCH_ESP32 +} + +ESP_File::~ESP_File() +{ + //log_esp3d("Destructor %s index %d",(_isdir)?"Dir":"File", _index); +} + +ESP_File::operator bool() const +{ + if ((_index != -1) || (_filename.length() > 0)) { + //log_esp3d("Bool yes %d %d",_index, _filename.length()); + return true; + } else { + return false; + } +} + +bool ESP_File::isOpen() +{ + return !(_index == -1); +} + +const char* ESP_File::name() const +{ + return _name.c_str(); +} + +const char* ESP_File::filename() const +{ + return _filename.c_str(); +} + +bool ESP_File::isDirectory() +{ + return _isdir; +} + +size_t ESP_File::size() +{ + return _size; +} + +#ifdef FILESYSTEM_TIMESTAMP_FEATURE +time_t ESP_File::getLastWrite() +{ + return _lastwrite; +} +#endif //FILESYSTEM_TIMESTAMP_FEATURE + +int ESP_File::available() +{ + if (_index == -1 || _isdir) { + return 0; + } + return tFile_handle[_index].available(); +} + +size_t ESP_File::write(uint8_t i) +{ + if ((_index == -1) || _isdir) { + return 0; + } + return tFile_handle[_index].write (i); +} + +size_t ESP_File::write(const uint8_t *buf, size_t size) +{ + if ((_index == -1) || _isdir) { + return 0; + } + return tFile_handle[_index].write (buf, size); +} + +int ESP_File::read() +{ + if ((_index == -1) || _isdir) { + return -1; + } + return tFile_handle[_index].read(); +} + +size_t ESP_File::read(uint8_t* buf, size_t size) +{ + if ((_index == -1) || _isdir) { + return -1; + } + return tFile_handle[_index].read(buf, size); +} + +void ESP_File::flush() +{ + if ((_index == -1) || _isdir) { + return; + } + tFile_handle[_index].flush(); +} + +ESP_File& ESP_File::operator=(const ESP_File & other) +{ + //log_esp3d("Copy %s", other._filename.c_str()); + _isdir = other._isdir; + _isfakedir = other._isfakedir; + _index = other._index; + _filename = other._filename; + _name = other._name; + _size = other._size; + _iswritemode = other._iswritemode; + _dirlist = other._dirlist; +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + memcpy(&_lastwrite, &(other._lastwrite), sizeof (time_t)); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + return *this; +} diff --git a/esp3d/src/modules/filesystem/esp_filesystem.h b/esp3d/src/modules/filesystem/esp_filesystem.h new file mode 100644 index 00000000..dffc2f58 --- /dev/null +++ b/esp3d/src/modules/filesystem/esp_filesystem.h @@ -0,0 +1,96 @@ +/* + esp_filesystem.h - ESP3D filesystem configuration class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#define ESP_SPIFFS_FILESYSTEM 0 +#define ESP_FAT_FILESYSTEM 1 +#define ESP_LITTLEFS_FILESYSTEM 2 + +#ifndef _ESP_FILESYSTEM_H +#define _ESP_FILESYSTEM_H +#include "../../include/esp3d_config.h" +#ifdef FILESYSTEM_TIMESTAMP_FEATURE +#include +#endif //FILESYSTEM_TIMESTAMP_FEATURE +#define ESP_FILE_READ 0 +#define ESP_FILE_WRITE 1 +#define ESP_FILE_APPEND 2 + +class ESP_File +{ +public: + ESP_File(void * handle = nullptr, bool isdir =false, bool iswritemode = false, const char * path = nullptr); + ESP_File(const char * name, const char * filename, bool isdir = true); + ~ESP_File(); + operator bool() const; + bool isDirectory(); + const char* name() const; + const char* filename() const; + void close(); + bool isOpen(); + ESP_File & operator=(const ESP_File & other); + size_t size(); +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + time_t getLastWrite(); +#endif //FILESYSTEM_TIMESTAMP_FEATURE + int available(); + size_t write(uint8_t i); + size_t write(const uint8_t *buf, size_t size); + int read(); + size_t read(uint8_t* buf, size_t size); + void flush(); + ESP_File openNextFile(); +private: + String _dirlist; + bool _isdir; + bool _isfakedir; + bool _iswritemode; + int8_t _index; + String _filename; + String _name; + size_t _size; +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + time_t _lastwrite; +#endif //FILESYSTEM_TIMESTAMP_FEATURE +}; + +class ESP_FileSystem +{ +public: + static String & formatBytes (uint32_t bytes); + ESP_FileSystem(); + ~ESP_FileSystem(); + static bool begin(); + static void end(); + static size_t totalBytes(); + static size_t usedBytes(); + static size_t max_update_size(); + static const char * FilesystemName(); + static bool format(); + static ESP_File open(const char* path, uint8_t mode = ESP_FILE_READ); + static bool exists(const char* path); + static bool remove(const char *path); + static bool mkdir(const char *path); + static bool rmdir(const char *path); + static void closeAll(); +private: +}; + + +#endif //ESP_FILESYSTEM_H diff --git a/esp3d/src/modules/http/embedded.h b/esp3d/src/modules/http/embedded.h new file mode 100644 index 00000000..31ad3b91 --- /dev/null +++ b/esp3d/src/modules/http/embedded.h @@ -0,0 +1,401 @@ +/* + embedded.h - ESP3D data file + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//data generated by https://github.com/AraHaan/bin2c +//bin2c Conversion Tool v0.14.0 - Windows - [FINAL]. +#ifndef __embedded_h +#define __embedded_h +/* Generated by bin2c, do not edit manually */ + +/* Contents of file tool.html.gz */ +#define tool_html_gz_size 5916 +const unsigned char tool_html_gz[5916] = { + 0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xED, 0x3C, 0x69, 0x73, 0xDB, 0x46, + 0x96, 0x7F, 0xA5, 0xDD, 0x53, 0x31, 0x89, 0x25, 0x40, 0xE2, 0xE2, 0x2D, 0xD2, 0xE3, 0xC4, 0xF6, + 0x44, 0x5B, 0x76, 0x9C, 0xB2, 0xE4, 0xF1, 0x6E, 0xD9, 0x2E, 0x17, 0x48, 0x34, 0x49, 0xAC, 0x41, + 0x80, 0x02, 0x9A, 0xA2, 0x64, 0x59, 0xFB, 0xDB, 0xF7, 0xBD, 0xEE, 0xC6, 0xC5, 0x4B, 0x47, 0x3C, + 0x3B, 0xF9, 0x90, 0xD0, 0x24, 0x80, 0x3E, 0x5E, 0xBF, 0xFB, 0xE8, 0x86, 0x72, 0xB2, 0xE0, 0xCB, + 0x70, 0x7C, 0xB2, 0x60, 0x9E, 0x3F, 0x3E, 0x49, 0xF9, 0x75, 0xC8, 0xC6, 0xD8, 0x72, 0x33, 0x8B, + 0x23, 0x6E, 0xCC, 0xBC, 0x65, 0x10, 0x5E, 0x0F, 0x52, 0x2F, 0x4A, 0x8D, 0x94, 0x25, 0xC1, 0x6C, + 0x68, 0x2C, 0x53, 0x83, 0xB3, 0x2B, 0x6E, 0xA4, 0xC1, 0x37, 0x66, 0x78, 0xFE, 0xFF, 0xAC, 0x53, + 0x3E, 0xB0, 0x4C, 0xF3, 0xA7, 0xA1, 0xB1, 0x61, 0x93, 0xAF, 0x01, 0x3F, 0xD0, 0x2B, 0xC0, 0x61, + 0x2B, 0x3C, 0xAE, 0xAE, 0x6E, 0x27, 0xB1, 0x7F, 0x5D, 0x59, 0x82, 0xFE, 0xCA, 0xC2, 0x4B, 0xC6, + 0x83, 0xA9, 0x47, 0x7E, 0x63, 0x6B, 0x46, 0xF5, 0xFC, 0x59, 0x7F, 0x9E, 0x04, 0x5E, 0xA8, 0x97, + 0x70, 0x28, 0xC1, 0x72, 0x57, 0x57, 0xC3, 0x30, 0x88, 0x98, 0xB1, 0x60, 0xC1, 0x7C, 0x01, 0x6B, + 0x35, 0x5D, 0xBB, 0xD7, 0xEE, 0x5A, 0xAE, 0x33, 0x9C, 0xC6, 0x61, 0x9C, 0x0C, 0xFE, 0xE6, 0x38, + 0xCE, 0x70, 0xE2, 0x4D, 0xBF, 0xCE, 0x93, 0x78, 0x1D, 0xF9, 0x86, 0x6A, 0x9D, 0xCD, 0x66, 0xB7, + 0xDC, 0x9B, 0x84, 0xEC, 0x66, 0x12, 0x27, 0x3E, 0x4B, 0x06, 0xE6, 0x50, 0xDE, 0x18, 0xE9, 0xCA, + 0x9B, 0x06, 0xD1, 0x1C, 0x1A, 0x96, 0xDE, 0x95, 0xB1, 0x09, 0x7C, 0xBE, 0x10, 0x14, 0xDC, 0x72, + 0xFF, 0x66, 0xB3, 0x08, 0x38, 0x13, 0x23, 0xD8, 0x20, 0x8A, 0x37, 0x89, 0xB7, 0x1A, 0xAE, 0x3C, + 0xDF, 0xC7, 0xE1, 0xF6, 0x72, 0x79, 0xCB, 0x17, 0x37, 0x82, 0x78, 0x2F, 0x0C, 0xE6, 0xD1, 0x20, + 0x64, 0x33, 0x7E, 0xDB, 0x14, 0x8B, 0x8C, 0x39, 0xD2, 0x3B, 0xE6, 0xC9, 0x98, 0xFB, 0xFA, 0x4E, + 0xD3, 0x22, 0x6F, 0x12, 0x42, 0xA8, 0x8E, 0xCA, 0x9B, 0x16, 0x37, 0xD9, 0x52, 0xBD, 0xC3, 0x34, + 0x5F, 0xB2, 0x04, 0x59, 0x16, 0x2A, 0x14, 0x78, 0xBC, 0xCA, 0xC8, 0x82, 0xDB, 0x81, 0xB5, 0xBA, + 0x22, 0x69, 0x1C, 0x06, 0x3E, 0xF9, 0x9B, 0xEF, 0xFB, 0x0A, 0x37, 0x23, 0xE5, 0x49, 0xB0, 0x62, + 0x7E, 0x8E, 0xD0, 0x20, 0xE2, 0x0B, 0x23, 0x9E, 0x19, 0xFC, 0x7A, 0xC5, 0xEA, 0xB1, 0xEF, 0x6B, + 0x37, 0x7B, 0xD8, 0xD7, 0xC7, 0xCF, 0xAD, 0x77, 0xB3, 0x8A, 0xD3, 0x80, 0x07, 0x71, 0x34, 0x48, + 0x58, 0xE8, 0xF1, 0xE0, 0x92, 0x0D, 0xFD, 0x20, 0x5D, 0x85, 0xDE, 0xF5, 0x60, 0x12, 0xC6, 0xD3, + 0xAF, 0x39, 0x7B, 0x50, 0xE8, 0xC4, 0x6A, 0x03, 0xE6, 0x82, 0x43, 0x3E, 0x9B, 0xC6, 0x89, 0x27, + 0x26, 0x46, 0x71, 0xC4, 0x32, 0x59, 0x4D, 0xA7, 0xD3, 0xDB, 0xA6, 0x37, 0x45, 0x38, 0x37, 0x85, + 0xA0, 0xF6, 0x88, 0xCF, 0x34, 0xCD, 0x6C, 0x20, 0xF1, 0x74, 0x6F, 0x30, 0x8B, 0xA7, 0xEB, 0x14, + 0xAE, 0x8B, 0x18, 0x38, 0x50, 0x9A, 0x7A, 0xDB, 0x5C, 0x79, 0x11, 0x0B, 0x6F, 0x96, 0x5E, 0x32, + 0x0F, 0x22, 0x63, 0x12, 0x73, 0x1E, 0x2F, 0x07, 0x36, 0x20, 0xB3, 0x5F, 0x27, 0x14, 0xB7, 0xB6, + 0x38, 0x95, 0xF1, 0x30, 0xF1, 0xFC, 0x60, 0x9D, 0x0E, 0x50, 0xE7, 0x32, 0x65, 0x9F, 0xC4, 0x57, + 0x46, 0xBA, 0xF0, 0xFC, 0x78, 0x33, 0x30, 0x09, 0xCE, 0xC2, 0x6F, 0x32, 0x9F, 0x78, 0x75, 0x53, + 0xC7, 0x4F, 0xD3, 0x6C, 0x6B, 0xC3, 0xFB, 0x0C, 0x52, 0x98, 0x1A, 0xC2, 0x30, 0x72, 0xAE, 0x01, + 0xC3, 0xB2, 0x0E, 0x54, 0x04, 0x68, 0xBB, 0xD9, 0xE5, 0xE8, 0x71, 0x45, 0x6F, 0xE3, 0x27, 0xA3, + 0x40, 0x35, 0x96, 0x68, 0x02, 0xBD, 0x30, 0x12, 0x54, 0xA3, 0x8C, 0x3A, 0x07, 0x79, 0x53, 0xF4, + 0xA1, 0x16, 0xEF, 0xE9, 0x52, 0x9C, 0xDC, 0xD6, 0xA8, 0x59, 0x9C, 0x2C, 0x61, 0x91, 0x88, 0x27, + 0x71, 0x78, 0x53, 0xD5, 0x04, 0x69, 0x49, 0xDE, 0x9A, 0xC7, 0x43, 0xA5, 0xB7, 0x0E, 0x32, 0x32, + 0x23, 0xA7, 0x83, 0xD4, 0xD8, 0xD0, 0xF0, 0x20, 0xE3, 0x6E, 0xB7, 0xDB, 0x87, 0x04, 0x59, 0xB4, + 0x06, 0x4B, 0x6F, 0xCE, 0xA4, 0x9E, 0xED, 0x8A, 0x17, 0x54, 0xEE, 0x7E, 0xE2, 0x0D, 0xA2, 0x94, + 0x71, 0x72, 0x40, 0x7E, 0xDD, 0xAA, 0x94, 0xEF, 0x1C, 0x6B, 0xC4, 0x06, 0x4F, 0xC0, 0xA1, 0x49, + 0xDB, 0x29, 0x0B, 0x87, 0x30, 0x2F, 0x65, 0x06, 0xE8, 0x6A, 0xBC, 0xE6, 0xA4, 0x69, 0xB5, 0x53, + 0xBD, 0x80, 0xBB, 0xD3, 0x57, 0x65, 0xB8, 0xB4, 0x82, 0x9B, 0xAA, 0xA8, 0x3B, 0x1D, 0x6F, 0xC6, + 0xFA, 0x43, 0x98, 0x81, 0x9C, 0x04, 0xAF, 0xF6, 0x08, 0xD2, 0x74, 0x13, 0x3A, 0x7B, 0x59, 0x87, + 0x65, 0xDA, 0xBA, 0xD5, 0x6D, 0xEB, 0xB6, 0xE3, 0xE8, 0xCD, 0x8E, 0xA6, 0x70, 0x40, 0x5E, 0xAF, + 0xB6, 0xEC, 0x4C, 0xAA, 0xEF, 0x84, 0x47, 0xB9, 0x2A, 0x04, 0x91, 0x90, 0xA7, 0xD4, 0x88, 0xEA, + 0x60, 0x53, 0x4A, 0x7E, 0x23, 0x45, 0xED, 0x9A, 0xE6, 0xB0, 0xE4, 0x4B, 0xA7, 0x2C, 0xE2, 0x2C, + 0xD9, 0x76, 0x6F, 0xCB, 0xC0, 0xF7, 0x43, 0x26, 0x43, 0x52, 0xBC, 0x9E, 0x2E, 0x0C, 0xF4, 0x08, + 0xC0, 0xCF, 0xA5, 0x17, 0x05, 0xAB, 0x75, 0x28, 0xFC, 0xCB, 0xF0, 0x70, 0xCF, 0x74, 0x9D, 0xA4, + 0xC0, 0xA2, 0x55, 0x1C, 0x08, 0xE0, 0xF7, 0xD4, 0x18, 0x21, 0xB7, 0x95, 0x97, 0x00, 0x46, 0xC3, + 0x23, 0xF1, 0xE0, 0x81, 0xFA, 0xBC, 0x47, 0x05, 0x97, 0xF1, 0x37, 0x63, 0x9D, 0x62, 0x44, 0x62, + 0x21, 0x9B, 0x72, 0x89, 0x0E, 0xD2, 0xBA, 0xD3, 0xB8, 0xDD, 0x20, 0x78, 0x6E, 0xAC, 0x12, 0x20, + 0x23, 0xB9, 0x3E, 0xEE, 0x48, 0x1D, 0xA7, 0xEB, 0x4D, 0xBA, 0x5B, 0xEE, 0xC1, 0x66, 0x1D, 0xDF, + 0x73, 0x2B, 0x50, 0x94, 0xB3, 0xD5, 0x2B, 0x6D, 0xD2, 0xEB, 0x56, 0x9A, 0x84, 0x03, 0xAE, 0x34, + 0x0D, 0xF6, 0xCC, 0x1C, 0xEC, 0xCE, 0xDC, 0x71, 0xDD, 0x7B, 0x90, 0xB5, 0x7B, 0x1D, 0xB3, 0x6F, + 0x6E, 0x21, 0x6B, 0xD9, 0xF6, 0xC4, 0x35, 0x05, 0xB2, 0xC1, 0x72, 0x7E, 0xA3, 0x84, 0xBA, 0xF0, + 0xA2, 0x6D, 0xB7, 0xDD, 0xC9, 0xBD, 0x57, 0xD9, 0xFE, 0x45, 0x90, 0x90, 0x73, 0x15, 0x0A, 0x7B, + 0xFC, 0x89, 0x89, 0x9F, 0xAD, 0x75, 0x3B, 0x53, 0xFC, 0x3C, 0xDA, 0x9C, 0x50, 0x3F, 0xE6, 0x09, + 0xBB, 0x7E, 0x88, 0xDB, 0xA8, 0x4C, 0x14, 0x58, 0x0B, 0x34, 0x8F, 0x93, 0xED, 0x98, 0xCA, 0x08, + 0xB3, 0xB1, 0x77, 0x91, 0xF9, 0xEF, 0xA4, 0x28, 0x04, 0xA4, 0xC0, 0x42, 0xBE, 0xEA, 0xC5, 0xED, + 0xA0, 0x9A, 0x0F, 0x88, 0xC8, 0x5F, 0x74, 0x56, 0xB4, 0x06, 0xFB, 0x82, 0x68, 0xB5, 0xE6, 0x1F, + 0x31, 0x77, 0x19, 0xCD, 0x82, 0x90, 0x7D, 0x1E, 0x0C, 0x32, 0x7A, 0xF0, 0xD1, 0x58, 0xAF, 0xC2, + 0xD8, 0xF3, 0x8D, 0xC9, 0x1A, 0x7C, 0xCE, 0x5F, 0x6E, 0xE9, 0xFF, 0xD7, 0x2D, 0x0D, 0x8F, 0x1A, + 0x77, 0x7B, 0x32, 0x35, 0x7D, 0xB6, 0x65, 0x64, 0x6E, 0x67, 0xD2, 0xF3, 0xBD, 0x07, 0x09, 0x55, + 0x45, 0xC1, 0xBF, 0x44, 0xFB, 0xE7, 0x11, 0xAD, 0x63, 0x4D, 0x4C, 0x7F, 0x3B, 0x07, 0xB5, 0x26, + 0x1D, 0xBF, 0xD7, 0x7E, 0x98, 0x68, 0xA5, 0xB5, 0xFF, 0x25, 0xDA, 0x3F, 0xB9, 0x68, 0xED, 0x4E, + 0xDF, 0x9B, 0x4C, 0xB3, 0xC2, 0x65, 0x16, 0xC7, 0xC0, 0x91, 0x23, 0x75, 0x8B, 0xD5, 0x35, 0x7B, + 0xFB, 0x60, 0xDF, 0xA3, 0x74, 0xD9, 0x29, 0x40, 0xFE, 0x0D, 0x4B, 0x2E, 0x63, 0xDF, 0x2B, 0x8A, + 0x1D, 0xC1, 0xB2, 0xBC, 0x2A, 0x9E, 0x05, 0x57, 0xCC, 0x1F, 0x7E, 0x83, 0x9C, 0xDD, 0x67, 0x57, + 0xB8, 0x8D, 0x00, 0x9A, 0xA8, 0xB0, 0x92, 0xB0, 0x4C, 0x2C, 0x45, 0xB1, 0xC6, 0x02, 0x95, 0xC5, + 0x06, 0x73, 0x58, 0xEC, 0x38, 0x64, 0x75, 0x92, 0xB8, 0x47, 0xCD, 0x9F, 0x85, 0x10, 0x52, 0x45, + 0x05, 0xB5, 0xB7, 0x22, 0xDE, 0x6D, 0x2D, 0x87, 0x5B, 0x57, 0x53, 0xA8, 0x8A, 0x72, 0x01, 0x14, + 0xEE, 0xE6, 0x40, 0x95, 0x67, 0x99, 0xD5, 0x0A, 0xB0, 0x52, 0x1D, 0x96, 0x3B, 0xA5, 0xAD, 0x1D, + 0x9C, 0xAB, 0xBA, 0x0F, 0x4D, 0x1F, 0xD8, 0x05, 0x1F, 0xF3, 0x2C, 0xB4, 0x54, 0x27, 0x63, 0xBD, + 0x61, 0xA1, 0xE2, 0x9B, 0x95, 0xAC, 0xC1, 0xD6, 0x86, 0xBB, 0x7B, 0x0E, 0xD2, 0xF8, 0x25, 0x6B, + 0x32, 0xA1, 0xEF, 0x61, 0xC7, 0xDF, 0x66, 0x0C, 0x3F, 0x19, 0x1F, 0xB0, 0xA2, 0x2E, 0x69, 0x89, + 0xAD, 0x16, 0xCC, 0x94, 0x44, 0x64, 0x43, 0x7B, 0x95, 0xC4, 0xC6, 0xCF, 0xA1, 0x22, 0xF9, 0x81, + 0xEC, 0xAB, 0xD4, 0xA2, 0x33, 0xFC, 0x64, 0xE8, 0x55, 0x77, 0x02, 0x4C, 0x85, 0x5D, 0xD6, 0xBB, + 0xAD, 0xE2, 0x9D, 0x0C, 0x7B, 0xA5, 0x34, 0x6E, 0xB3, 0xCD, 0x96, 0x0F, 0x27, 0x65, 0x17, 0x9D, + 0x3F, 0x28, 0xED, 0xDB, 0xE6, 0x22, 0xF0, 0xD9, 0x97, 0x80, 0x57, 0x2C, 0xE4, 0xF6, 0xEF, 0x4B, + 0xE6, 0x07, 0x1E, 0xA9, 0x2F, 0xC1, 0x67, 0x4B, 0x8D, 0xEF, 0x76, 0x40, 0xE2, 0xDA, 0xCD, 0x96, + 0x8E, 0xCA, 0xBE, 0x76, 0x0F, 0x21, 0x65, 0x93, 0xD2, 0x69, 0xC2, 0x58, 0x44, 0x20, 0xD5, 0x85, + 0xF9, 0xF9, 0x1E, 0x5D, 0xB7, 0xD3, 0x3D, 0x38, 0x5F, 0xEC, 0xDF, 0xDD, 0x9E, 0xB4, 0xE4, 0xF6, + 0xE6, 0x09, 0x0F, 0x38, 0x5C, 0x5E, 0x9E, 0xFD, 0xEE, 0xBC, 0x20, 0x3C, 0x8E, 0x43, 0xB2, 0x02, + 0x0F, 0x7D, 0xD2, 0x92, 0xCD, 0x27, 0x2D, 0xB9, 0x15, 0x2A, 0x76, 0xC3, 0x4E, 0xFC, 0xE0, 0x92, + 0x4C, 0x43, 0x2F, 0x4D, 0x47, 0x54, 0xB8, 0x16, 0x0A, 0xB3, 0x71, 0xD7, 0x8C, 0x08, 0xC0, 0x23, + 0x8A, 0x90, 0xB1, 0x2D, 0x81, 0x2F, 0x4C, 0xF2, 0xB2, 0xC1, 0xB2, 0xA2, 0xA0, 0x64, 0x91, 0xB0, + 0xD9, 0x88, 0x2E, 0x38, 0x5F, 0xA5, 0x83, 0x56, 0x6B, 0xB3, 0xD9, 0x34, 0xE7, 0x01, 0x5F, 0xAC, + 0x27, 0xCD, 0x69, 0xBC, 0x6C, 0x85, 0xEB, 0xA9, 0x21, 0x1F, 0x5B, 0x02, 0x99, 0x16, 0x07, 0xC2, + 0x5A, 0x4E, 0xD3, 0xA4, 0x84, 0x83, 0x46, 0x33, 0x3E, 0xA2, 0x5F, 0x20, 0xD5, 0x8D, 0xBE, 0xC2, + 0x0A, 0xE9, 0xE5, 0x3C, 0x5F, 0x93, 0x2D, 0x01, 0xB0, 0x10, 0xB3, 0x7A, 0xB8, 0x0C, 0xD8, 0xE6, + 0xE7, 0xF8, 0x6A, 0x44, 0x31, 0x9D, 0xB6, 0x1C, 0x13, 0x7E, 0x6C, 0xD3, 0x84, 0x59, 0x73, 0x19, + 0x61, 0xB0, 0x42, 0x1F, 0x51, 0x71, 0x0B, 0x16, 0xC3, 0xEA, 0x6D, 0x53, 0xC7, 0x01, 0x1A, 0xB0, + 0xD2, 0x0B, 0x59, 0xDD, 0xD2, 0x89, 0x61, 0x69, 0x30, 0x7C, 0xE5, 0xF1, 0x05, 0xF1, 0x47, 0xF4, + 0x4D, 0x07, 0x41, 0x58, 0x5D, 0xF7, 0xC2, 0x71, 0x00, 0x62, 0xD7, 0x25, 0x46, 0x3B, 0x74, 0x7A, + 0x30, 0xAA, 0x6D, 0x87, 0x6D, 0xB8, 0x5C, 0xB8, 0x7D, 0xF8, 0x75, 0x49, 0x1F, 0x7A, 0x9C, 0x3E, + 0x36, 0xD9, 0xA1, 0xE5, 0xB8, 0xA4, 0x67, 0x5E, 0x74, 0x2C, 0x62, 0xB8, 0x3D, 0x62, 0x99, 0xD0, + 0x65, 0x99, 0xED, 0xD0, 0xE8, 0x99, 0x70, 0xE3, 0xB8, 0xA1, 0x03, 0x40, 0x2E, 0x6C, 0x18, 0xEA, + 0xBA, 0xC4, 0x81, 0xE9, 0x7D, 0x27, 0x84, 0xA1, 0x9D, 0x10, 0x60, 0x02, 0x90, 0xDE, 0x05, 0xF6, + 0x38, 0x04, 0x7E, 0xBB, 0xCE, 0x05, 0x4C, 0x71, 0x70, 0x51, 0x78, 0x70, 0x43, 0x43, 0x8D, 0x80, + 0x1B, 0x18, 0x7F, 0x01, 0x8F, 0x30, 0xB2, 0x8F, 0x0B, 0x0B, 0x20, 0x06, 0x02, 0x0E, 0xD5, 0x2A, + 0x17, 0xB8, 0xB6, 0x81, 0x38, 0x14, 0x08, 0x08, 0xC4, 0xAC, 0x10, 0xA1, 0x39, 0x17, 0xB8, 0xBA, + 0x81, 0x58, 0x28, 0xD4, 0x0D, 0x81, 0xBB, 0x21, 0x89, 0xB3, 0xC8, 0x05, 0xE2, 0x20, 0xD7, 0x45, + 0x74, 0x0D, 0x41, 0x3F, 0x3E, 0xB4, 0xC5, 0x18, 0x18, 0x82, 0x33, 0xEC, 0x0B, 0x44, 0x00, 0xE8, + 0x47, 0x28, 0x12, 0x88, 0x23, 0xD7, 0x31, 0x7A, 0xD6, 0x85, 0xD1, 0x31, 0x09, 0x62, 0x81, 0x18, + 0x20, 0x02, 0x3D, 0x94, 0x89, 0x8B, 0x78, 0x02, 0x40, 0x58, 0xDA, 0x45, 0x44, 0x7A, 0x04, 0x51, + 0xB7, 0x49, 0x27, 0x14, 0xEB, 0x02, 0xFD, 0x46, 0x87, 0xB8, 0x40, 0x67, 0x07, 0xD8, 0x0D, 0xF4, + 0xC3, 0xC2, 0x70, 0x07, 0x2C, 0x12, 0x9D, 0x21, 0x0C, 0xBC, 0xB0, 0x1C, 0x04, 0x2B, 0x67, 0x3A, + 0x44, 0x72, 0x16, 0x49, 0x76, 0xBB, 0x04, 0x08, 0x86, 0x95, 0xC4, 0x6A, 0x16, 0xCC, 0x84, 0x9E, + 0x10, 0xB1, 0x84, 0x95, 0x60, 0x3D, 0x89, 0x23, 0xF4, 0x86, 0x82, 0x02, 0x68, 0x46, 0x36, 0x23, + 0x4D, 0xDF, 0x84, 0xA0, 0x7B, 0xC0, 0xD0, 0x0B, 0xA3, 0xD7, 0x47, 0x4A, 0x05, 0xAB, 0x3B, 0x0E, + 0x87, 0xAF, 0x60, 0x48, 0xB3, 0xCD, 0x8B, 0xBB, 0xAC, 0x13, 0xAF, 0x70, 0x81, 0x0E, 0xD9, 0x6E, + 0x14, 0x77, 0xB2, 0xEB, 0x1B, 0xE8, 0x52, 0x0B, 0x95, 0x09, 0x2E, 0x73, 0xF8, 0x82, 0xF2, 0x8E, + 0xC9, 0x09, 0xA4, 0x36, 0x51, 0x6E, 0x1F, 0x59, 0x05, 0x47, 0xC7, 0xAF, 0x82, 0x64, 0xB9, 0x81, + 0x14, 0x08, 0x86, 0xC1, 0x00, 0x18, 0xED, 0xC1, 0x17, 0x8D, 0xE9, 0x8F, 0x1B, 0x94, 0xF1, 0xE1, + 0xE5, 0xCF, 0xEF, 0x4F, 0xFF, 0x64, 0x66, 0x65, 0xF6, 0xDD, 0x8B, 0x9E, 0x8D, 0x10, 0x3B, 0x66, + 0x13, 0xB5, 0xCF, 0x46, 0xD6, 0xBA, 0xC0, 0xFC, 0x76, 0x9F, 0x5B, 0x56, 0x07, 0xDB, 0x7A, 0xD8, + 0xD6, 0x77, 0xF1, 0xB6, 0x0F, 0x12, 0xE8, 0x89, 0x8B, 0x6B, 0xE7, 0x5D, 0xA8, 0x7A, 0xED, 0xAE, + 0x60, 0x78, 0x7E, 0x87, 0x8A, 0x2B, 0x3A, 0x8D, 0x4E, 0x4F, 0x4D, 0x34, 0x72, 0x10, 0x46, 0x19, + 0xB0, 0x91, 0xAD, 0x06, 0xE2, 0xEA, 0xE7, 0x28, 0xA8, 0x07, 0x3B, 0x1F, 0x21, 0x06, 0x88, 0x69, + 0x72, 0x96, 0x00, 0xD6, 0xCF, 0xE0, 0xF7, 0xE5, 0x92, 0x19, 0x40, 0x22, 0x90, 0xC8, 0xAE, 0x02, + 0x55, 0xD1, 0x05, 0xB8, 0xF7, 0xDB, 0x84, 0x67, 0x73, 0x4B, 0xF0, 0xD4, 0x12, 0x92, 0x0B, 0xB8, + 0xEA, 0xB7, 0x37, 0xBD, 0x5E, 0x0F, 0xFA, 0xFA, 0xC2, 0xC4, 0xD1, 0xCA, 0x2D, 0xD0, 0x57, 0x9B, + 0x0B, 0x04, 0x85, 0xE7, 0x68, 0x77, 0x51, 0x9F, 0x01, 0xA9, 0x3E, 0x7A, 0x08, 0xCB, 0x46, 0x7B, + 0x03, 0xDE, 0xD8, 0x30, 0x08, 0x7F, 0xF0, 0x49, 0xDE, 0xE0, 0x15, 0x7A, 0xE0, 0xF6, 0x02, 0x17, + 0x21, 0x36, 0x28, 0xA8, 0x05, 0x6C, 0x27, 0x56, 0x9F, 0xB8, 0x62, 0x39, 0xC0, 0xB9, 0x8B, 0xA4, + 0xC3, 0x08, 0xA3, 0x0B, 0xC0, 0x3A, 0xE8, 0xD0, 0x3A, 0x08, 0xB5, 0x07, 0x4E, 0xC4, 0x42, 0x9D, + 0xEF, 0x10, 0xE9, 0x6A, 0x4C, 0x94, 0x05, 0x5C, 0x01, 0xC5, 0x0B, 0x1B, 0x3D, 0x11, 0x18, 0x6A, + 0x17, 0x9C, 0x82, 0xC5, 0x71, 0x62, 0xCF, 0xE6, 0x7D, 0x29, 0x18, 0x0B, 0xA8, 0x43, 0xE7, 0xD1, + 0x43, 0xE2, 0x1C, 0x47, 0x30, 0x16, 0x17, 0x53, 0x0F, 0xB6, 0x2B, 0xFA, 0x45, 0xB7, 0x98, 0xD1, + 0x43, 0x8B, 0xE9, 0x9A, 0xF2, 0x0A, 0x10, 0xBB, 0xB0, 0xD0, 0x85, 0x05, 0x96, 0x0C, 0x2C, 0x23, + 0x6E, 0xC6, 0x57, 0x17, 0x7A, 0x2F, 0x8C, 0xBE, 0xF0, 0xC7, 0x88, 0x14, 0x50, 0xD2, 0xEB, 0x7F, + 0x7B, 0xE3, 0x82, 0x2B, 0xE8, 0xDA, 0x5D, 0xF0, 0x2A, 0xE8, 0x4D, 0x94, 0x57, 0x14, 0x5F, 0x21, + 0x51, 0x07, 0x57, 0x11, 0xC2, 0x17, 0xF3, 0x1D, 0x98, 0x8A, 0x92, 0x40, 0xB2, 0x2C, 0xF0, 0x24, + 0x48, 0x9A, 0x43, 0x1C, 0xA1, 0x27, 0x96, 0xC5, 0x1D, 0x14, 0x8A, 0xD5, 0x0D, 0x01, 0x16, 0xF8, + 0x13, 0x58, 0x14, 0xF9, 0x8F, 0x28, 0x22, 0xE2, 0x80, 0x45, 0x47, 0xDD, 0x0A, 0xEF, 0x89, 0x0E, + 0x14, 0x9C, 0x05, 0xA0, 0x03, 0x8B, 0x0A, 0x6C, 0x0D, 0x1B, 0x58, 0x6D, 0x72, 0xC3, 0xB1, 0x91, + 0x9F, 0x0F, 0x32, 0xFE, 0x53, 0xAC, 0x96, 0x66, 0x50, 0xF2, 0xEC, 0xB1, 0x7E, 0x22, 0xC2, 0xF5, + 0x88, 0x96, 0x32, 0xE2, 0x52, 0x15, 0x27, 0x32, 0x0D, 0x4A, 0x02, 0xB0, 0xAB, 0x57, 0x1F, 0xFE, + 0xF9, 0xF2, 0xDD, 0xD9, 0xE9, 0xDB, 0xDF, 0xA8, 0x9A, 0xDB, 0xC2, 0x78, 0xDC, 0x92, 0x27, 0x68, + 0x27, 0x2D, 0x88, 0xE1, 0x7B, 0x03, 0xB9, 0xAC, 0x03, 0xC7, 0x27, 0x0B, 0x5B, 0x40, 0x79, 0x73, + 0xF6, 0x0F, 0x9C, 0xBF, 0xB0, 0xE1, 0x27, 0xEB, 0xDA, 0x3F, 0x97, 0xA8, 0x94, 0x46, 0xAD, 0x7E, + 0xFA, 0xFA, 0xE5, 0xD9, 0x7F, 0x9F, 0x9D, 0xBF, 0x7C, 0x43, 0x77, 0x87, 0x66, 0xC7, 0x36, 0xE0, + 0xE6, 0xA0, 0x75, 0x41, 0x5E, 0x41, 0x51, 0x9B, 0x5E, 0xA7, 0x9C, 0x2D, 0x0F, 0xC0, 0x16, 0x69, + 0x1F, 0x00, 0x12, 0x75, 0x31, 0x11, 0x75, 0x31, 0xC5, 0x4A, 0x58, 0xAE, 0x25, 0x6A, 0x62, 0x59, + 0x9C, 0x51, 0x12, 0x79, 0x4B, 0xE8, 0x5C, 0x5E, 0x63, 0x63, 0xFA, 0xF1, 0x33, 0x25, 0xCB, 0x75, + 0xC8, 0x83, 0x15, 0xB2, 0x2C, 0xBB, 0xA3, 0xC0, 0x79, 0x09, 0xA9, 0x70, 0xA3, 0xA4, 0xB4, 0xEB, + 0x4A, 0xD5, 0x0A, 0xB2, 0xBE, 0x96, 0x6B, 0x54, 0x4A, 0x6E, 0x4A, 0xE2, 0x68, 0x1A, 0x06, 0xD3, + 0xAF, 0x23, 0x7A, 0xC6, 0x22, 0x1F, 0x97, 0xAA, 0x6B, 0x43, 0x70, 0x8D, 0x5E, 0xB8, 0x86, 0x79, + 0xEF, 0xC5, 0x58, 0x3A, 0x7E, 0x1A, 0x4D, 0xD2, 0xD5, 0x50, 0xFE, 0x9E, 0xAC, 0x92, 0x78, 0x9E, + 0xB0, 0x34, 0xCD, 0xE4, 0x77, 0x19, 0xA4, 0xC1, 0x24, 0x08, 0x03, 0x7E, 0x3D, 0x00, 0xC6, 0xF9, + 0x2C, 0xCA, 0x50, 0x5F, 0x25, 0x73, 0xB9, 0xA4, 0xB8, 0x81, 0x54, 0x4E, 0xE4, 0x53, 0x42, 0x7D, + 0x14, 0x08, 0x48, 0xC3, 0x12, 0xF9, 0xDD, 0x23, 0xBF, 0x43, 0xAC, 0x53, 0x72, 0x97, 0x39, 0x59, + 0xE6, 0xDD, 0x45, 0x9A, 0xF6, 0x10, 0x56, 0x54, 0xE8, 0xFE, 0x25, 0x5E, 0x42, 0xB5, 0xEF, 0xD7, + 0x6B, 0x61, 0x90, 0xF2, 0x9A, 0x5E, 0xF3, 0xC2, 0xB0, 0x56, 0x62, 0xC3, 0x3B, 0x36, 0x03, 0x6C, + 0x17, 0xB4, 0x50, 0xDD, 0xF2, 0xAA, 0x88, 0x67, 0x0E, 0xED, 0x97, 0x84, 0x41, 0xA8, 0xF0, 0x83, + 0xA4, 0xAE, 0xD1, 0xAD, 0xD8, 0x56, 0x89, 0x46, 0xAE, 0x59, 0x04, 0x23, 0xBC, 0xAF, 0xC4, 0x22, + 0x17, 0xFF, 0xC1, 0xF8, 0x04, 0xF4, 0x80, 0x40, 0x5B, 0x9B, 0x92, 0x6B, 0xE4, 0x1D, 0xCD, 0x66, + 0x3B, 0xA5, 0xD9, 0x36, 0xDC, 0x27, 0x30, 0xC8, 0x86, 0xCB, 0xB5, 0xB8, 0x80, 0x14, 0xC3, 0x11, + 0x55, 0xB5, 0x3B, 0x6D, 0x15, 0x70, 0x70, 0xE8, 0xB5, 0x00, 0x97, 0xC5, 0xC4, 0x76, 0x29, 0x24, + 0xB6, 0xEF, 0x84, 0x83, 0xE6, 0x89, 0x70, 0x2C, 0x89, 0x90, 0x0D, 0x97, 0x7C, 0xC7, 0x02, 0x5A, + 0x7B, 0xEA, 0x71, 0xA3, 0x20, 0xF6, 0x40, 0xD8, 0x0A, 0x88, 0xD8, 0xFC, 0xA0, 0xE3, 0x06, 0x30, + 0x10, 0x60, 0x28, 0xAF, 0xA1, 0x4C, 0x64, 0x8B, 0xA7, 0x2A, 0xE5, 0x46, 0xAE, 0x0A, 0xDD, 0x01, + 0x4F, 0x93, 0x73, 0x32, 0x88, 0x66, 0x71, 0xA6, 0x8D, 0xE5, 0xD9, 0x15, 0x87, 0x20, 0x13, 0x78, + 0x35, 0x43, 0x3E, 0x54, 0x4E, 0xC2, 0x69, 0xA6, 0xB8, 0xC5, 0x76, 0x0D, 0x6A, 0x95, 0x2C, 0x0B, + 0x84, 0x56, 0x2D, 0xCA, 0xF2, 0x3D, 0x07, 0xB5, 0x01, 0xD8, 0x0B, 0x6C, 0x1F, 0xFF, 0x06, 0x8A, + 0x9D, 0x3F, 0x9C, 0x01, 0xD9, 0xD9, 0x43, 0x45, 0x23, 0xB6, 0xDA, 0x14, 0x45, 0xA2, 0x55, 0x61, + 0xAA, 0x16, 0x43, 0x8D, 0xCE, 0x0D, 0xFF, 0x0B, 0x2A, 0x9F, 0x18, 0x27, 0x6B, 0x93, 0xE3, 0x0E, + 0x4E, 0x55, 0x88, 0xD2, 0xC0, 0x52, 0xEE, 0xF1, 0x75, 0x4A, 0x73, 0x8E, 0xEE, 0xFC, 0xDE, 0xE1, + 0xE2, 0x3E, 0xBC, 0xFF, 0xFD, 0xC5, 0xF3, 0xF3, 0x97, 0xC7, 0x1D, 0x9C, 0xCA, 0xE3, 0xC8, 0xFB, + 0x95, 0x0F, 0x2A, 0x7E, 0x87, 0x7F, 0xAB, 0x18, 0xE9, 0x41, 0x77, 0xB7, 0x39, 0xE8, 0xEC, 0x4A, + 0x39, 0xE2, 0x83, 0x1D, 0x1C, 0x3C, 0x94, 0xEC, 0x5B, 0xFA, 0xB0, 0x5D, 0xCF, 0x86, 0x44, 0x94, + 0x97, 0x79, 0x88, 0x5B, 0x9B, 0x6D, 0x72, 0xC7, 0x86, 0xB7, 0xFB, 0x5D, 0x5B, 0x0E, 0x59, 0xC4, + 0x46, 0x1C, 0xBE, 0x4C, 0xE7, 0xF4, 0x30, 0xF8, 0xF1, 0x3B, 0x06, 0x72, 0x4C, 0x38, 0x70, 0x5B, + 0x27, 0xE0, 0xDC, 0xBD, 0x14, 0xCA, 0x50, 0x2F, 0xE0, 0x4D, 0xF8, 0x2F, 0x0B, 0x9F, 0x39, 0xA8, + 0x29, 0x94, 0xF9, 0x28, 0xFE, 0x71, 0x1E, 0x58, 0x0F, 0x04, 0xC6, 0x42, 0x4C, 0x38, 0x2D, 0x8C, + 0xE7, 0xC0, 0x4E, 0x28, 0x87, 0x73, 0x93, 0x12, 0xF5, 0x74, 0x55, 0xF0, 0x95, 0x12, 0x7B, 0x5F, + 0x97, 0xDC, 0x59, 0x81, 0x9E, 0x85, 0x33, 0x3E, 0x05, 0xD4, 0x79, 0x30, 0x0B, 0xA6, 0x62, 0x9F, + 0x14, 0xE2, 0xAB, 0xB3, 0x47, 0xE7, 0x8A, 0x1D, 0x0F, 0x2A, 0x69, 0x18, 0x57, 0x12, 0x06, 0xD9, + 0x8D, 0x9E, 0x81, 0x92, 0x3C, 0xDD, 0x1E, 0xBF, 0x4F, 0xC1, 0x38, 0x15, 0x79, 0x5B, 0x61, 0xAE, + 0x7C, 0x2C, 0x9E, 0xA9, 0x80, 0x9C, 0x2E, 0x88, 0x44, 0xF9, 0x57, 0x92, 0x0B, 0xDC, 0x39, 0x2A, + 0x58, 0xB5, 0x48, 0xEE, 0x8F, 0xC4, 0xEF, 0xD0, 0xB7, 0x01, 0x3F, 0xF1, 0x00, 0x44, 0x56, 0x6A, + 0x8A, 0x42, 0x66, 0x75, 0x1C, 0x99, 0xC9, 0xBE, 0x34, 0xA4, 0xBC, 0x07, 0xB4, 0x95, 0x2C, 0x64, + 0x9A, 0x7E, 0xC8, 0x1E, 0x8A, 0xB8, 0xF6, 0xFA, 0x1D, 0xC6, 0x20, 0xA5, 0xEF, 0x67, 0xEB, 0xC9, + 0x32, 0xE0, 0x7B, 0x3D, 0x44, 0x3A, 0x05, 0xB7, 0xC8, 0xC7, 0x97, 0x5E, 0x42, 0x36, 0xE9, 0x97, + 0x34, 0x5E, 0x27, 0x53, 0xA6, 0x4F, 0xD7, 0x09, 0xEE, 0x5A, 0xA3, 0xF7, 0x1D, 0xD1, 0x16, 0xD5, + 0x01, 0xE9, 0x05, 0x0A, 0x5A, 0x8A, 0x79, 0xF4, 0xC4, 0xD2, 0x37, 0x6C, 0xB2, 0x16, 0x46, 0x84, + 0x0F, 0xB3, 0x3C, 0xEF, 0x51, 0x5D, 0x69, 0x3C, 0xFD, 0xCA, 0xF8, 0x97, 0x55, 0x9C, 0xF0, 0x91, + 0xA9, 0x7B, 0xE9, 0x75, 0x34, 0xFD, 0x02, 0xCD, 0x50, 0xC5, 0x2D, 0xD7, 0x51, 0x09, 0x0A, 0xEA, + 0xE2, 0x17, 0xE4, 0x13, 0xD5, 0x41, 0x37, 0xBF, 0xC4, 0xB3, 0x59, 0x15, 0x80, 0xB0, 0x08, 0xE6, + 0x43, 0xE3, 0x70, 0xB6, 0x8E, 0xC4, 0xC6, 0x3C, 0x98, 0xE1, 0xE5, 0xC4, 0x83, 0x00, 0x7B, 0x83, + 0x28, 0x03, 0x6D, 0x25, 0x67, 0x43, 0x75, 0x3E, 0x2A, 0xA1, 0xDE, 0x4C, 0x57, 0x60, 0x61, 0x75, + 0x20, 0x40, 0xD3, 0x23, 0x41, 0x47, 0x30, 0x02, 0x40, 0x71, 0x52, 0x67, 0x0D, 0x9C, 0xE7, 0x2B, + 0x36, 0xD6, 0x64, 0x88, 0xAE, 0x91, 0x9C, 0x7D, 0x9F, 0x68, 0x99, 0x03, 0xB5, 0x56, 0x6D, 0x48, + 0x0E, 0x27, 0x0A, 0x9F, 0xE8, 0xB8, 0x25, 0x0C, 0x90, 0x0E, 0x83, 0x13, 0xDE, 0x0C, 0x59, 0x34, + 0xE7, 0x0B, 0xC3, 0x1A, 0x6A, 0x07, 0x56, 0x39, 0xB0, 0x08, 0x6D, 0xD4, 0xA3, 0xC6, 0x88, 0x7F, + 0x0C, 0x3E, 0x37, 0x10, 0xE3, 0x06, 0xBD, 0x6B, 0x51, 0xDA, 0x90, 0x83, 0x73, 0x57, 0xA3, 0xB0, + 0xD0, 0x83, 0x46, 0x63, 0x98, 0x30, 0xBE, 0x4E, 0x22, 0x22, 0x50, 0x28, 0xFB, 0x05, 0x7A, 0x9B, + 0x33, 0x12, 0xD4, 0x3C, 0x5D, 0x7C, 0x09, 0x40, 0x7F, 0x81, 0x99, 0x72, 0x3C, 0xCD, 0x92, 0x85, + 0x5A, 0xDB, 0xAE, 0x41, 0x90, 0xAF, 0x59, 0x70, 0x81, 0xB4, 0xA0, 0xD6, 0xA9, 0x61, 0x5A, 0x80, + 0x17, 0x19, 0xD5, 0x6A, 0x76, 0xBB, 0x96, 0xE5, 0x0E, 0xB5, 0x6E, 0x4D, 0xA9, 0x78, 0x0D, 0xC3, + 0xFD, 0x20, 0x61, 0xFE, 0xB0, 0x46, 0x5A, 0x80, 0xC8, 0x2E, 0xB8, 0xFD, 0x00, 0xEC, 0x2A, 0x00, + 0x91, 0x2E, 0xEC, 0x80, 0x70, 0x4C, 0x09, 0xA2, 0x77, 0x00, 0xA3, 0x4E, 0xB7, 0x00, 0x08, 0xDE, + 0xF8, 0x6E, 0x9C, 0xEC, 0x2A, 0x40, 0xCB, 0x94, 0x10, 0xF1, 0xAA, 0x40, 0xF6, 0xCA, 0x20, 0xDD, + 0x7B, 0x43, 0xB4, 0xFB, 0x7B, 0x21, 0x38, 0xF7, 0xA1, 0xD2, 0x95, 0x20, 0x5C, 0x47, 0x22, 0xD5, + 0x95, 0x38, 0x75, 0x73, 0x80, 0x25, 0x78, 0x9D, 0x7B, 0x01, 0xEC, 0xFC, 0x68, 0x80, 0xBD, 0x1F, + 0x01, 0x50, 0xA6, 0x80, 0x08, 0xB6, 0xC8, 0x8A, 0x6B, 0xB6, 0x5B, 0x52, 0x09, 0xB8, 0xCF, 0xB2, + 0xE2, 0x9A, 0xD8, 0xA1, 0xB1, 0x71, 0xBB, 0xA0, 0x57, 0x1B, 0xFF, 0x48, 0x15, 0xFD, 0xA3, 0xFA, + 0xF9, 0x63, 0x95, 0xF3, 0x07, 0x6B, 0xE6, 0x1F, 0x55, 0xCB, 0x1F, 0xAB, 0x93, 0x3F, 0x56, 0x21, + 0xFF, 0x25, 0xDA, 0x58, 0xB8, 0x46, 0x3C, 0x4B, 0xD9, 0xF6, 0x8C, 0xF7, 0x55, 0x54, 0xDB, 0x85, + 0x7F, 0xB5, 0x7C, 0x5F, 0xB0, 0xF6, 0xA6, 0xAB, 0x3B, 0xE4, 0xB5, 0xAD, 0xF7, 0xC8, 0xEB, 0xAE, + 0x6E, 0x39, 0xE2, 0xD7, 0x24, 0xAF, 0x2D, 0x75, 0xE9, 0xE9, 0x96, 0x25, 0x2F, 0x6D, 0xD9, 0xD8, + 0x81, 0x8B, 0x29, 0x2E, 0x7D, 0xDD, 0xEA, 0x8A, 0xDF, 0xBE, 0x68, 0xB2, 0x61, 0xB8, 0xAD, 0x2E, + 0xB6, 0x6E, 0xF5, 0xC4, 0xA5, 0x27, 0xDA, 0x3A, 0x08, 0xB5, 0x43, 0xBE, 0x21, 0x81, 0x49, 0xFC, + 0x15, 0x28, 0x14, 0xBB, 0x30, 0x35, 0x59, 0x7F, 0xD5, 0x04, 0xA5, 0x7B, 0x09, 0x95, 0x09, 0xF8, + 0x17, 0xAC, 0x58, 0x99, 0x76, 0x53, 0x8A, 0x47, 0x8D, 0x11, 0xC3, 0x30, 0xA4, 0x97, 0x23, 0x10, + 0x15, 0x25, 0x8A, 0x4E, 0x21, 0x02, 0x51, 0xAD, 0x80, 0x01, 0x51, 0x1D, 0x0F, 0xB9, 0xCF, 0xA0, + 0xBE, 0x8A, 0xE6, 0x69, 0x9D, 0xE9, 0x3C, 0x63, 0x5A, 0x9D, 0x8D, 0x58, 0x93, 0xC7, 0xAF, 0xE3, + 0x0D, 0x4B, 0x7E, 0x81, 0x84, 0xB6, 0xAE, 0x69, 0x27, 0x75, 0x3E, 0xE2, 0x5B, 0x6D, 0xCF, 0x0C, + 0x6B, 0xC0, 0x4F, 0xD8, 0x33, 0x6B, 0x60, 0x16, 0x50, 0xF1, 0x40, 0xC9, 0xE3, 0xD3, 0x85, 0xC8, + 0x2D, 0x44, 0x81, 0x83, 0x18, 0x62, 0xCC, 0xE7, 0x98, 0x2E, 0x60, 0xFA, 0x30, 0x0C, 0x66, 0x00, + 0x8D, 0x96, 0xB7, 0x27, 0xCE, 0xC4, 0xC8, 0x01, 0xA1, 0x0D, 0xD6, 0x94, 0xB3, 0x74, 0xDE, 0xA8, + 0x0E, 0xF9, 0x5E, 0x7E, 0x38, 0x8F, 0xB9, 0x17, 0x12, 0x79, 0x2C, 0x2F, 0x26, 0x71, 0x6C, 0x38, + 0x3E, 0x07, 0x32, 0x53, 0xBF, 0x3C, 0x65, 0x0D, 0xCF, 0xC7, 0x67, 0xBC, 0x9D, 0x4E, 0xD7, 0x2B, + 0xF9, 0x42, 0x34, 0xA1, 0x62, 0xE8, 0xC9, 0x92, 0x41, 0x72, 0x47, 0x96, 0x41, 0x04, 0x4A, 0x53, + 0x13, 0x25, 0x84, 0xF4, 0x0B, 0x0B, 0xD0, 0xAA, 0x51, 0xAD, 0x0F, 0x77, 0x32, 0x75, 0xAB, 0xE1, + 0x0A, 0x71, 0x3E, 0x1F, 0xB2, 0x02, 0x10, 0xA2, 0x98, 0xAC, 0x0A, 0xE1, 0xED, 0xFE, 0x9F, 0xA8, + 0xEE, 0xC7, 0xD3, 0xF5, 0x12, 0xE4, 0xD8, 0x9C, 0x33, 0xFE, 0x32, 0x64, 0x78, 0xFB, 0xF3, 0xF5, + 0x29, 0xC8, 0x4F, 0x15, 0x8A, 0x5A, 0x33, 0x88, 0x22, 0x96, 0xFC, 0x7A, 0xFE, 0xE6, 0xF5, 0x88, + 0xEB, 0x82, 0x9D, 0x20, 0xEA, 0x27, 0xE5, 0xD4, 0x49, 0x72, 0x3A, 0xA8, 0x64, 0x53, 0x90, 0xC5, + 0xF0, 0x53, 0x3C, 0xEA, 0x7E, 0x3B, 0xC3, 0x9C, 0x4A, 0xAF, 0xF4, 0xC9, 0xA4, 0xC7, 0xD6, 0x86, + 0x82, 0x3A, 0x9E, 0x64, 0x96, 0x56, 0x7E, 0x75, 0xED, 0x48, 0xEA, 0x53, 0xC9, 0xDA, 0x60, 0x08, + 0xAB, 0x9B, 0x90, 0xC6, 0x58, 0xF7, 0xC8, 0x82, 0x30, 0xC1, 0x82, 0x54, 0xA8, 0x64, 0xB1, 0x45, + 0x46, 0x04, 0xAA, 0x19, 0x62, 0x9E, 0x0D, 0xFE, 0xA1, 0x36, 0x86, 0xCA, 0x15, 0x2B, 0xA9, 0xAC, + 0x50, 0xA2, 0xB7, 0xAC, 0x29, 0x74, 0xAB, 0x09, 0xF8, 0xF1, 0x7A, 0xA6, 0x77, 0x65, 0xF5, 0xDD, + 0xD1, 0xEC, 0x26, 0x96, 0x80, 0x3A, 0x17, 0x17, 0xED, 0x56, 0x43, 0xAE, 0x8D, 0xCA, 0x2C, 0x7A, + 0xFA, 0xB4, 0x0E, 0x7A, 0x69, 0x6A, 0x22, 0xC7, 0x44, 0x06, 0xC6, 0x23, 0x73, 0x18, 0x9F, 0x64, + 0x0B, 0x49, 0x26, 0x0D, 0xE3, 0x46, 0x43, 0xA3, 0x86, 0x05, 0x0C, 0x97, 0x80, 0xEB, 0xAA, 0xFF, + 0x63, 0xFC, 0xB9, 0x89, 0x7B, 0x29, 0x1A, 0x80, 0x11, 0x5C, 0x3C, 0x7F, 0x37, 0x56, 0xDA, 0x22, + 0xEA, 0x48, 0xF0, 0x3F, 0x65, 0xA7, 0x53, 0xF2, 0x45, 0x7B, 0xFC, 0x0F, 0x19, 0x7F, 0xE2, 0x85, + 0x0B, 0xB2, 0x74, 0x1B, 0x5C, 0x88, 0x6E, 0x5B, 0xE8, 0x88, 0x6C, 0xBC, 0xEF, 0xC8, 0x4B, 0x57, + 0xB4, 0x59, 0xE8, 0x3C, 0x5E, 0x5B, 0xB6, 0xFA, 0xB5, 0x08, 0x0E, 0xB3, 0xEE, 0xE1, 0x4E, 0xF0, + 0x15, 0x15, 0x72, 0x65, 0xC9, 0x20, 0x7C, 0x8D, 0xD7, 0x1A, 0xB9, 0xB2, 0xE1, 0x02, 0x6E, 0xF7, + 0xDA, 0x16, 0x01, 0x70, 0x0B, 0x82, 0x7C, 0x34, 0x14, 0xF2, 0x56, 0xAD, 0x95, 0x91, 0xA8, 0xF6, + 0x84, 0x44, 0x02, 0x0B, 0x0D, 0xB5, 0x93, 0xF3, 0x17, 0x2A, 0x71, 0xFE, 0xA4, 0x32, 0xE7, 0x4F, + 0x99, 0xFB, 0xA6, 0xF9, 0x5B, 0x00, 0xAB, 0xAB, 0x21, 0xC5, 0x63, 0x24, 0x79, 0x6E, 0x54, 0x03, + 0x53, 0x10, 0xBE, 0xAB, 0xC4, 0x50, 0x14, 0x55, 0xA3, 0x96, 0x9F, 0x0D, 0xC9, 0xA3, 0xA1, 0x4A, + 0xB5, 0x55, 0xDA, 0xAF, 0xAE, 0xE1, 0xCA, 0x5B, 0x93, 0x75, 0x2A, 0x5E, 0xEF, 0x68, 0xE2, 0x9F, + 0xCD, 0x34, 0xE7, 0xDF, 0x40, 0x6A, 0x5B, 0x03, 0x9E, 0x3E, 0x2D, 0x8D, 0xD8, 0xED, 0xFE, 0xFE, + 0x1D, 0x75, 0xC2, 0xD2, 0x14, 0x95, 0xB2, 0xF2, 0xC2, 0x6D, 0xF0, 0xF3, 0x17, 0x63, 0xA0, 0x51, + 0x52, 0xBB, 0xA5, 0x01, 0x6A, 0xAC, 0x1C, 0x91, 0x09, 0xDA, 0xFC, 0xA9, 0x56, 0xC6, 0xFB, 0x93, + 0xDA, 0x58, 0xFC, 0x44, 0x4B, 0x56, 0xF5, 0x02, 0xDC, 0x39, 0x67, 0x75, 0xE1, 0x34, 0xAA, 0x2C, + 0xA0, 0x35, 0x0D, 0x4B, 0x06, 0x84, 0x5C, 0xCE, 0xFB, 0x2B, 0x58, 0xE1, 0x7A, 0xD2, 0x3C, 0xF0, + 0x07, 0xB5, 0xAF, 0xD0, 0xE4, 0x10, 0x34, 0x39, 0xDC, 0xD6, 0xE4, 0x50, 0x69, 0xF2, 0x68, 0x5B, + 0x93, 0xC3, 0x5D, 0x4D, 0x7E, 0x94, 0x0E, 0x97, 0x34, 0xB8, 0x2F, 0xE3, 0x64, 0x1F, 0x23, 0x1E, + 0x44, 0x4B, 0x08, 0x8C, 0xEA, 0xA7, 0x8D, 0xB1, 0xCF, 0x45, 0x8D, 0x75, 0x51, 0xC7, 0xDB, 0x42, + 0xD1, 0x6D, 0x31, 0x14, 0x2F, 0x18, 0x31, 0x51, 0xED, 0x1D, 0x31, 0xBF, 0x2D, 0x7E, 0x6D, 0xA9, + 0xF5, 0xD0, 0x7F, 0xBF, 0x80, 0x59, 0xA8, 0x25, 0x45, 0x81, 0x54, 0x0B, 0x3A, 0x92, 0x2B, 0x50, + 0x9E, 0x5E, 0xEC, 0xBE, 0x9A, 0x54, 0x76, 0x7D, 0xA5, 0x98, 0x5B, 0x12, 0x54, 0x58, 0x08, 0x6A, + 0x98, 0x49, 0x6A, 0xAB, 0xAF, 0xA2, 0x16, 0xB9, 0xDD, 0x3C, 0x56, 0x4B, 0x0E, 0xAF, 0xFF, 0x28, + 0x45, 0x39, 0x18, 0x74, 0xF0, 0xE4, 0xA5, 0x1C, 0x71, 0xA2, 0x67, 0x14, 0xCF, 0x49, 0x48, 0xC5, + 0xAC, 0x48, 0x90, 0x42, 0x30, 0x4C, 0xD3, 0xF2, 0xEE, 0x97, 0x3C, 0xAA, 0x20, 0x01, 0xA7, 0x03, + 0x9A, 0xD9, 0x38, 0x94, 0xE2, 0x8A, 0x32, 0x52, 0xDB, 0xDA, 0xFC, 0xA8, 0x8D, 0xFF, 0x11, 0x13, + 0x1E, 0x13, 0xF9, 0xE6, 0x47, 0x50, 0x1C, 0x3D, 0x79, 0xE3, 0x23, 0x21, 0xB1, 0xD8, 0x7A, 0xAD, + 0x46, 0xC5, 0x83, 0x13, 0xC4, 0x8E, 0x74, 0x85, 0x1E, 0xB5, 0x1F, 0x51, 0xA4, 0x2E, 0xCA, 0x0A, + 0x31, 0xA1, 0x8A, 0xA3, 0x59, 0x90, 0x2C, 0xEB, 0xF4, 0x17, 0x79, 0x43, 0x7C, 0xEC, 0xC2, 0x31, + 0xF1, 0x0C, 0x55, 0x4D, 0x66, 0x10, 0x60, 0x26, 0x95, 0x34, 0x4B, 0x0C, 0x62, 0x54, 0x67, 0x3B, + 0x30, 0xB3, 0x3C, 0xED, 0x18, 0x58, 0x18, 0x03, 0xEA, 0x15, 0x27, 0xD7, 0x47, 0x60, 0xC3, 0x98, + 0x2A, 0xF8, 0xD2, 0xC1, 0x85, 0xDA, 0x57, 0x59, 0x25, 0x10, 0xFC, 0x78, 0x9D, 0xBE, 0xC8, 0xC0, + 0x89, 0xFD, 0x4F, 0xC8, 0x14, 0x40, 0xD6, 0xD1, 0x3A, 0x0C, 0xC1, 0xD1, 0x6D, 0x81, 0x9E, 0x66, + 0x30, 0x00, 0x74, 0x13, 0xFC, 0xC1, 0x12, 0x52, 0xBB, 0x62, 0x85, 0xF2, 0x50, 0x11, 0x63, 0x71, + 0x99, 0x68, 0x14, 0xB1, 0x0D, 0xF9, 0xAF, 0x37, 0xAF, 0x7F, 0xE5, 0x7C, 0xF5, 0x8E, 0x5D, 0xAC, + 0x21, 0xD5, 0xD3, 0x83, 0x11, 0x6D, 0x09, 0x9D, 0x7C, 0x26, 0x5F, 0xC5, 0x1C, 0x01, 0x19, 0xF7, + 0x57, 0x2F, 0xE4, 0x49, 0x04, 0x18, 0x83, 0x26, 0x35, 0x9B, 0x4D, 0xDC, 0x0C, 0x81, 0xC4, 0x0C, + 0xC1, 0xC9, 0xED, 0xDB, 0x06, 0x8B, 0xA6, 0xB1, 0xCF, 0xDE, 0xBF, 0x3B, 0xAD, 0x73, 0x4D, 0x76, + 0xCA, 0x5D, 0xAE, 0x52, 0x47, 0x39, 0x0F, 0xD2, 0xA3, 0x66, 0x1C, 0x01, 0x61, 0xFE, 0x35, 0x26, + 0x4F, 0x6C, 0x0A, 0x89, 0xCC, 0x9C, 0x8D, 0xF2, 0x6C, 0x41, 0xBB, 0x71, 0x47, 0xA3, 0xA8, 0x29, + 0x06, 0x60, 0xEE, 0x09, 0x3C, 0xA9, 0xDB, 0xA6, 0x89, 0x6D, 0x32, 0xD9, 0x7A, 0xB6, 0x27, 0x8F, + 0xFD, 0xCF, 0xB3, 0xB7, 0xBF, 0x41, 0xA0, 0x4A, 0x20, 0xF9, 0xC5, 0xA9, 0xE9, 0x2A, 0x8E, 0x52, + 0x76, 0xCE, 0xAE, 0xB8, 0xA6, 0x0D, 0x5C, 0xD3, 0x2A, 0x4D, 0x7E, 0xF7, 0xBA, 0xAE, 0x0D, 0xEA, + 0x20, 0xEE, 0x34, 0x0E, 0x59, 0x33, 0x8C, 0xE7, 0xF5, 0xAC, 0x4B, 0xD3, 0x5F, 0x7D, 0x78, 0x99, + 0x24, 0xE0, 0x9D, 0x35, 0xE0, 0x32, 0x62, 0xB9, 0x62, 0x51, 0x9D, 0xFE, 0xE3, 0xE5, 0x39, 0x90, + 0xAC, 0x43, 0x0E, 0x02, 0x4D, 0x29, 0xB0, 0xBC, 0xBE, 0x25, 0x02, 0xB9, 0x13, 0xAE, 0x64, 0x7C, + 0xD4, 0x2A, 0xB2, 0xCD, 0x79, 0x4D, 0x3A, 0x08, 0x4C, 0xB6, 0x4D, 0x8C, 0x6E, 0xD2, 0xF7, 0x6B, + 0x37, 0x07, 0x27, 0x57, 0x8F, 0x18, 0xB5, 0x66, 0xE5, 0x34, 0x31, 0x13, 0xCB, 0x61, 0xFB, 0x4A, + 0xE6, 0x30, 0x47, 0x38, 0xD2, 0x66, 0xB1, 0x4B, 0xAE, 0x76, 0xCC, 0x43, 0x46, 0x87, 0xB2, 0x04, + 0x40, 0xBD, 0x79, 0x15, 0x27, 0xCB, 0x17, 0x1E, 0xF7, 0x86, 0xBC, 0xE9, 0xAD, 0x56, 0x48, 0xAC, + 0xB4, 0xCE, 0x72, 0x66, 0x5A, 0x44, 0xB0, 0x08, 0x22, 0x58, 0x74, 0x92, 0xE1, 0x3F, 0x8C, 0x20, + 0x76, 0xA9, 0x24, 0x97, 0x7D, 0x8C, 0x3E, 0xEB, 0x71, 0x39, 0x91, 0x6B, 0x04, 0xCA, 0x19, 0x9E, + 0xD1, 0x02, 0x78, 0xAC, 0x07, 0x32, 0xAA, 0xE9, 0xC5, 0x7A, 0xC5, 0x91, 0x05, 0x70, 0x7D, 0x17, + 0x80, 0x76, 0x2B, 0x63, 0xE7, 0xAE, 0x96, 0x0F, 0x43, 0x25, 0xB1, 0xDF, 0xDF, 0x9E, 0x9D, 0x63, + 0xFE, 0x2D, 0xE0, 0x50, 0x21, 0xB9, 0xB0, 0x29, 0x59, 0xD8, 0x84, 0x30, 0xF2, 0xF2, 0x12, 0x20, + 0xBE, 0x06, 0xFF, 0xC4, 0x40, 0xC1, 0x91, 0x3B, 0xF2, 0x7C, 0x81, 0xEA, 0x45, 0xC2, 0xAA, 0xDD, + 0x80, 0x68, 0x32, 0xBA, 0xC0, 0xC4, 0x56, 0x6B, 0xB1, 0xD5, 0x97, 0x15, 0x4B, 0xA8, 0x37, 0x9E, + 0xCF, 0xFC, 0x96, 0x2A, 0x6E, 0xFE, 0x03, 0xEA, 0x8C, 0xC3, 0xE6, 0x24, 0xD9, 0x2F, 0x45, 0x76, + 0xC4, 0x0B, 0xDE, 0x25, 0x63, 0x82, 0x42, 0x6E, 0x60, 0xA1, 0xF7, 0x0A, 0x5F, 0x92, 0xAD, 0x9B, + 0x1A, 0x56, 0x26, 0xB7, 0xB7, 0x3A, 0x66, 0x42, 0x40, 0x7A, 0x84, 0xE3, 0xCA, 0x66, 0x24, 0x8C, + 0x66, 0x14, 0x66, 0x8A, 0x5F, 0x7F, 0xDC, 0xCA, 0x8F, 0xD0, 0x2B, 0x75, 0xFC, 0x72, 0x3C, 0x42, + 0x14, 0xB6, 0xA0, 0x96, 0x83, 0xF1, 0x47, 0xED, 0x3A, 0xDC, 0xB2, 0x6B, 0x6D, 0xE0, 0x85, 0x0C, + 0xCA, 0x0C, 0xFA, 0x3C, 0x22, 0x0C, 0xCD, 0x96, 0x60, 0xCD, 0x06, 0xEA, 0xE2, 0x3F, 0x81, 0x52, + 0x1A, 0x38, 0x22, 0x6C, 0x95, 0x6B, 0xB7, 0x85, 0xB5, 0xFE, 0x1A, 0xF8, 0xEC, 0x79, 0x18, 0xA2, + 0x7C, 0xF3, 0xBD, 0x71, 0x73, 0x77, 0x6F, 0xFC, 0xE9, 0xD3, 0x7C, 0xDB, 0xBE, 0x39, 0x0D, 0x63, + 0x2C, 0xA8, 0x0B, 0x5A, 0xC4, 0x4B, 0x8F, 0xA3, 0xEA, 0x63, 0x83, 0xD6, 0x01, 0xF7, 0xA9, 0xF4, + 0x92, 0xCC, 0xD7, 0x8E, 0x90, 0xBE, 0xED, 0x5B, 0xD9, 0xE1, 0xA1, 0xA5, 0xF7, 0x24, 0x32, 0x2E, + 0xAB, 0x57, 0x42, 0x47, 0x14, 0xDF, 0x09, 0x3D, 0xB2, 0x4A, 0x7E, 0xFE, 0xB8, 0x7F, 0x62, 0xC1, + 0x91, 0xDC, 0xE3, 0xDD, 0x64, 0xBC, 0xA1, 0xAF, 0x3C, 0x60, 0xBF, 0x8F, 0x61, 0xBF, 0x38, 0x5B, + 0xC0, 0xD7, 0x37, 0x21, 0x6B, 0x7C, 0xF5, 0xE1, 0x49, 0x79, 0x97, 0xE2, 0xD5, 0x87, 0xB7, 0x5F, + 0xEB, 0x47, 0xBC, 0xD6, 0xA1, 0x38, 0xC2, 0x40, 0xA9, 0x8A, 0xF3, 0x0D, 0x70, 0xEF, 0x8F, 0x62, + 0x81, 0xF8, 0x33, 0x06, 0xAA, 0x15, 0xC7, 0x26, 0x47, 0x01, 0x1D, 0x64, 0x88, 0x02, 0x53, 0x50, + 0x75, 0x1A, 0x05, 0xFC, 0xFD, 0x69, 0xFD, 0x48, 0x34, 0x05, 0x65, 0x6D, 0x4D, 0x65, 0xDC, 0x7D, + 0x36, 0x5D, 0xFA, 0x95, 0x40, 0x47, 0x3F, 0x42, 0xB2, 0xD4, 0x33, 0xCD, 0xCF, 0x10, 0xD3, 0x0F, + 0x1C, 0xD2, 0xEC, 0x9E, 0xFC, 0xDC, 0x15, 0x10, 0xC1, 0x19, 0x6D, 0xC5, 0xC4, 0x2C, 0xDC, 0xC8, + 0x1D, 0x9B, 0x4A, 0x78, 0xCC, 0x9C, 0xD4, 0xE1, 0x90, 0x38, 0xBC, 0x8C, 0x03, 0x9F, 0xA0, 0x73, + 0xE0, 0xCD, 0x57, 0x1F, 0xFE, 0xC9, 0x92, 0x14, 0x96, 0xF9, 0xFE, 0xBD, 0xD4, 0xFA, 0x6B, 0x9C, + 0x72, 0x59, 0x69, 0x95, 0x1A, 0x3F, 0xB0, 0x89, 0x3C, 0xE4, 0xDD, 0x6E, 0x3D, 0x13, 0xD6, 0x83, + 0x27, 0x53, 0xDB, 0x3D, 0xBF, 0x94, 0x09, 0xAF, 0x74, 0x16, 0xEF, 0xF5, 0x54, 0x9A, 0x9F, 0x57, + 0x78, 0xF3, 0x0C, 0x08, 0x34, 0x07, 0xC7, 0xA4, 0x9A, 0xBD, 0xC7, 0x54, 0x51, 0xB3, 0x4B, 0x74, + 0x93, 0x39, 0x61, 0x3A, 0xFD, 0x0D, 0x75, 0xFE, 0x49, 0x65, 0x51, 0xD0, 0x95, 0xF2, 0x09, 0x9B, + 0xA9, 0x97, 0x13, 0x82, 0xF2, 0x40, 0x4D, 0xD3, 0x21, 0x5F, 0x4B, 0xD1, 0xFB, 0xFB, 0x02, 0x46, + 0xCE, 0x05, 0x00, 0x51, 0x3A, 0xB1, 0xDB, 0x86, 0x90, 0x0F, 0xD3, 0xB4, 0xED, 0xD3, 0xBB, 0x2D, + 0x9E, 0x55, 0x26, 0x56, 0x87, 0xC2, 0xDA, 0x67, 0xA0, 0x42, 0x8B, 0x24, 0x8E, 0xE2, 0x75, 0x9A, + 0x2D, 0x5F, 0x61, 0x2A, 0x60, 0x71, 0xE8, 0x2C, 0x70, 0x0F, 0x4A, 0x95, 0xA9, 0x1A, 0x26, 0x3C, + 0x68, 0xC1, 0xBA, 0xF0, 0x7B, 0x12, 0xA3, 0x5D, 0x57, 0x57, 0x68, 0x43, 0xC5, 0x68, 0x0F, 0xEE, + 0x62, 0x6E, 0xF1, 0xAB, 0x2A, 0x51, 0x44, 0x77, 0x4B, 0xFD, 0xB7, 0xD1, 0xAC, 0x4E, 0xC0, 0x54, + 0x8C, 0x85, 0x50, 0xBA, 0xEC, 0xCB, 0xE2, 0x76, 0xF8, 0x9E, 0xDB, 0x80, 0x36, 0x04, 0xF9, 0xE4, + 0xCE, 0xED, 0x76, 0x2B, 0x9B, 0x63, 0x07, 0xB2, 0xB9, 0x0A, 0x1F, 0x6E, 0xEE, 0x15, 0x17, 0xEA, + 0x79, 0xD3, 0x68, 0xBF, 0x1C, 0x9E, 0xA1, 0x03, 0xC9, 0x05, 0x5E, 0xA7, 0x1B, 0x7C, 0x0D, 0x97, + 0x36, 0x72, 0x26, 0x83, 0xFB, 0x11, 0xE3, 0x9A, 0x0B, 0xE0, 0x72, 0x83, 0xB6, 0x36, 0x90, 0x88, + 0x7C, 0xA4, 0x5E, 0xE2, 0xAF, 0x83, 0x28, 0xA6, 0x9F, 0xB5, 0xC1, 0x43, 0xE6, 0xCB, 0x0C, 0x6B, + 0x40, 0x1B, 0x55, 0x3D, 0xAA, 0x00, 0xD4, 0x9A, 0x93, 0x20, 0x82, 0xE2, 0xEE, 0x5C, 0x9C, 0x7E, + 0x7B, 0x49, 0xE2, 0x5D, 0x4F, 0xD6, 0xB3, 0x19, 0x83, 0x1A, 0xA3, 0x20, 0x0F, 0x14, 0x0E, 0xD8, + 0x35, 0x2A, 0x67, 0x44, 0x65, 0x3E, 0xD3, 0x0F, 0x67, 0x54, 0xDB, 0x77, 0xA8, 0x6C, 0xDE, 0x56, + 0x80, 0x08, 0x2E, 0x55, 0xA0, 0xEC, 0x3B, 0x88, 0xAE, 0x88, 0x90, 0xFE, 0xAF, 0x80, 0xAD, 0xE2, + 0xF3, 0xF7, 0xEF, 0x29, 0xE3, 0xE7, 0xC1, 0x92, 0xC5, 0x6B, 0x5E, 0x2F, 0x89, 0x47, 0x77, 0x98, + 0xA3, 0x55, 0xD7, 0x12, 0x39, 0xC0, 0x51, 0x8C, 0xB1, 0x3C, 0xAB, 0x4C, 0x59, 0x42, 0xDA, 0xE7, + 0xCD, 0xAB, 0x08, 0x82, 0x3B, 0x7D, 0x02, 0xA9, 0x1F, 0x98, 0xAE, 0x07, 0x15, 0x2F, 0x2C, 0x19, + 0x4D, 0x19, 0x94, 0x80, 0xCF, 0x91, 0x4F, 0x3F, 0x0B, 0x3E, 0x69, 0x45, 0x1A, 0x88, 0xA3, 0xB2, + 0x23, 0xF1, 0x01, 0x78, 0x7C, 0x1B, 0x7D, 0x98, 0xCC, 0x1A, 0x41, 0xD3, 0xB3, 0x1D, 0xD9, 0xD3, + 0x17, 0x14, 0xDA, 0x3F, 0x9A, 0x9F, 0xA1, 0x2D, 0x3B, 0x9E, 0xE7, 0x1F, 0xAD, 0xCF, 0x55, 0xBA, + 0x4F, 0x5F, 0x40, 0x71, 0xA9, 0xBA, 0xD1, 0xEB, 0xC8, 0x3F, 0xE3, 0x2D, 0xCD, 0x55, 0x7D, 0x4F, + 0xC4, 0xDC, 0xA7, 0x4F, 0xF3, 0x80, 0x7D, 0xCA, 0x49, 0xCA, 0xD8, 0x32, 0x25, 0xD7, 0xF1, 0x9A, + 0xE0, 0xFB, 0x44, 0x2A, 0x11, 0x21, 0x33, 0xA8, 0x37, 0x89, 0x17, 0xC5, 0x60, 0x4F, 0x90, 0x31, + 0x2B, 0x3D, 0xD1, 0x71, 0x58, 0x22, 0xC6, 0x45, 0xF1, 0x86, 0x94, 0xF3, 0x16, 0x0A, 0xB6, 0x56, + 0xCA, 0x96, 0xCA, 0xEF, 0xF9, 0x20, 0x5B, 0x76, 0x4A, 0xE4, 0xAD, 0x17, 0x98, 0xC8, 0x33, 0xAA, + 0xDD, 0x5D, 0x06, 0x6D, 0x1E, 0x5F, 0x04, 0xE1, 0x6B, 0x48, 0x8F, 0xCB, 0x37, 0x4B, 0x8B, 0x3E, + 0x62, 0x3A, 0xBE, 0x65, 0x74, 0xAC, 0x7E, 0xBA, 0x6B, 0x66, 0x29, 0x34, 0x1D, 0xCB, 0xD9, 0x1E, + 0x9F, 0xEE, 0xC9, 0xB7, 0xA6, 0x8E, 0x96, 0x78, 0x59, 0xC9, 0x56, 0x2D, 0xF3, 0xF4, 0x3B, 0x2B, + 0x38, 0x0A, 0xBE, 0xE6, 0x61, 0x95, 0x9B, 0x2C, 0xDC, 0x8A, 0x79, 0x0F, 0x28, 0xD8, 0x64, 0x30, + 0x05, 0x52, 0xFE, 0x0C, 0x35, 0x9B, 0xE0, 0xE8, 0x9D, 0x55, 0xDB, 0x8E, 0x84, 0x1F, 0x5F, 0xAF, + 0x65, 0x89, 0x5C, 0x5E, 0xB2, 0xDD, 0x6D, 0x0B, 0xF7, 0xAD, 0xD4, 0x76, 0xB0, 0x3C, 0xF2, 0x66, + 0xDC, 0x11, 0x30, 0xD9, 0x3B, 0x72, 0x8F, 0x33, 0x86, 0xC7, 0xDB, 0x6F, 0x65, 0x66, 0xF6, 0x5E, + 0xE8, 0xEA, 0xEA, 0x9E, 0x16, 0x5F, 0xD4, 0x97, 0xFF, 0x22, 0xFF, 0xB0, 0x3B, 0xBD, 0x84, 0xE3, + 0x50, 0x3A, 0xC4, 0xC3, 0x85, 0x2C, 0x3A, 0x40, 0x6A, 0x89, 0x53, 0x0E, 0x29, 0xF6, 0xA7, 0x4F, + 0xA9, 0x5B, 0x7D, 0x2C, 0xF7, 0x7E, 0xFF, 0xAE, 0xCA, 0x5E, 0xE5, 0x74, 0x67, 0xA2, 0x5C, 0xC3, + 0xB4, 0xCB, 0x86, 0x48, 0x91, 0x8D, 0xD2, 0xAA, 0x83, 0xA6, 0x18, 0xC5, 0x42, 0x51, 0x19, 0x0F, + 0x45, 0x36, 0x85, 0x6B, 0x3A, 0xE5, 0xF1, 0xD2, 0x4A, 0x84, 0x3F, 0xB8, 0xD3, 0x28, 0xF0, 0x68, + 0xD5, 0x35, 0x75, 0x3E, 0x82, 0xD8, 0x2C, 0xFE, 0x1E, 0x01, 0x38, 0x5C, 0x2F, 0x29, 0x72, 0xD4, + 0x18, 0x59, 0x77, 0x3B, 0x2B, 0x29, 0x96, 0xE8, 0x3E, 0xDA, 0x56, 0x28, 0xAE, 0x6B, 0x19, 0x91, + 0xEE, 0x9A, 0x27, 0x98, 0x48, 0x4E, 0x41, 0x6F, 0x93, 0x7C, 0x7D, 0x8E, 0x39, 0x83, 0x4A, 0x85, + 0x12, 0x86, 0xF6, 0x80, 0x7B, 0xA5, 0xBA, 0x85, 0x79, 0x82, 0x20, 0x79, 0x1F, 0xDB, 0xB0, 0x9C, + 0x2D, 0x77, 0xDE, 0xB9, 0x95, 0x50, 0x44, 0x47, 0xCC, 0x40, 0x0F, 0xDB, 0x67, 0xF1, 0x5A, 0xE8, + 0x81, 0x82, 0xB3, 0xB4, 0x85, 0x88, 0xAF, 0x15, 0x3E, 0x0A, 0x92, 0x88, 0x0A, 0xC3, 0x3B, 0x02, + 0x6E, 0x58, 0x38, 0x0A, 0xB5, 0x83, 0x0C, 0x82, 0x3B, 0x3C, 0x7A, 0xB5, 0x33, 0x1A, 0x5F, 0xF3, + 0x13, 0x48, 0x3C, 0x7B, 0x7F, 0xF6, 0xF2, 0x5D, 0xB9, 0xD8, 0x45, 0x27, 0x0B, 0x28, 0x44, 0x1C, + 0x9C, 0x6F, 0x83, 0x3E, 0xFD, 0xFD, 0xF9, 0xD9, 0xD9, 0x87, 0xB7, 0xEF, 0x5E, 0xEC, 0x1F, 0xC2, + 0x71, 0xC8, 0xD9, 0xFB, 0x9F, 0xDF, 0x9C, 0x9E, 0x8F, 0xAE, 0x71, 0x5B, 0x2E, 0xD8, 0x17, 0x11, + 0x82, 0xBB, 0xB7, 0x86, 0x83, 0x9D, 0xAD, 0xE1, 0x27, 0xD0, 0xA6, 0xEA, 0x02, 0x51, 0x25, 0x04, + 0xD5, 0x2A, 0x21, 0x2F, 0x03, 0x2A, 0xF9, 0x56, 0x90, 0x97, 0x0A, 0x83, 0xAC, 0xE2, 0x07, 0x51, + 0x07, 0xE5, 0x32, 0x21, 0x12, 0x61, 0x28, 0xC8, 0xCA, 0x84, 0x4D, 0x10, 0xF9, 0xF1, 0x66, 0x8F, + 0xE3, 0xCE, 0xE6, 0xDF, 0x0E, 0x4F, 0x5A, 0xEA, 0xDD, 0xCF, 0x93, 0x96, 0x7A, 0xE5, 0x5C, 0xFC, + 0xCF, 0x02, 0xFF, 0x0F, 0x90, 0xD3, 0xC8, 0x0C, 0x33, 0x50, 0x00, 0x00 +}; +#endif //__embedded_h \ No newline at end of file diff --git a/esp3d/src/modules/http/handles/handle-command.cpp b/esp3d/src/modules/http/handles/handle-command.cpp new file mode 100644 index 00000000..74116847 --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-command.cpp @@ -0,0 +1,48 @@ +/* + handle-command.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../authentication/authentication_service.h" +#include "../../../core/commands.h" +#include "../../../core/esp3doutput.h" + +//Handle web command query and send answer////////////////////////////// +void HTTP_Server::handle_web_command () +{ + level_authenticate_type auth_level = AuthenticationService::authenticated_level(); + //log_esp3d("Authentication = %d", auth_level); + String cmd = ""; + if (_webserver->hasArg ("cmd")) { + cmd = _webserver->arg ("cmd"); + ESP3DOutput output(_webserver); + esp3d_commands.process((uint8_t*)cmd.c_str(), cmd.length(), &output, auth_level); + } else { + _webserver->send (400, "text/plain", "Invalid command"); + } + return; +} +#endif //HTTP_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-description_xml.cpp b/esp3d/src/modules/http/handles/handle-description_xml.cpp new file mode 100644 index 00000000..1d8d23fa --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-description_xml.cpp @@ -0,0 +1,39 @@ +/* + handle-SSDP.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) && defined (SSDP_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#ifdef SSDP_FEATURE +#include +#endif //SSDP_FEATURE +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#ifdef SSDP_FEATURE +#include +#endif //SSDP_FEATURE +#endif //ARDUINO_ARCH_ESP8266 +void HTTP_Server::handle_SSDP() +{ + SSDP.schema(_webserver->client()); +} +#endif //HTTP_FEATURE && SSDP_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-filenotfound.cpp b/esp3d/src/modules/http/handles/handle-filenotfound.cpp new file mode 100644 index 00000000..6a475aba --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-filenotfound.cpp @@ -0,0 +1,100 @@ +/* + handle-filenotfound.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../filesystem/esp_filesystem.h" +#include "../../authentication/authentication_service.h" + +//Handle not registred path on FS neither SD /////////////////////// +void HTTP_Server:: handle_not_found() +{ + if (AuthenticationService::authenticated_level() == LEVEL_GUEST) { + _webserver->sendContent("HTTP/1.1 301 OK\r\nLocation: /\r\nCache-Control: no-cache\r\n\r\n"); + return; + } + String path = _webserver->urlDecode(_webserver->uri()); + String contentType = getContentType(path.c_str()); + String pathWithGz = path + ".gz"; +#if defined (FILESYSTEM_FEATURE) + if(ESP_FileSystem::exists(pathWithGz.c_str()) || ESP_FileSystem::exists(path.c_str())) { + if(ESP_FileSystem::exists(pathWithGz.c_str())) { + path = pathWithGz; + } + if(!StreamFSFile(path.c_str(),contentType.c_str())) { + log_esp3d("Stream `%s` failed", path.c_str()); + } + return; + } +#endif //#if defined (FILESYSTEM_FEATURE) + +#ifdef FILESYSTEM_FEATURE + //check local page + path = "/404.htm"; + contentType = getContentType(path.c_str()); + pathWithGz = path + ".gz"; + if(ESP_FileSystem::exists(pathWithGz.c_str()) || ESP_FileSystem::exists(path.c_str())) { + if(ESP_FileSystem::exists(pathWithGz.c_str())) { + path = pathWithGz; + } + if(!StreamFSFile(path.c_str(),contentType.c_str())) { + log_esp3d("Stream `%s` failed", path.c_str()); + } + return; + } +#endif //FILESYSTEM_FEATURE + //let's keep simple just send minimum + _webserver->send(404); + + /* + + #ifdef ENABLE_SD_CARD + if ((path.substring(0,4) == "/SD/")) { + //remove /SD + path = path.substring(3); + if(SD.exists((char *)pathWithGz.c_str()) || SD.exists((char *)path.c_str())) { + if(SD.exists((char *)pathWithGz.c_str())) { + path = pathWithGz; + } + File datafile = SD.open((char *)path.c_str()); + if (datafile) { + if( _webserver->streamFile(datafile, contentType) == datafile.size()) { + datafile.close(); + COMMANDS::wait(0); + return; + } else{ + datafile.close(); + } + } + } + String content = "cannot find "; + content+=path; + _webserver->send(404,"text/plain",content.c_str()); + return; + } else + #endif*/ +} +#endif //HTTP_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-files.cpp b/esp3d/src/modules/http/handles/handle-files.cpp new file mode 100644 index 00000000..fa0cb7a9 --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-files.cpp @@ -0,0 +1,204 @@ +/* + handle-files.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) && defined(FILESYSTEM_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../filesystem/esp_filesystem.h" +#include "../../authentication/authentication_service.h" +//Filesystem +//Filesystem files list and file commands +void HTTP_Server::handleFSFileList () +{ + uint32_t start = millis(); + level_authenticate_type auth_level = AuthenticationService::authenticated_level(); + if (auth_level == LEVEL_GUEST) { + _upload_status = UPLOAD_STATUS_NONE; + _webserver->send (401, "text/plain", "Wrong authentication!"); + return; + } + String path ; + String status = "ok"; + if ( (_upload_status == UPLOAD_STATUS_FAILED) || (_upload_status == UPLOAD_STATUS_CANCELLED) ) { + status = "Upload failed"; + } + if (_webserver->hasArg ("quiet")) { + if(_webserver->arg ("quiet") == "yes") { + Serial.println("quiet"); + _webserver->send (200, "text/plain", status.c_str()); + return; + } + } + //get current path + if (_webserver->hasArg ("path") ) { + path += _webserver->arg ("path") ; + } + //to have a clean path + path.trim(); + path.replace ("//", "/"); + if (path[path.length() - 1] != '/') { + path += "/"; + } + //check if query need some action + if (_webserver->hasArg ("action") ) { + //delete a file + if (_webserver->arg ("action") == "delete" && _webserver->hasArg ("filename") ) { + String filename; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename = path + _webserver->arg ("filename"); + filename.replace ("//", "/"); + if (!ESP_FileSystem::exists (filename.c_str()) ) { + status = shortname + " does not exists!"; + } else { + if (ESP_FileSystem::remove (filename.c_str()) ) { + status = shortname + " deleted"; + //what happen if no "/." and no other subfiles for SPIFFS like? + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + if (!ESP_FileSystem::exists (ptmp.c_str())) { + ESP_FileSystem::mkdir(ptmp.c_str()); + } + } else { + status = "Cannot deleted " ; + status += shortname ; + } + } + } + //delete a directory + if (_webserver->arg ("action") == "deletedir" && _webserver->hasArg ("filename") ) { + String filename; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename = path + _webserver->arg ("filename"); + filename += "/"; + filename.replace ("//", "/"); + if (filename != "/") { + if (ESP_FileSystem::rmdir(filename.c_str())) { + log_esp3d("Deleting %s",filename.c_str()); + status = shortname ; + status += " deleted"; + } else { + status = "Cannot deleted " ; + status += shortname ; + } + } + } + //create a directory + if (_webserver->arg ("action") == "createdir" && _webserver->hasArg ("filename") ) { + String filename; + filename = path + _webserver->arg ("filename") + "/."; + String shortname = _webserver->arg ("filename"); + shortname.replace ("/", ""); + filename.replace ("//", "/"); + if (ESP_FileSystem::exists (filename.c_str()) ) { + status = shortname + " already exists!"; + } else { + if (!ESP_FileSystem::mkdir(filename.c_str())) { + status = "Cannot create "; + status += shortname ; + } else { + status = shortname + " created"; + } + } + } + } + String buffer2send ; + buffer2send.reserve(1200); + buffer2send = "{\"files\":["; + String ptmp = path; + if ( (path != "/") && (path[path.length() - 1] = '/') ) { + ptmp = path.substring (0, path.length() - 1); + } + _webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + _webserver->sendHeader("Content-Type","application/json"); + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(200); + if (ESP_FileSystem::exists(ptmp.c_str())) { + ESP_File f = ESP_FileSystem::open(ptmp.c_str(), ESP_FILE_READ); + //Parse files + ESP_File sub = f.openNextFile(); + if (f) { + bool needseparator = false; + while (sub) { + if (needseparator) { + buffer2send+=","; + } else { + //for next entry + needseparator=true; + } + buffer2send+="{\"name\":\""; + buffer2send+=sub.name(); + buffer2send+="\",\"size\":\""; + if (sub.isDirectory()) { + buffer2send+="-1"; + } else { + buffer2send+=ESP_FileSystem::formatBytes(sub.size()); + } +#ifdef FILESYSTEM_TIMESTAMP_FEATURE + buffer2send+="\",\"time\":\""; + time_t t = sub.getLastWrite(); + struct tm * tmstruct = localtime(&t); + char str[20]; //buffer should be 20 + sprintf(str,"%d-%02d-%02d %02d:%02d:%02d",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); + buffer2send+=str; +#endif //FILESYSTEM_TIMESTAMP_FEATURE + buffer2send+="\"}"; + if (buffer2send.length() > 1100) { + _webserver->sendContent_P(buffer2send.c_str(),buffer2send.length()); + buffer2send = ""; + } + sub.close(); + sub = f.openNextFile(); + } + f.close(); + } else { + if (status == "ok") { + status = "cannot open" + ptmp; + } else { + status += ", cannot open" + ptmp; + } + } + } else { + if (status == "ok") { + status = ptmp + " does not exists!"; + } else { + status += ", " + ptmp + " does not exists!"; + } + } + buffer2send += "],\"path\":\"" + path + "\","; + buffer2send += "\"status\":\"" + status + "\","; + buffer2send += "\"occupation\":\"" + String(100*ESP_FileSystem::usedBytes()/ESP_FileSystem::totalBytes()) + "\","; + buffer2send += "\"total\":\"" + ESP_FileSystem::formatBytes (ESP_FileSystem::totalBytes()) + "\","; + buffer2send += "\"used\":\"" + ESP_FileSystem::formatBytes (ESP_FileSystem::usedBytes()) + "\"}"; + path = ""; + _webserver->sendContent_P(buffer2send.c_str(),buffer2send.length()); + _webserver->sendContent(""); + _upload_status = UPLOAD_STATUS_NONE; +} + +#endif //HTTP_FEATURE && FILESYSTEM_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-login.cpp b/esp3d/src/modules/http/handles/handle-login.cpp new file mode 100644 index 00000000..dcf59050 --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-login.cpp @@ -0,0 +1,119 @@ +/* + handle-login.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../authentication/authentication_service.h" +#include "../../../core/esp3doutput.h" +#include "../../../core/settings_esp3d.h" + +//login status check +void HTTP_Server::handle_login() +{ +#ifdef AUTHENTICATION_FEATURE + int code = 401; + String status = "Wrong authentication!"; + //Disconnect can be done anytime no need to check credential + if (_webserver->hasArg("DISCONNECT")) { + AuthenticationService::ClearCurrentSession(); + _webserver->sendHeader("Set-Cookie","ESPSESSIONID=0"); + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(200, "application/json", "{\"status\":\"ok\",\"authentication_lvl\":\"guest\"}"); + return; + } + level_authenticate_type auth_level = AuthenticationService::authenticated_level(); + //check is it is a submission or a query + if (_webserver->hasArg("SUBMIT")) { + //is there a correct list of query? + if (_webserver->hasArg("PASSWORD") && _webserver->hasArg("USER")) { + //User + String sUser = _webserver->arg("USER"); + //Password + String sPassword = _webserver->arg("PASSWORD"); + if((((sUser == DEFAULT_ADMIN_LOGIN) && (AuthenticationService::isadmin(sPassword.c_str()))) || + ((sUser == DEFAULT_USER_LOGIN) && (AuthenticationService::isuser(sPassword.c_str()))))) { + //check if it is to change password or login + if (_webserver->hasArg("NEWPASSWORD")) { + String newpassword = _webserver->arg("NEWPASSWORD"); + //check new password + if (Settings_ESP3D::isLocalPasswordValid(newpassword.c_str())) { + if (!Settings_ESP3D::write_string (ESP_ADMIN_PWD, newpassword.c_str())) { + code = 500; + status = "Set failed!"; + } else { + code = 200; + status = "ok"; + } + } else { + code = 500; + status = "Incorrect password!"; + } + } else { //do authentication + //allow to change session timeout when login + if (_webserver->hasArg("TIMEOUT")) { + String timeout = _webserver->arg("TIMEOUT"); + AuthenticationService::setSessionTimeout(timeout.toInt()); + } + //it is a change or same level + if (((auth_level == LEVEL_USER) && (sUser == DEFAULT_USER_LOGIN)) || + ((auth_level == LEVEL_ADMIN)&& (sUser == DEFAULT_ADMIN_LOGIN))) { + code = 200; + status = "ok"; + } else { //new authentication + String session = AuthenticationService::create_session_ID(); + if (AuthenticationService::CreateSession((sUser == DEFAULT_ADMIN_LOGIN)?LEVEL_ADMIN:LEVEL_USER,sUser.c_str(), session.c_str())) { + AuthenticationService::ClearCurrentSession(); + code = 200; + status = "ok"; + String tmps ="ESPSESSIONID="; + tmps+=session; + _webserver->sendHeader("Set-Cookie",tmps); + } + } + } + } + } + }//SUBMIT + _webserver->sendHeader("Cache-Control","no-cache"); + String smsg = "{\"status\":\""; + smsg+=status; + smsg+="\",\"authentication_lvl\":\""; + if (auth_level == LEVEL_USER) { + smsg += "user"; + } else if (auth_level == LEVEL_ADMIN) { + smsg += "admin"; + } else { + smsg += "guest"; + } + smsg += "\"}"; + _webserver->send(code, "application/json", smsg); +#else // No AUTHENTICATION_FEATURE + _webserver->sendHeader("Cache-Control","no-cache"); + _webserver->send(200, "application/json", "{\"status\":\"ok\",\"authentication_lvl\":\"admin\"}"); +#endif //AUTHENTICATION_FEATURE +} + +#endif //HTTP_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-root.cpp b/esp3d/src/modules/http/handles/handle-root.cpp new file mode 100644 index 00000000..4e6d7087 --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-root.cpp @@ -0,0 +1,53 @@ +/* + handle-root.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) +#include "../http_server.h" +//embedded response file if no files on ESP Filesystem +#include "../embedded.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../filesystem/esp_filesystem.h" +//Root of Webserver///////////////////////////////////////////////////// +void HTTP_Server::handle_root() +{ + String path = "/index.html"; + String contentType = getContentType(path.c_str()); + String pathWithGz = path + ".gz"; + //if have a index.html or gzip version this is default root page + if((ESP_FileSystem::exists(pathWithGz.c_str()) || ESP_FileSystem::exists(path.c_str())) && !_webserver->hasArg("forcefallback") && _webserver->arg("forcefallback")!="yes") { + if(ESP_FileSystem::exists(pathWithGz.c_str())) { + _webserver->sendHeader("Content-Encoding", "gzip"); + path = pathWithGz; + } + if(!StreamFSFile(path.c_str(),contentType.c_str())) { + log_esp3d("Stream `%s` failed", path.c_str()); + } + return; + } + //if no lets launch the default content + _webserver->sendHeader("Content-Encoding", "gzip"); + _webserver->send_P(200,"text/html",(const char *)tool_html_gz,tool_html_gz_size); +} +#endif //HTTP_FEATURE diff --git a/esp3d/src/modules/http/handles/handle-updatefw.cpp b/esp3d/src/modules/http/handles/handle-updatefw.cpp new file mode 100644 index 00000000..eb0319d7 --- /dev/null +++ b/esp3d/src/modules/http/handles/handle-updatefw.cpp @@ -0,0 +1,54 @@ +/* + handle-updatefw.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) && defined(WEB_UPDATE_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../../core/esp3d.h" +#include "../../authentication/authentication_service.h" +//Web Update handler +void HTTP_Server::handleUpdate () +{ + level_authenticate_type auth_level = AuthenticationService::authenticated_level(); + if (auth_level != LEVEL_ADMIN) { + _upload_status = UPLOAD_STATUS_NONE; + _webserver->send (401, "text/plain", "Wrong authentication!"); + return; + } + String jsonfile = "{\"status\":\"" ; + jsonfile += String(_upload_status); + jsonfile += "\"}"; + _webserver->sendHeader("Cache-Control", "no-cache"); + _webserver->send(200, "application/json", jsonfile); + //if success restart + if (_upload_status == UPLOAD_STATUS_SUCCESSFUL) { + Hal::wait(1000); + Esp3D::restart_esp(); + } else { + _upload_status = UPLOAD_STATUS_NONE; + } +} + +#endif //HTTP_FEATURE && WEB_UPDATE_FEATURE diff --git a/esp3d/src/modules/http/handles/upload-files.cpp b/esp3d/src/modules/http/handles/upload-files.cpp new file mode 100644 index 00000000..a23b8dd6 --- /dev/null +++ b/esp3d/src/modules/http/handles/upload-files.cpp @@ -0,0 +1,133 @@ +/* + upload-files.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) && defined(FILESYSTEM_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "../../filesystem/esp_filesystem.h" +#include "../../authentication/authentication_service.h" +//FS files uploader handle +void HTTP_Server::FSFileupload () +{ + //get authentication status + level_authenticate_type auth_level= AuthenticationService::authenticated_level(); + //Guest cannot upload - only admin + if (auth_level == LEVEL_GUEST) { + _webserver->send (401, "text/plain", "Wrong authentication!"); + return; + } + static String filename; + static ESP_File fsUploadFile; + + HTTPUpload& upload = _webserver->upload(); + //Upload start + if(upload.status == UPLOAD_FILE_START) { + String upload_filename = upload.filename; + if (upload_filename[0] != '/') { + filename = "/" + upload_filename; + } else { + filename = upload.filename; + } + + if (ESP_FileSystem::exists (filename.c_str()) ) { + ESP_FileSystem::remove (filename.c_str()); + } + if (fsUploadFile.isOpen() ) { + fsUploadFile.close(); + } + //create file + fsUploadFile = ESP_FileSystem::open(filename.c_str(), ESP_FILE_WRITE); + //check If creation succeed + if (fsUploadFile) { + //if yes upload is started + _upload_status= UPLOAD_STATUS_ONGOING; + } else { + //if no set cancel flag + _upload_status=UPLOAD_STATUS_CANCELLED; + _webserver->send (500, "text/plain", "Cannot create file!"); + _webserver->client().stop(); + } + //Upload write + } else if(upload.status == UPLOAD_FILE_WRITE) { +//check if file is available and no error + if(fsUploadFile && _upload_status == UPLOAD_STATUS_ONGOING) { + //no error so write post date + fsUploadFile.write(upload.buf, upload.currentSize); + } else { + //we have a problem set flag UPLOAD_STATUS_CANCELLED + _upload_status=UPLOAD_STATUS_CANCELLED; + fsUploadFile.close(); + if (ESP_FileSystem::exists (filename.c_str())) { + ESP_FileSystem::remove (filename.c_str()); + } + _webserver->send (500, "text/plain", "Cannot write file!"); + _webserver->client().stop(); + } +//Upload end + } else if(upload.status == UPLOAD_FILE_END) { + //check if file is still open + if(fsUploadFile) { + //close it + fsUploadFile.close(); + //check size + String sizeargname = upload.filename + "S"; + //fsUploadFile = ESP_FileSystem::open (filename, ESP_FILE_READ); + uint32_t filesize = fsUploadFile.size(); + //fsUploadFile.close(); + if (_webserver->hasArg (sizeargname.c_str()) ) { + if (_webserver->arg (sizeargname.c_str()) != String(filesize)) { + _upload_status = UPLOAD_STATUS_FAILED; + ESP_FileSystem::remove (filename.c_str()); + } + } + if (_upload_status == UPLOAD_STATUS_ONGOING) { + _upload_status = UPLOAD_STATUS_SUCCESSFUL; + } else { + _webserver->send (500, "text/plain", "Upload error!"); + } + } else { + //we have a problem set flag UPLOAD_STATUS_CANCELLED + _upload_status=UPLOAD_STATUS_CANCELLED; + _webserver->client().stop(); + if (ESP_FileSystem::exists (filename.c_str()) ) { + ESP_FileSystem::remove (filename.c_str()); + } + _webserver->send (500, "text/plain", "Upload error!"); + } + //Upload cancelled + } else { + if (_upload_status == UPLOAD_STATUS_ONGOING) { + _upload_status = UPLOAD_STATUS_CANCELLED; + } + if(fsUploadFile) { + fsUploadFile.close(); + } + if (ESP_FileSystem::exists (filename.c_str()) ) { + ESP_FileSystem::remove (filename.c_str()); + } + _webserver->send (500, "text/plain", "Upload error!"); + } +} +#endif //HTTP_FEATURE && FILESYSTEM_FEATURE diff --git a/esp3d/src/modules/http/handles/upload-updatefw.cpp b/esp3d/src/modules/http/handles/upload-updatefw.cpp new file mode 100644 index 00000000..471fe260 --- /dev/null +++ b/esp3d/src/modules/http/handles/upload-updatefw.cpp @@ -0,0 +1,126 @@ +/* + upload-updatefw.cpp - ESP3D http handle + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "../../../include/esp3d_config.h" +#if defined (HTTP_FEATURE) && defined(WEB_UPDATE_FEATURE) +#include "../http_server.h" +#if defined (ARDUINO_ARCH_ESP32) +#include +#define UPDATE_SIZE +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#define UPDATE_SIZE ESP_FileSystem::max_update_size() +#endif //ARDUINO_ARCH_ESP8266 +#include "../../filesystem/esp_filesystem.h" +#include "../../authentication/authentication_service.h" +#include "../../../core/esp3doutput.h" +//File upload for Web update +void HTTP_Server::WebUpdateUpload () +{ + static size_t last_upload_update; + static uint32_t downloadsize = 0; + ESP3DOutput output(ESP_ALL_CLIENTS); + //only admin can update FW + if (AuthenticationService::authenticated_level() != LEVEL_ADMIN) { + _upload_status = UPLOAD_STATUS_CANCELLED; + _webserver->send (401, "text/plain", "Wrong authentication!"); + return; + } + //get current file ID + HTTPUpload& upload = _webserver->upload(); + //Upload start + if(upload.status == UPLOAD_FILE_START) { + output.printMSG("Update Firmware"); + _upload_status= UPLOAD_STATUS_ONGOING; + String sizeargname = upload.filename + "S"; + if (_webserver->hasArg (sizeargname.c_str()) ) { + downloadsize = _webserver->arg (sizeargname).toInt(); + } else { + downloadsize = 0; + } + if (downloadsize > ESP_FileSystem::max_update_size()) { + _upload_status=UPLOAD_STATUS_CANCELLED; + output.printERROR("Update oversized!"); + _webserver->send (500, "text/plain", "Update oversized!"); + return; + } + last_upload_update = 0; + if(!Update.begin(UPDATE_SIZE)) { //start with unknown = max available size + _upload_status=UPLOAD_STATUS_CANCELLED; + output.printERROR("Update canceled!"); + _webserver->send (500, "text/plain", "Update canceled!"); + return; + } else { + output.printMSG("Update 0%"); + } + //Upload write + + } else if(upload.status == UPLOAD_FILE_WRITE) { +//check if no error + if (_upload_status == UPLOAD_STATUS_ONGOING) { + //we do not know the total file size yet but we know the available space so let's use it + if (downloadsize != 0) { + if ( ((100 * upload.totalSize) / downloadsize) !=last_upload_update) { + if ( downloadsize > 0) { + last_upload_update = (100 * upload.totalSize) / downloadsize; + } else { + last_upload_update = upload.totalSize; + } + String s = "Update "; + s+= String(last_upload_update); + s+="%"; + output.printMSG(s.c_str()); + } + } + if(Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + _upload_status=UPLOAD_STATUS_CANCELLED; + output.printERROR("Update write failed!"); + _webserver->send (500, "text/plain", "Update write failed!"); + return; + } + } +//Upload end + + } else if(upload.status == UPLOAD_FILE_END) { + if ((downloadsize!=0) && (downloadsize < upload.totalSize)) { + _upload_status=UPLOAD_STATUS_CANCELLED; + } + if (_upload_status == UPLOAD_STATUS_ONGOING) { + if(Update.end(true)) { //true to set the size to the current progress + //Now Reboot + output.printMSG("Update 100%"); + _upload_status=UPLOAD_STATUS_SUCCESSFUL; + } else { + output.printERROR("Update failed!"); + _webserver->send (500, "text/plain", "Update failed!"); + } + } else { + output.printERROR("Update failed!"); + _webserver->send (500, "text/plain", "Update failed!"); + } + } else if(upload.status == UPLOAD_FILE_ABORTED) { + output.printERROR("Update failed!"); + _webserver->send (500, "text/plain", "Update failed!"); + Update.end(); + _upload_status=UPLOAD_STATUS_CANCELLED; + } +} +#endif //HTTP_FEATURE && WEB_UPDATE_FEATURE diff --git a/esp3d/src/modules/http/http_server.cpp b/esp3d/src/modules/http/http_server.cpp new file mode 100644 index 00000000..a039ec55 --- /dev/null +++ b/esp3d/src/modules/http/http_server.cpp @@ -0,0 +1,220 @@ +/* + http_server.cpp - http server functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#include "../../include/esp3d_config.h" + +#if defined (HTTP_FEATURE) +#if defined (ARDUINO_ARCH_ESP32) +#include +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +#include +#endif //ARDUINO_ARCH_ESP8266 +#include "http_server.h" +#include "../authentication/authentication_service.h" +#include "../../core/settings_esp3d.h" +#include "../filesystem/esp_filesystem.h" + +bool HTTP_Server::_started = false; +uint16_t HTTP_Server::_port = 0; +WEBSERVER * HTTP_Server::_webserver = nullptr; +uint8_t HTTP_Server::_upload_status = UPLOAD_STATUS_NONE; + +HTTP_Server::HTTP_Server() +{ +} +HTTP_Server::~HTTP_Server() +{ + end(); +} + +void HTTP_Server::init_handlers() +{ + _webserver->on("/",HTTP_ANY, handle_root); + //Page not found handler + _webserver->onNotFound (handle_not_found); + //web commands + _webserver->on ("/command", HTTP_ANY, handle_web_command); + //need to be there even no authentication to say to UI no authentication + _webserver->on("/login", HTTP_ANY, handle_login); +#ifdef FILESYSTEM_FEATURE + //FileSystem + _webserver->on ("/files", HTTP_ANY, handleFSFileList, FSFileupload); +#endif //FILESYSTEM_FEATURE +#ifdef WEB_UPDATE_FEATURE + //web update + _webserver->on ("/updatefw", HTTP_ANY, handleUpdate, WebUpdateUpload); +#endif //WEB_UPDATE_FEATURE +#ifdef SSDP_FEATURE + if(WiFi.getMode() != WIFI_AP) { + _webserver->on("/description.xml", HTTP_GET, handle_SSDP); + } +#endif +#ifdef CAPTIVE_PORTAL_FEATURE + if(WiFi.getMode() == WIFI_AP) { + _webserver->on ("/generate_204", HTTP_ANY, handle_root); + _webserver->on ("/gconnectivitycheck.gstatic.com", HTTP_ANY, handle_root); + //do not forget the / at the end + _webserver->on ("/fwlink/", HTTP_ANY, handle_root); + } +#endif +} + +bool HTTP_Server::StreamFSFile(const char* filename, const char * contentType) +{ + ESP_File datafile = ESP_FileSystem::open(filename); + if (!datafile) { + return false; + } + size_t totalFileSize = datafile.size(); + size_t i = 0; + bool done = false; + _webserver->setContentLength(totalFileSize); + _webserver->send(200, contentType, ""); + uint8_t buf[1024]; + while (!done) { + Hal::wait(0); + int v = datafile.read(buf,1024); + if ((v == -1) || (v == 0)) { + done = true; + } else { + _webserver->client().write(buf,v); + i+=v; + } + if (i >= totalFileSize) { + done = true; + } + } + datafile.close(); + if ( i != totalFileSize) { + return false; + } + return true; +} + +bool HTTP_Server::begin() +{ + bool no_error = true; + end(); + _port = Settings_ESP3D::read_uint32(ESP_HTTP_PORT); + _webserver= new WEBSERVER(_port); + if (!_webserver) { + return false; + } + + init_handlers(); +#ifdef AUTHENTICATION_FEATURE + //here the list of headers to be recorded + //Autrization is already added + const char * headerkeys[] = {"Cookie"} ; + size_t headerkeyssize = sizeof (headerkeys) / sizeof (char*); + //ask server to track these headers + _webserver->collectHeaders (headerkeys, headerkeyssize ); +#endif + _webserver->begin(); +#ifdef AUTHENTICATION_FEATURE + AuthenticationService::begin(_webserver); +#endif //AUTHENTICATION_FEATURE + + _started = no_error; + return no_error; +} + +void HTTP_Server::end() +{ + _started = false; + _upload_status = UPLOAD_STATUS_NONE; +#ifdef AUTHENTICATION_FEATURE + AuthenticationService::end(); +#endif //AUTHENTICATION_FEATURE + if (_webserver) { + _webserver->stop(); + delete _webserver; + _webserver = NULL; + } +} + +void HTTP_Server::handle() +{ + if (_started) { + if (_webserver) { + _webserver->handleClient(); + } + } +} + +const char * HTTP_Server::get_Splited_Value(String data, char separator, int index) +{ + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length()-1; + + for(int i=0; i<=maxIndex && found<=index; i++) { + if(data.charAt(i)==separator || i==maxIndex) { + found++; + strIndex[0] = strIndex[1]+1; + strIndex[1] = (i == maxIndex) ? i+1 : i; + } + } + + return found>index ? data.substring(strIndex[0], strIndex[1]).c_str() : ""; +} + +//helper to extract content type from file extension +//Check what is the content tye according extension file +const char* HTTP_Server::getContentType (const char* filename) +{ + String file_name = filename; + file_name.toLowerCase(); + if (file_name.endsWith (".htm") ) { + return "text/html"; + } else if (file_name.endsWith (".html") ) { + return "text/html"; + } else if (file_name.endsWith (".css") ) { + return "text/css"; + } else if (file_name.endsWith (".js") ) { + return "application/javascript"; + } else if (file_name.endsWith (".png") ) { + return "image/png"; + } else if (file_name.endsWith (".gif") ) { + return "image/gif"; + } else if (file_name.endsWith (".jpeg") ) { + return "image/jpeg"; + } else if (file_name.endsWith (".jpg") ) { + return "image/jpeg"; + } else if (file_name.endsWith (".ico") ) { + return "image/x-icon"; + } else if (file_name.endsWith (".xml") ) { + return "text/xml"; + } else if (file_name.endsWith (".pdf") ) { + return "application/x-pdf"; + } else if (file_name.endsWith (".zip") ) { + return "application/x-zip"; + } else if (file_name.endsWith (".gz") ) { + return "application/x-gzip"; + } else if (file_name.endsWith (".txt") ) { + return "text/plain"; + } + return "application/octet-stream"; +} + +#endif // Enable HTTP + diff --git a/esp3d/src/modules/http/http_server.h b/esp3d/src/modules/http/http_server.h new file mode 100644 index 00000000..cd91761d --- /dev/null +++ b/esp3d/src/modules/http/http_server.h @@ -0,0 +1,95 @@ +/* + http_server.h - http server functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef _HTTP_SERVER_H +#define _HTTP_SERVER_H +#include "../../include/esp3d_config.h" +//class WebSocketsServer; +#if defined (ARDUINO_ARCH_ESP32) +class WebServer; +#define WEBSERVER WebServer +#endif //ARDUINO_ARCH_ESP32 +#if defined (ARDUINO_ARCH_ESP8266) +class ESP8266WebServer; +#define WEBSERVER ESP8266WebServer +#endif //ARDUINO_ARCH_ESP8266 + + +//Upload status +typedef enum { + UPLOAD_STATUS_NONE = 0, + UPLOAD_STATUS_FAILED = 1, + UPLOAD_STATUS_CANCELLED = 2, + UPLOAD_STATUS_SUCCESSFUL = 3, + UPLOAD_STATUS_ONGOING = 4 +} upload_status_type; + +class HTTP_Server +{ +public: + HTTP_Server(); + ~HTTP_Server(); + static bool begin(); + static void end(); + static void handle(); + static bool started() + { + return _started; + } + static uint16_t port() + { + return _port; + } +private: + static bool _started; + static WEBSERVER * _webserver; + static uint16_t _port; + static uint8_t _upload_status; + static const char * getContentType (const char * filename); + static const char * get_Splited_Value(String data, char separator, int index); +#ifdef SSDP_FEATURE + static void handle_SSDP (); +#endif //SSDP_FEATURE + static void init_handlers(); + static bool StreamFSFile(const char* filename, const char * contentType); + static void handle_root(); + static void handle_login(); + static void handle_not_found (); + static void handle_web_command (); + // static void handle_Websocket_Event(uint8_t num, uint8_t type, uint8_t * payload, size_t length); +#ifdef FILESYSTEM_FEATURE + static void FSFileupload (); + static void handleFSFileList (); +#endif //FILESYSTEM_FEATURE +#ifdef WEB_UPDATE_FEATURE + static void handleUpdate (); + static void WebUpdateUpload (); +#endif //WEB_UPDATE_FEATURE + //static bool is_realtime_cmd(char c); +#ifdef ENABLE_SD_CARD + //static void handle_direct_SDFileList(); + //static void SDFile_direct_upload(); + //static bool deleteRecursive(String path); +#endif +}; + +#endif //_HTTP_SERVER_H + diff --git a/esp3d/src/modules/network/netconfig.cpp b/esp3d/src/modules/network/netconfig.cpp new file mode 100644 index 00000000..b94d4771 --- /dev/null +++ b/esp3d/src/modules/network/netconfig.cpp @@ -0,0 +1,373 @@ +/* + netconfig.cpp - network functions class + + Copyright (c) 2018 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) || defined (ETH_FEATURE) +#ifdef ARDUINO_ARCH_ESP32 +#define WIFI_EVENT_STAMODE_CONNECTED SYSTEM_EVENT_STA_CONNECTED +#define WIFI_EVENT_STAMODE_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED +#define WIFI_EVENT_STAMODE_GOT_IP SYSTEM_EVENT_STA_GOT_IP +#define WIFI_EVENT_SOFTAPMODE_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#endif //ARDUINO_ARCH_ESP8266 +#include "netconfig.h" +#if defined (WIFI_FEATURE) +#include "../wifi/wificonfig.h" +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) +#include "../ethernet/ethconfig.h" +#endif //ETH_FEATURE +#if defined (BLUETOOTH_FEATURE) +#include "../bluetooth/BT_service.h" +#endif //BLUETOOTH_FEATURE +#include "netservices.h" +#include "../../core/esp3doutput.h" +#include "../../core/settings_esp3d.h" + +String NetConfig::_hostname = ""; +bool NetConfig::_needReconnect2AP = false; +bool NetConfig::_events_registered = false; +bool NetConfig::_started = false; + +NetConfig::NetConfig() +{ +} + +NetConfig::~NetConfig() +{ + //end(); +} + +//just simple helper to convert mac address to string +char * NetConfig::mac2str (uint8_t mac [8]) +{ + static char macstr [18]; + if (0 > sprintf (macstr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) ) { + strcpy (macstr, "00:00:00:00:00:00"); + } + return macstr; +} + + +/** + * Helper to convert IP string to int + */ +uint32_t NetConfig::IP_int_from_string(const char * s) +{ + uint32_t ip_int = 0; + IPAddress ipaddr; + if (ipaddr.fromString(s)) { + ip_int = ipaddr; + } + return ip_int; +} + +/** + * Helper to convert int to IP string + */ +String NetConfig::IP_string_from_int(uint32_t ip_int) +{ + IPAddress ipaddr(ip_int); + return ipaddr.toString(); +} + +/** + * Check if Hostname string is valid + */ + +bool NetConfig::isHostnameValid (const char * hostname) +{ + //limited size + char c; + if (strlen (hostname) > MAX_HOSTNAME_LENGTH || strlen (hostname) < MIN_HOSTNAME_LENGTH) { + return false; + } + //only letter and digit + for (uint i = 0; i < strlen (hostname); i++) { + c = hostname[i]; + if (! (isdigit (c) || isalpha (c) || c == '-') ) { + return false; + } + if (c == ' ') { + return false; + } + } + return true; +} + +/** + * Check if IP string is valid + */ + +bool NetConfig::isValidIP(const char * string) +{ + IPAddress ip; + return ip.fromString(string); +} + + +//wifi event +void NetConfig::onWiFiEvent(WiFiEvent_t event) +{ + ESP3DOutput output(ESP_PRINTER_LCD_CLIENT); + switch (event) { + case WIFI_EVENT_STAMODE_CONNECTED: + _needReconnect2AP = false; +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(wifi_config.getSignal (WiFi.RSSI ())); + OLED_DISPLAY::setCursor(0, 0); + ESPCOM::print("", OLED_PIPE); +#endif //ESP_OLED_FEATURE + break; + case WIFI_EVENT_STAMODE_DISCONNECTED: +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::display_signal(-1); + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print("", OLED_PIPE); + OLED_DISPLAY::setCursor(0, 48); +#endif //ESP_OLED_FEATURE + if(_started) { + output.printMSG ("Disconnected"); + _needReconnect2AP = true; + } + break; + case WIFI_EVENT_STAMODE_GOT_IP: + output.printMSG ("Connected"); + output.printMSG (WiFi.localIP().toString().c_str()); +#ifdef ESP_OLED_FEATURE + OLED_DISPLAY::setCursor(0, 16); + ESPCOM::print(WiFi.localIP().toString().c_str(), OLED_PIPE); + OLED_DISPLAY::setCursor(0, 48); + ESPCOM::print("", OLED_PIPE); +#endif //ESP_OLED_FEATURE + break; + case WIFI_EVENT_SOFTAPMODE_STACONNECTED: + output.printMSG ("New client"); + break; +#ifdef ARDUINO_ARCH_ESP32 + case SYSTEM_EVENT_STA_LOST_IP: + if(_started) { + _needReconnect2AP = true; + } + break; +#ifdef ETH_FEATURE + case SYSTEM_EVENT_ETH_CONNECTED: + output.printMSG ("Cable connected"); + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + output.printMSG ("Cable disconnected"); + break; + case SYSTEM_EVENT_ETH_GOT_IP: + output.printMSG (ETH.localIP().toString().c_str()); + break; +#endif //ETH_FEATURE +#endif //ARDUINO_ARCH_ESP32 + default: + break; + } + +} + +/** + * begin WiFi setup + */ +bool NetConfig::begin() +{ + bool res = false; + //clear everything + end(); + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printMSG("Starting Network"); + //setup events + if(!_events_registered) { +#ifdef ARDUINO_ARCH_ESP8266 + WiFi.onEvent(NetConfig::onWiFiEvent, WIFI_EVENT_ANY); + +#endif +#ifdef ARDUINO_ARCH_ESP32 + WiFi.onEvent(NetConfig::onWiFiEvent); + +#endif + _events_registered = true; + } + //Get hostname + _hostname = Settings_ESP3D::read_string(ESP_HOSTNAME); + int8_t espMode =Settings_ESP3D::read_byte(ESP_RADIO_MODE); + if (espMode == NO_NETWORK) { + return true; + } +#if defined (WIFI_FEATURE) + if ((espMode == ESP_WIFI_AP) || (espMode == ESP_WIFI_STA)) { + res = WiFiConfig::begin(); + } +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) + //if ((espMode == ESP_ETH_STA) || (espMode == ESP_ETH_SRV)) { + if ((espMode == ESP_ETH_STA)) { + res = EthConfig::begin(); + } +#endif //ETH_FEATURE +#if defined (BLUETOOTH_FEATURE) + if ((espMode == ESP_BT)) { + res = bt_service.begin(); + } +#endif //BLUETOOTH_FEATURE + //if network is up, let's start services + if (res) { + bool start_services = false; +#if defined (ETH_FEATURE) + if (EthConfig::started()) { + start_services = true; + } +#endif //ETH_FEATURE +#if defined (WIFI_FEATURE) + if (WiFiConfig::started()) { + start_services = true; + } +#endif //WIFI_FEATURE + if (start_services) { + res = NetServices::begin(); + } + } + //work around as every services seems reset the AP name +#ifdef ARDUINO_ARCH_ESP32 +#if defined (WIFI_FEATURE) + if (WiFi.getMode() == WIFI_AP) { + WiFi.softAPsetHostname(_hostname.c_str()); + } +#endif //WIFI_FEATURE +#endif //ARDUINO_ARCH_ESP32 + if (res) { + _started = true; + } else { + end(); + } + return res; +} + +/** + * End WiFi + */ + +void NetConfig::end() +{ + NetServices::end(); + +#if defined (WIFI_FEATURE) + WiFiConfig::end(); + _needReconnect2AP=false; +#else + WiFi.mode(WIFI_OFF); +#endif //WIFI_FEATURE + +#if defined (ETH_FEATURE) + EthConfig::end(); +#endif //ETH_FEATURE + ESP3DOutput output(ESP_ALL_CLIENTS); +#if defined (BLUETOOTH_FEATURE) + bt_service.end(); +#endif //BLUETOOTH_FEATURE + output.printMSG("Network Off"); + _started = false; +} + +const char* NetConfig::hostname(bool fromsettings) +{ + if (fromsettings) { + _hostname = Settings_ESP3D::read_string(ESP_HOSTNAME); + return _hostname.c_str(); + } +#if defined (WIFI_FEATURE) + if(WiFi.getMode()!= WIFI_OFF) { + _hostname = WiFiConfig::hostname(); + return _hostname.c_str(); + } +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) + if(EthConfig::started()) { + return ETH.getHostname(); + } +#endif //ETH_FEATURE + +#if defined (BLUETOOTH_FEATURE) + if(bt_service.started()) { + return bt_service.hostname(); + } +#endif //BLUETOOTH_FEATURE + return _hostname.c_str(); +} + +/** + * Handle not critical actions that must be done in sync environement + */ + +void NetConfig::handle() +{ +#if defined (WIFI_FEATURE) + if(_needReconnect2AP) { + + if(WiFi.getMode()!= WIFI_OFF) { + begin(); + } + } + WiFiConfig::handle(); +#endif //WIFI_FEATURE +#if defined (ETH_FEATURE) + EthConfig::handle(); +#endif //ETH_FEATURE +#if defined (BLUETOOTH_FEATURE) + bt_service.handle(); +#endif //BLUETOOTH_FEATURE + NetServices::handle(); +} + +bool NetConfig::isIPModeDHCP (uint8_t mode) +{ + bool started = false; +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcpc_get_status ((mode == ESP_WIFI_STA)?TCPIP_ADAPTER_IF_STA:(mode == ESP_WIFI_AP)?TCPIP_ADAPTER_IF_AP:TCPIP_ADAPTER_IF_ETH, &dhcp_status); + started = (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + (void)mode; + started = (wifi_station_dhcpc_status() == DHCP_STARTED); +#endif //ARDUINO_ARCH_ESP8266 + return started; +} + +bool NetConfig::isDHCPServer (uint8_t mode) +{ + bool itis = false; +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_dhcp_status_t dhcp_status; + tcpip_adapter_dhcps_get_status ((mode == ESP_WIFI_STA)?TCPIP_ADAPTER_IF_STA:(mode == ESP_WIFI_AP)?TCPIP_ADAPTER_IF_AP:TCPIP_ADAPTER_IF_ETH, &dhcp_status); + itis = (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + (void)mode; + itis = (wifi_softap_dhcps_status() == DHCP_STARTED); +#endif //ARDUINO_ARCH_ESP8266 + return itis; +} + +#endif // WIFI_FEATURE || ETH_FEATURE + diff --git a/esp3d/src/modules/network/netconfig.h b/esp3d/src/modules/network/netconfig.h new file mode 100644 index 00000000..0de02215 --- /dev/null +++ b/esp3d/src/modules/network/netconfig.h @@ -0,0 +1,83 @@ +/* + netconfig.h - network functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//boundaries + +#define MAX_SERVER_ADDRESS_LENGTH 128 +#define MIN_SERVER_ADDRESS_LENGTH 0 + +#define MAX_HTTP_PORT 65001 +#define MIN_HTTP_PORT 1 +#define MAX_TELNET_PORT 65001 +#define MIN_TELNET_PORT 1 +#define MAX_HOSTNAME_LENGTH 32 +#define MIN_HOSTNAME_LENGTH 1 + +//IP mode +#define DHCP_MODE 1 +#define STATIC_IP_MODE 2 + +//Network Mode +#define ESP_RADIO_OFF 0 +#define ESP_WIFI_STA 1 +#define ESP_WIFI_AP 2 +#define ESP_BT 3 +#define ESP_ETH_STA 4 +//#define ESP_ETH_SRV 5 + +#ifdef ARDUINO_ARCH_ESP32 +#include +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#include +#endif //ARDUINO_ARCH_ESP8266 + +#ifndef _NET_CONFIG_H +#define _NET_CONFIG_H + +class NetConfig +{ +public: + NetConfig(); + ~NetConfig(); + static bool isValidIP(const char * string); + static bool isHostnameValid (const char * hostname); + static uint32_t IP_int_from_string(const char * s); + static String IP_string_from_int(uint32_t ip_int); + static bool isIPModeDHCP(uint8_t mode); + static bool isDHCPServer (uint8_t mode); + static const char* hostname(bool fromsettings = false); + static char * mac2str (uint8_t mac [8]); + static bool begin(); + static void end(); + static void handle(); + static bool started() + { + return _started; + } +private : + static String _hostname; + static void onWiFiEvent(WiFiEvent_t event); + static bool _needReconnect2AP; + static bool _events_registered; + static bool _started; +}; + +#endif //_NET_CONFIG_H diff --git a/esp3d/src/modules/network/netservices.cpp b/esp3d/src/modules/network/netservices.cpp new file mode 100644 index 00000000..31fbd025 --- /dev/null +++ b/esp3d/src/modules/network/netservices.cpp @@ -0,0 +1,303 @@ +/* + netservices.cpp - network services functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" + +#include "netconfig.h" +#include "netservices.h" +#include "../../core/settings_esp3d.h" +#include "../../core/esp3doutput.h" +#if defined( ARDUINO_ARCH_ESP8266) +#ifdef MDNS_FEATURE +#include +#endif //MDNS_FEATURE +#ifdef SSDP_FEATURE +#include +#endif //SSDP_FEATURE +#endif //ARDUINO_ARCH_ESP8266 +#if defined( ARDUINO_ARCH_ESP32) +#ifdef MDNS_FEATURE +#include +#endif //MDNS_FEATURE +#ifdef SSDP_FEATURE +#include +#endif //SSDP_FEATURE +#endif //ARDUINO_ARCH_ESP32 +#ifdef OTA_FEATURE +#include +#endif //OTA_FEATURE +#if defined(FILESYSTEM_FEATURE) +#include "../filesystem/esp_filesystem.h" +#endif //FILESYSTEM_FEATURE +#ifdef TELNET_FEATURE +#include "../telnet/telnet_server.h" +#endif //TELNET_FEATURE +#ifdef HTTP_FEATURE +#include "../http/http_server.h" +#endif //HTTP_FEATURE +#if defined(HTTP_FEATURE) || defined(WS_DATA_FEATURE) +#include "../websocket/websocket_server.h" +#endif //HTTP_FEATURE || WS_DATA_FEATURE +#ifdef CAPTIVE_PORTAL_FEATURE +#include +const byte DNS_PORT = 53; +DNSServer dnsServer; +#endif //CAPTIVE_PORTAL_FEATURE + +bool NetServices::_started = false; + +NetServices::NetServices() +{ +} +NetServices::~NetServices() +{ + end(); +} + +bool NetServices::begin() +{ + bool res = true; + _started = false; + String hostname = Settings_ESP3D::read_string(ESP_HOSTNAME); + ESP3DOutput output(ESP_ALL_CLIENTS); + end(); +#if defined(MDNS_FEATURE) && defined(ARDUINO_ARCH_ESP8266) + if(WiFi.getMode() != WIFI_AP) { + String lhostname =hostname; + lhostname.toLowerCase(); + if (!MDNS.begin(hostname.c_str())) { + output.printERROR("mDNS failed to start"); + _started =false; + } else { + String stmp = "mDNS started with '" + lhostname + ".local'"; + output.printMSG(stmp.c_str()); + } + } +#endif //MDNS_FEATURE && ARDUINO_ARCH_ESP8266 + +#ifdef OTA_FEATURE + if(WiFi.getMode() != WIFI_AP) { + ArduinoOTA.onStart([]() { + ESP3DOutput output(ESP_ALL_CLIENTS); + String type = "Start OTA updating "; + if (ArduinoOTA.getCommand() == U_FLASH) { + type += "sketch"; + } else { // U_SPIFFS or any FS + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + type += "filesystem"; +#if defined(FILESYSTEM_FEATURE) + ESP_FileSystem::end(); +#endif //FILESYSTEM_FEATURE + + } + output.printMSG(type.c_str()); + }); + ArduinoOTA.onEnd([]() { + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printMSG("End OTA"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + String prg = "OTA Progress "; + ESP3DOutput output(ESP_ALL_CLIENTS); + prg += String(progress / (total / 100)) + "%"; + output.printMSG(prg.c_str()); + }); + ArduinoOTA.onError([](ota_error_t error) { + String stmp = "OTA Error: " + String(error); + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printERROR(stmp.c_str()); + if (error == OTA_AUTH_ERROR) { + output.printERROR("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + output.printERROR("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + output.printERROR("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + output.printERROR("Receive Failed"); + } else if (error == OTA_END_ERROR) { + output.printERROR("End Failed"); + } + }); + output.printMSG("OTA service started"); + ArduinoOTA.begin(); + } +#endif + +#if defined(MDNS_FEATURE) && defined(ARDUINO_ARCH_ESP32) + if(WiFi.getMode() != WIFI_AP) { + String lhostname =hostname; + lhostname.toLowerCase(); + if (!MDNS.begin(hostname.c_str())) { + output.printERROR("mDNS failed to start"); + _started =false; + } else { + String stmp = "mDNS started with '" + lhostname + ".local'"; + output.printMSG(stmp.c_str()); + } + } +#endif //MDNS_FEATURE && ARDUINO_ARCH_ESP8266 + +#ifdef CAPTIVE_PORTAL_FEATURE + if(WiFi.getMode() == WIFI_AP) { + // if DNSServer is started with "*" for domain name, it will reply with + // provided IP to all DNS request + if (dnsServer.start(DNS_PORT, "*", WiFi.softAPIP())) { + output.printMSG("Captive Portal started"); + } else { + output.printERROR("Failed start Captive Portal"); + } + } +#endif //CAPTIVE_PORTAL_FEATURE +#ifdef HTTP_FEATURE + if (!HTTP_Server::begin()) { + res= false; + output.printERROR("HTTP server failed"); + } else { + String stmp = "HTTP server started port " + String(HTTP_Server::port()); + output.printMSG(stmp.c_str()); + } +#endif //HTTP_FEATURE +#ifdef TELNET_FEATURE + if (!telnet_server.begin()) { + res= false; + output.printERROR("Telnet server failed"); + } else { + String stmp = "Telnet server started port " + String(telnet_server.port()); + output.printMSG(stmp.c_str()); + } +#endif //TELNET_FEATURE +#if defined(HTTP_FEATURE) + if (!websocket_terminal_server.begin()) { + output.printMSG("Failed start Terminal Web Socket"); + } +#endif //HTTP_FEATURE || WS_DATA_FEATURE +#ifdef MDNS_FEATURE + if(WiFi.getMode() != WIFI_AP) { + // Add service to MDNS-SD + MDNS.addService("http", "tcp", 80); + } +#endif //MDNS_FEATURE +#ifdef SSDP_FEATURE + //SSDP service presentation + if(WiFi.getMode() != WIFI_AP) { + //Add specific for SSDP + String stmp = String(Hal::getChipID()); + SSDP.setSchemaURL ("description.xml"); + SSDP.setHTTPPort (HTTP_Server::port()); + SSDP.setName (hostname.c_str()); + SSDP.setURL ("/"); + SSDP.setDeviceType ("upnp:rootdevice"); + SSDP.setSerialNumber (stmp.c_str()); + //Any customization could be here + SSDP.setModelName (ESP_MODEL_NAME); + SSDP.setModelURL (ESP_MODEL_URL); + SSDP.setModelNumber (ESP_MODEL_NUMBER); + SSDP.setManufacturer (ESP_MANUFACTURER_NAME); + SSDP.setManufacturerURL (ESP_MANUFACTURER_URL); + SSDP.begin(); + stmp = "SSDP started with '" + hostname + "'"; + output.printMSG(stmp.c_str()); + } +#endif //SSDP_FEATURE + + /* #ifdef ENABLE_TELNET + telnet_server.begin(); + #endif + return no_error;*/ + if (!res) { + end(); + } + _started = res; + return _started; +} +void NetServices::end() +{ + if(!_started) { + return; + } + _started = false; +#ifdef CAPTIVE_PORTAL_FEATURE + if(WiFi.getMode() == WIFI_AP) { + dnsServer.stop(); + } +#endif //CAPTIVE_PORTAL_FEATURE +#ifdef SSDP_FEATURE +#if defined(ARDUINO_ARCH_ESP32) + SSDP.end(); +#endif //ARDUINO_ARCH_ESP32 +#endif //SSDP_FEATURE +#ifdef MDNS_FEATURE +#if defined(ARDUINO_ARCH_ESP32) + if(WiFi.getMode() != WIFI_AP) { + mdns_service_remove("_http", "_tcp"); + MDNS.end(); + } +#endif // ARDUINO_ARCH_ESP32 +#endif //MDNS_FEATURE + +#ifdef OTA_FEATURE +#if defined(ARDUINO_ARCH_ESP32) + if(WiFi.getMode() != WIFI_AP) { + ArduinoOTA.end(); + } +#endif // ARDUINO_ARCH_ESP32 +#endif //OTA_FEATURE +#if defined(HTTP_FEATURE) + websocket_terminal_server.end(); +#endif //HTTP_FEATURE +#ifdef HTTP_FEATURE + HTTP_Server::end(); +#endif //HTTP_FEATURE + +#ifdef TELNET_FEATURE + telnet_server.end(); +#endif //TELNET_FEATURE + +} + +void NetServices::handle() +{ + if (_started) { +#ifdef MDNS_FEATURE +#if defined(ARDUINO_ARCH_ESP8266) + MDNS.update(); +#endif //ARDUINO_ARCH_ESP8266 +#endif //MDNS_FEATURE +#ifdef OTA_FEATURE + ArduinoOTA.handle(); +#endif //OTA_FEATURE +#ifdef CAPTIVE_PORTAL_FEATURE + if (WiFi.getMode()== WIFI_AP ) { + dnsServer.processNextRequest(); + } +#endif //CAPTIVE_PORTAL_FEATURE +#ifdef HTTP_FEATURE + HTTP_Server::handle(); +#endif //HTTP_FEATURE +#if defined(HTTP_FEATURE) + websocket_terminal_server.handle(); +#endif //HTTP_FEATURE + + } +#ifdef TELNET_FEATURE + telnet_server.handle(); +#endif //TELNET_FEATURE +} + diff --git a/esp3d/src/modules/network/netservices.h b/esp3d/src/modules/network/netservices.h new file mode 100644 index 00000000..e93509c4 --- /dev/null +++ b/esp3d/src/modules/network/netservices.h @@ -0,0 +1,40 @@ +/* + netservices.h - network services functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _NET_SERVICES_H +#define _NET_SERVICES_H + + +class NetServices +{ +public: + NetServices(); + ~NetServices(); + static bool begin(); + static void end(); + static void handle(); +private: + static bool _started; +}; + +#endif //_NET_SERVICES_H + diff --git a/esp3d/src/modules/serial/serial_service.cpp b/esp3d/src/modules/serial/serial_service.cpp new file mode 100644 index 00000000..1c51fcc2 --- /dev/null +++ b/esp3d/src/modules/serial/serial_service.cpp @@ -0,0 +1,266 @@ +/* + serial_service.cpp - serial services functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" +#include "serial_service.h" +#include "../../core/settings_esp3d.h" +#include "../../core/esp3doutput.h" +#include "../../core/commands.h" + +#ifdef USE_SERIAL_0 +#define ESP3D_SERIAL Serial +#endif //USE_SERIAL_0 + +#ifdef USE_SERIAL_1 +#define ESP3D_SERIAL Serial1 +#endif //USE_SERIAL_1 + +#ifdef USE_SERIAL_2 +#define ESP3D_SERIAL Serial2 +#endif //USE_SERIAL_2 + +SerialService serial_service; + +const long SupportedBaudList[] = {9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000, 500000, 921600}; + +#define TIMEOUT_SERIAL_FLUSH 1500 +//Constructor +SerialService::SerialService() +{ + _buffer_size = 0; +} + +//Destructor +SerialService::~SerialService() +{ + end(); +} + +//Setup Serial +bool SerialService::begin() +{ + _lastflush = millis(); + //read from settings + long br = Settings_ESP3D::read_uint32(ESP_BAUD_RATE); + _buffer_size = 0; + //change only if different from current + if (br != baudRate() || (ESP_RX_PIN != -1) || (ESP_TX_PIN != -1)) { + if ( !is_valid_baudrate(br)) { + br = Settings_ESP3D::get_default_int32_value(ESP_BAUD_RATE); + } +#ifdef ARDUINO_ARCH_ESP8266 + ESP3D_SERIAL.begin(br, SERIAL_8N1, SERIAL_FULL, (ESP_TX_PIN == -1)?1:ESP_TX_PIN); +#if ESP_RX_PIN != -1 + ESP3D_SERIAL.pins((ESP_TX_PIN == -1)?1:ESP_TX_PIN, ESP_RX_PIN) +#endif //ESP_RX_PIN != -1 +#endif //ARDUINO_ARCH_ESP8266 +#ifdef ARDUINO_ARCH_ESP32 + ESP3D_SERIAL.begin (br, ESP_SERIAL_PARAM, ESP_RX_PIN, ESP_TX_PIN); +#endif //ARDUINO_ARCH_ESP32 + } + ESP3D_SERIAL.setRxBufferSize (SERIAL_RX_BUFFER_SIZE); + return true; +} +//End serial +bool SerialService::end() +{ + flush(); + delay (100); + swap(); + ESP3D_SERIAL.end(); + _buffer_size = 0; + return true; +} + +//return the array of long and array size +const long * SerialService::get_baudratelist(uint8_t * count) +{ + if (count) { + *count = sizeof(SupportedBaudList)/sizeof(long); + } + return SupportedBaudList; +} + +//check if value is in baudrate list +bool SerialService::is_valid_baudrate(long br) +{ + uint8_t listesize = sizeof(SupportedBaudList)/sizeof(long); + for (uint8_t i = 0; i < listesize ; i++) { + if (SupportedBaudList[i] == br) { + return true; + } + } + return false; +} + +//Function which could be called in other loop +void SerialService::handle() +{ + //Do we have some data waiting + size_t len = available(); + if (len > 0) { + //if yes read them + uint8_t * sbuf = (uint8_t *)malloc(len); + if(sbuf) { + size_t count = readBytes(sbuf, len); + //push to buffer + if (count > 0) { + push2buffer(sbuf, count); + } + //freen buffer + free(sbuf); + } + } + //we cannot left data in buffer too long + //in case some commands "forget" to add \n + if (((millis() - _lastflush) > TIMEOUT_SERIAL_FLUSH) && (_buffer_size > 0)) { + flushbuffer(); + } +} + +void SerialService::flushbuffer() +{ + ESP3DOutput output(ESP_SERIAL_CLIENT); + _buffer[_buffer_size] = 0x0; + //dispatch command + esp3d_commands.process(_buffer, _buffer_size, &output); + _lastflush = millis(); + _buffer_size = 0; +} + +//push collected data to buffer and proceed accordingly +void SerialService::push2buffer(uint8_t * sbuf, size_t len) +{ + for (size_t i = 0; i < len; i++) { + _lastflush = millis(); + //command is defined + if (char(sbuf[i]) == '\n') { + if (_buffer_size < ESP3D_SERIAL_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + flushbuffer(); + } else if (isPrintable (char(sbuf[i]) ) || char(sbuf[i]) == '\r') { + if (_buffer_size < ESP3D_SERIAL_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } else { + flushbuffer(); + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + } else { //it is not printable char + //clean buffer first + if (_buffer_size > 0) { + flushbuffer(); + } + //process char + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + flushbuffer(); + } + } +} + +//Reset Serial Setting (baud rate) +bool SerialService::reset() +{ + return Settings_ESP3D::write_uint32 (ESP_BAUD_RATE, Settings_ESP3D::get_default_int32_value(ESP_BAUD_RATE)); +} + +//Get current baud rate +long SerialService::baudRate() +{ + long br = 0; + br = ESP3D_SERIAL.baudRate(); +#ifdef ARDUINO_ARCH_ESP32 + //workaround for ESP32 + if (br == 115201) { + br = 115200; + } + if (br == 230423) { + br = 230400; + } +#endif //ARDUINO_ARCH_ESP32 + return br; +} + +size_t SerialService::write(uint8_t c) +{ + return ESP3D_SERIAL.write(c); +} + +size_t SerialService::write(const uint8_t *buffer, size_t size) +{ + if (ESP3D_SERIAL.availableForWrite() >= size) { + return ESP3D_SERIAL.write(buffer, size); + } else { + size_t sizetosend = size; + size_t sizesent = 0; + uint8_t *buffertmp=(uint8_t *)buffer; + uint32_t starttime = millis(); + //loop until all is sent or timeout + while (sizetosend>0 && ((millis() - starttime) < 100)) { + size_t available = ESP3D_SERIAL.availableForWrite(); + if(available>0) { + //in case less is sent + available = ESP3D_SERIAL.write(&buffertmp[sizesent], (available >= sizetosend)?sizetosend:available); + sizetosend-=available; + sizesent+=available; + starttime=millis(); + } else { + Hal::wait(5); + } + } + return sizesent; + } +} + +int SerialService::availableForWrite() +{ + return ESP3D_SERIAL.availableForWrite(); +} + +int SerialService::available() +{ + return ESP3D_SERIAL.available(); +} + +int SerialService::read() +{ + return ESP3D_SERIAL.read(); +} + +size_t SerialService::readBytes(uint8_t * sbuf, size_t len) +{ + return ESP3D_SERIAL.readBytes(sbuf, len); +} + +void SerialService::flush() +{ + ESP3D_SERIAL.flush(); +} + +void SerialService::swap() +{ +#ifdef ARDUINO_ARCH_ESP8266 + ESP3D_SERIAL.swap(); +#endif //ARDUINO_ARCH_ESP8266 +} diff --git a/esp3d/src/modules/serial/serial_service.h b/esp3d/src/modules/serial/serial_service.h new file mode 100644 index 00000000..0c598036 --- /dev/null +++ b/esp3d/src/modules/serial/serial_service.h @@ -0,0 +1,80 @@ +/* + serial_service.h - serial services functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _SERIAL_SERVICES_H +#define _SERIAL_SERVICES_H + +#include "Print.h" + +#define ESP3D_SERIAL_BUFFER_SIZE 1024 + +class SerialService : public Print +{ +public: + SerialService(); + ~SerialService(); + bool begin(); + bool end(); + void handle(); + bool reset(); + long baudRate(); + const long * get_baudratelist(uint8_t * count); + void flush(); + void swap(); + int availableForWrite(); + int available(); + bool is_valid_baudrate(long br); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size); + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + int read(); + size_t readBytes (uint8_t * sbuf, size_t len); + +private: + uint32_t _lastflush; + uint8_t _buffer[ESP3D_SERIAL_BUFFER_SIZE + 1]; //keep space of 0x0 terminal + size_t _buffer_size; + void push2buffer(uint8_t * sbuf, size_t len); + void flushbuffer(); +}; + +extern SerialService serial_service; + +#endif //_SERIAL_SERVICES_H + diff --git a/esp3d/src/modules/telnet/telnet_server.cpp b/esp3d/src/modules/telnet/telnet_server.cpp new file mode 100644 index 00000000..fbc14837 --- /dev/null +++ b/esp3d/src/modules/telnet/telnet_server.cpp @@ -0,0 +1,281 @@ +/* + telnet_server.cpp - telnet server functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" + +#if defined (TELNET_FEATURE) +#include +#include +#include "telnet_server.h" +#include "../../core/settings_esp3d.h" +#include "../../core/esp3doutput.h" +#include "../../core/commands.h" + +Telnet_Server telnet_server; + +#define TIMEOUT_TELNET_FLUSH 1500 + +bool Telnet_Server::isConnected() +{ + if ( !_started || _telnetserver == NULL) { + return false; + } + //check if there are any new clients + if (_telnetserver->hasClient()) { + //find free/disconnected spot + if (!_telnetClients || !_telnetClients.connected()) { + if(_telnetClients) { + _telnetClients.stop(); + } + _telnetClients = _telnetserver->available(); + } + } + if (_telnetserver->hasClient()) { + //no free/disconnected spot so reject + _telnetserver->available().stop(); + } + return _telnetClients.connected(); +} + +const char* Telnet_Server::clientIPAddress() +{ + static String res; + res = "0.0.0.0"; + if (_telnetClients && _telnetClients.connected()) { + res = _telnetClients.remoteIP().toString(); + } + return res.c_str(); +} + + +Telnet_Server::Telnet_Server() +{ + _buffer_size = 0; + _started = false; + _port = 0; + WiFiServer * _telnetserver = nullptr; +} +Telnet_Server::~Telnet_Server() +{ + end(); +} + +/** + * begin Telnet setup + */ +bool Telnet_Server::begin() +{ + end(); + //Get telnet port + _port = Settings_ESP3D::read_uint32(ESP_TELNET_PORT); + //create instance + _telnetserver= new WiFiServer(_port); + if (!_telnetserver) { + return false; + } + _telnetserver->setNoDelay(true); + //start telnet server + _telnetserver->begin(); + _started = true; + _lastflush = millis(); + return _started; +} +/** + * End Telnet + */ +void Telnet_Server::end() +{ + _started = false; + _buffer_size = 0; + _port = 0; + if (_telnetserver) { + delete _telnetserver; + _telnetserver = nullptr; + } +} + +/** + * Reset Telnet + */ +bool Telnet_Server::reset() +{ + //nothing to reset + return true; +} + +bool Telnet_Server::started() +{ + return _started; +} + +void Telnet_Server::handle() +{ + Hal::wait(0); + if (isConnected()) { + //check clients for data + size_t len = _telnetClients.available(); + if(len > 0) { + //if yes read them + uint8_t * sbuf = (uint8_t *)malloc(len); + if(sbuf) { + size_t count = _telnetClients.read(sbuf, len); + //push to buffer + if (count > 0) { + push2buffer(sbuf, count); + } + //freen buffer + free(sbuf); + } + } + + } + //we cannot left data in buffer too long + //in case some commands "forget" to add \n + if (((millis() - _lastflush) > TIMEOUT_TELNET_FLUSH) && (_buffer_size > 0)) { + flushbuffer(); + } +} + +void Telnet_Server::flushbuffer() +{ + ESP3DOutput output(ESP_TELNET_CLIENT); + _buffer[_buffer_size] = 0x0; + //dispatch command + esp3d_commands.process(_buffer, _buffer_size, &output); + _lastflush = millis(); + _buffer_size = 0; +} + +void Telnet_Server::push2buffer(uint8_t * sbuf, size_t len) +{ + for (size_t i = 0; i < len; i++) { + _lastflush = millis(); + //command is defined + if (char(sbuf[i]) == '\n') { + if (_buffer_size < ESP3D_TELNET_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + flushbuffer(); + } else if (isPrintable (char(sbuf[i]) ) || char(sbuf[i]) == '\r') { + if (_buffer_size < ESP3D_TELNET_BUFFER_SIZE) { + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } else { + flushbuffer(); + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + } + } else { //it is not printable char + //clean buffer first + if (_buffer_size > 0) { + flushbuffer(); + } + //process char + _buffer[_buffer_size] = sbuf[i]; + _buffer_size++; + flushbuffer(); + } + } +} + +size_t Telnet_Server::write(uint8_t c) +{ + return write(&c,1); +} + +size_t Telnet_Server::write(const uint8_t *buffer, size_t size) +{ + if (isConnected()) { + if (availableForWrite() >= size) { + //push data to connected telnet client + return _telnetClients.write(buffer, size); + } else { + size_t sizetosend = size; + size_t sizesent = 0; + uint8_t *buffertmp=(uint8_t *)buffer; + uint32_t starttime = millis(); + //loop until all is sent or timeout + while (sizetosend>0 && ((millis() - starttime) < 100)) { + size_t available = availableForWrite(); + if(available>0) { + //in case less is sent + available = _telnetClients.write(&buffertmp[sizesent], (available >= sizetosend)?sizetosend:available); + sizetosend-=available; + sizesent+=available; + starttime=millis(); + } else { + Hal::wait(5); + } + } + return sizesent; + } + } + return 0; +} + +int Telnet_Server::availableForWrite() +{ + if (!isConnected()) { + return 0; + } +#ifdef ARDUINO_ARCH_ESP32 + return 128; //hard code for esp32 +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + return _telnetClients.availableForWrite(); +#endif //ARDUINO_ARCH_ESP8266 +} + +int Telnet_Server::available() +{ + if(isConnected()) { + return _telnetClients.available(); + } + return 0; +} + +int Telnet_Server::read(void) +{ + if(isConnected()) { + if(_telnetClients.available() > 0) { + return _telnetClients.read(); + } + } + return -1; +} + +size_t Telnet_Server::readBytes(uint8_t * sbuf, size_t len) +{ + if(isConnected()) { + if(_telnetClients.available() > 0) { + return _telnetClients.read(sbuf, len); + } + } + return 0; +} + + +void Telnet_Server::flush() +{ + _telnetClients.flush(); +} + +#endif //TELNET_FEATURE diff --git a/esp3d/src/modules/telnet/telnet_server.h b/esp3d/src/modules/telnet/telnet_server.h new file mode 100644 index 00000000..40c100f8 --- /dev/null +++ b/esp3d/src/modules/telnet/telnet_server.h @@ -0,0 +1,87 @@ +/* + telnet_server.h - telnet service functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _TELNET_SERVER_H +#define _TELNET_SERVER_H + +class WiFiServer; +class WiFiClient; + +#define ESP3D_TELNET_BUFFER_SIZE 1200 + +class Telnet_Server +{ +public: + Telnet_Server(); + ~Telnet_Server(); + bool begin(); + void end(); + void handle(); + bool reset(); + bool started(); + bool isConnected(); + const char* clientIPAddress(); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size); + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + int available(); + int availableForWrite(); + void flush(); + int read(void); + size_t readBytes (uint8_t * sbuf, size_t len); + uint16_t port() + { + return _port; + } +private: + bool _started; + WiFiServer * _telnetserver; + WiFiClient _telnetClients; + uint16_t _port; + uint32_t _lastflush; + uint8_t _buffer[ESP3D_TELNET_BUFFER_SIZE +1]; //keep space of 0x0 terminal + size_t _buffer_size; + void push2buffer(uint8_t * sbuf, size_t len); + void flushbuffer(); +}; + +extern Telnet_Server telnet_server; + +#endif + diff --git a/esp3d/src/modules/websocket/websocket_server.cpp b/esp3d/src/modules/websocket/websocket_server.cpp new file mode 100644 index 00000000..af6f2205 --- /dev/null +++ b/esp3d/src/modules/websocket/websocket_server.cpp @@ -0,0 +1,236 @@ +/* + websocket_server.cpp - websocket functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#include "../../include/esp3d_config.h" + +#if defined (HTTP_FEATURE) || defined(WS_DATA_FEATURE) + + +#include "websocket_server.h" +#include +#include "../../core/settings_esp3d.h" + +WebSocket_Server websocket_terminal_server; + +void handle_Websocket_Terminal_Event(uint8_t num, uint8_t type, uint8_t * payload, size_t length) +{ + + switch(type) { + case WStype_DISCONNECTED: + //Serial.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + String s = "currentID:" + String(num); + // send message to client + websocket_terminal_server.set_currentID(num); + websocket_terminal_server.Socket_Server()->sendTXT(num, s); + s = "activeID:" + String(num); + websocket_terminal_server.Socket_Server()->broadcastTXT(s); + } + break; + case WStype_TEXT: + //USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + //USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + //hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + default: + break; + } + +} + +WebSocket_Server::WebSocket_Server() +{ + _websocket_server = nullptr; + _started = false; + _port = 0; + _current_id = 0; + +} +WebSocket_Server::~WebSocket_Server() +{ + end(); +} +bool WebSocket_Server::begin(uint16_t port) +{ + end(); + if(port == 0) { + _port = Settings_ESP3D::read_uint32(ESP_HTTP_PORT) +1; + } else { + _port = port; + } + _websocket_server = new WebSocketsServer(_port); + if (_websocket_server) { + _websocket_server->begin(); +#if defined (HTTP_FEATURE) //terminal websocket for HTTP + if(port == 0) { + _websocket_server->onEvent(handle_Websocket_Terminal_Event); + } +#endif //HTTP_FEATURE + _started = true; + } else { + end(); + } + return _started; +} + +void WebSocket_Server::end() +{ + _current_id = 0; + _TXbufferSize = 0; + //_RXbufferSize = 0; + //_RXbufferpos = 0; + if (_websocket_server) { + _websocket_server->close(); + delete _websocket_server; + _websocket_server = nullptr; + _port = 0; + } + _started = false; +} + + +WebSocket_Server::operator bool() const +{ + return _started; +} + +void WebSocket_Server::set_currentID(uint8_t current_id) +{ + _current_id = current_id; +} +uint8_t WebSocket_Server::get_currentID() +{ + return _current_id; +} +WebSocketsServer * WebSocket_Server::Socket_Server() +{ + return _websocket_server; +} + + +/*int WebSocket_Server::available(){ + return _RXbufferSize; +}*/ + + +size_t WebSocket_Server::write(uint8_t c) +{ + return write(&c,1);; +} + +size_t WebSocket_Server::write(const uint8_t *buffer, size_t size) +{ + if((buffer == NULL) ||(!_websocket_server) || (size == 0)) { + log_esp3d("%s %d",_websocket_server?"[SOCKET]No socket":"[SOCKET]No buffer", size); + return 0; + } + if (_TXbufferSize==0) { + _lastflush = millis(); + } + //send full line + if (_TXbufferSize + size > TXBUFFERSIZE) { + flush(); + } + //need periodic check to force to flush in case of no end + for (int i = 0; i < size; i++) { + _TXbuffer[_TXbufferSize] = buffer[i]; + _TXbufferSize++; + } + log_esp3d("[SOCKET]buffer size %d",_TXbufferSize); + return size; +} + +/*int WebSocket_Server::peek(void){ + if (_RXbufferSize > 0)return _RXbuffer[_RXbufferpos]; + else return -1; +}*/ + +/*bool WebSocket_Server::push (const char * data){ + int data_size = strlen(data); + if ((data_size + _RXbufferSize) <= RXBUFFERSIZE){ + int current = _RXbufferpos + _RXbufferSize; + if (current > RXBUFFERSIZE) current = current - RXBUFFERSIZE; + for (int i = 0; i < data_size; i++){ + if (current > (RXBUFFERSIZE-1)) current = 0; + _RXbuffer[current] = data[i]; + current ++; + } + _RXbufferSize+=strlen(data); + return true; + } + return false; +}*/ + +/*int WebSocket_Server::read(void){ + if (_RXbufferSize > 0) { + int v = _RXbuffer[_RXbufferpos]; + _RXbufferpos++; + if (_RXbufferpos > (RXBUFFERSIZE-1))_RXbufferpos = 0; + _RXbufferSize--; + return v; + } else return -1; +}*/ + +void WebSocket_Server::handle() +{ + if (_TXbufferSize > 0) { + if ((_TXbufferSize>=TXBUFFERSIZE) || ((millis()- _lastflush) > FLUSHTIMEOUT)) { + log_esp3d("[SOCKET]need flush, buffer size %d",_TXbufferSize); + flush(); + } + } + if (_websocket_server) { + _websocket_server->loop(); + } +} + + +void WebSocket_Server::flush(void) +{ + if (_TXbufferSize > 0) { + log_esp3d("[SOCKET]flush data, buffer size %d",_TXbufferSize); + if (_websocket_server) { + _websocket_server->broadcastBIN + (_TXbuffer,_TXbufferSize); + } + //refresh timout + _lastflush = millis(); + //reset buffer + _TXbufferSize = 0; + } +} + + + +#endif // HTTP_FEATURE || WS_DATA_FEATURE + diff --git a/esp3d/src/modules/websocket/websocket_server.h b/esp3d/src/modules/websocket/websocket_server.h new file mode 100644 index 00000000..327e73fd --- /dev/null +++ b/esp3d/src/modules/websocket/websocket_server.h @@ -0,0 +1,88 @@ +/* + websocket_server.h - websocket functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef _WEBSOCKET_SERVER_H_ +#define _WEBSOCKET_SERVER_H_ + +#include "Print.h" +#define TXBUFFERSIZE 1200 +//#define RXBUFFERSIZE 128 +#define FLUSHTIMEOUT 500 +class WebSocketsServer; +class WebSocket_Server: public Print +{ +public: + WebSocket_Server(); + ~WebSocket_Server(); + size_t write(uint8_t c); + size_t write(const uint8_t *buffer, size_t size); + + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + bool begin(uint16_t port=0); + uint16_t port() + { + return _port; + } + void end(); + //int available(); + //int peek(void); + //int read(void); + //bool push (const char * data); + void flush(void); + void handle(); + operator bool() const; + void set_currentID(uint8_t current_id); + uint8_t get_currentID(); + WebSocketsServer * Socket_Server(); +private: + bool _started; + uint16_t _port; + uint32_t _lastflush; + WebSocketsServer * _websocket_server; + uint8_t _TXbuffer[TXBUFFERSIZE]; + uint16_t _TXbufferSize; + uint8_t _current_id; + //uint8_t _RXbuffer[RXBUFFERSIZE]; + //uint16_t _RXbufferSize; + //uint16_t _RXbufferpos; +}; + +extern WebSocket_Server websocket_terminal_server; +#endif //_WEBSOCKET_SERVER_H_ diff --git a/esp3d/src/modules/wifi/wificonfig.cpp b/esp3d/src/modules/wifi/wificonfig.cpp new file mode 100644 index 00000000..921a2857 --- /dev/null +++ b/esp3d/src/modules/wifi/wificonfig.cpp @@ -0,0 +1,531 @@ +/* + wificonfig.cpp - wifi functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" +#if defined (WIFI_FEATURE) +#ifdef ARDUINO_ARCH_ESP32 +#include +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#endif //ARDUINO_ARCH_ESP8266 +#include "../wifi/wificonfig.h" +#include "../network/netconfig.h" +#include "../../core/esp3doutput.h" +#include "../../core/settings_esp3d.h" + +const uint8_t DEFAULT_AP_MASK_VALUE[] = {255, 255, 255, 0}; + +WiFiConfig::WiFiConfig() +{ +} + +WiFiConfig::~WiFiConfig() +{ + end(); +} + +/** + * Check if SSID string is valid + */ +bool WiFiConfig::isSSIDValid (const char * ssid) +{ + //limited size + //char c; + if (strlen (ssid) > MAX_SSID_LENGTH || strlen (ssid) < MIN_SSID_LENGTH) { + return false; + } + //only printable + for (uint i = 0; i < strlen (ssid); i++) { + if (!isPrintable (ssid[i]) ) { + return false; + } + } + return true; +} + +const char * WiFiConfig::hostname() +{ + static String tmp; +#if defined (ARDUINO_ARCH_ESP8266) + if (WiFi.getMode() == WIFI_AP) { + //No API for AP + tmp = NetConfig::hostname(true); + } else { + tmp = WiFi.hostname(); + + } +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + if (WiFi.getMode() == WIFI_AP) { + //tmp = NetConfig::hostname(true); + //Set API is not working so far + tmp = WiFi.softAPgetHostname(); + } else { + tmp = WiFi.getHostname(); + } +#endif //ARDUINO_ARCH_ESP8266 + return tmp.c_str(); +} + +/** + * Check if password string is valid + */ + +bool WiFiConfig::isPasswordValid (const char * password) +{ + if (strlen (password) == 0) { + return true; //open network + } + //limited size + if ((strlen (password) > MAX_PASSWORD_LENGTH) || (strlen (password) < MIN_PASSWORD_LENGTH)) { + return false; + } + return true; +} + +/* + * Get WiFi signal strength + */ + +int32_t WiFiConfig::getSignal (int32_t RSSI) +{ + if (RSSI <= -100) { + return 0; + } + if (RSSI >= -50) { + return 100; + } + return (2 * (RSSI + 100) ); +} + +/* + * Connect client to AP + */ + +bool WiFiConfig::ConnectSTA2AP() +{ + String msg, msg_out; + uint8_t count = 0; + uint8_t dot = 0; + wl_status_t status = WiFi.status(); + ESP3DOutput output(ESP_ALL_CLIENTS); + while (status != WL_CONNECTED && count < 40) { + + switch (status) { + case WL_NO_SSID_AVAIL: + msg="No SSID"; + break; + case WL_CONNECT_FAILED: + msg="Connection failed"; + break; + case WL_CONNECTED: + break; + default: + if ((dot>3) || (dot==0) ) { + dot=0; + msg_out = "Connecting"; + } + msg_out+="."; + msg= msg_out; + dot++; + break; + } + output.printMSG(msg.c_str()); + output.flush(); + Hal::wait (500); + count++; + status = WiFi.status(); + } + return (status == WL_CONNECTED); +} + +/* + * Start client mode (Station) + */ +bool WiFiConfig::StartSTA() +{ + //Sanity check + if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.disconnect(); + } + if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.softAPdisconnect(); + } + WiFi.enableAP (false); + WiFi.mode(WIFI_STA); + //Get parameters for STA + String SSID = Settings_ESP3D::read_string(ESP_STA_SSID); + String password = Settings_ESP3D::read_string(ESP_STA_PASSWORD); + + if (Settings_ESP3D::read_byte(ESP_STA_IP_MODE) != DHCP_MODE) { + int32_t IP = Settings_ESP3D::read_IP(ESP_STA_IP_VALUE); + int32_t GW = Settings_ESP3D::read_IP(ESP_STA_GATEWAY_VALUE); + int32_t MK = Settings_ESP3D::read_IP(ESP_STA_MASK_VALUE); + IPAddress ip(IP), mask(MK), gateway(GW); + WiFi.config(ip, gateway,mask); + } + ESP3DOutput output(ESP_ALL_CLIENTS); + String stmp; + stmp = "Connecting to '" + SSID + "'";; + output.printMSG(stmp.c_str()); + if (WiFi.begin(SSID.c_str(), (password.length() > 0)?password.c_str():nullptr)) { +#if defined (ARDUINO_ARCH_ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + if (!WiFi.hostname(NetConfig::hostname(true))) { + output.printERROR("Set hostname STA failed"); + } +#endif //ARDUINO_ARCH_ESP8266 +#if defined (ARDUINO_ARCH_ESP32) + WiFi.setSleep(false); + WiFi.setHostname(NetConfig::hostname(true)); +#endif //ARDUINO_ARCH_ESP32 + return ConnectSTA2AP(); + } else { + output.printERROR("Starting client failed"); + return false; + } +} + +/** + * Setup and start Access point + */ + +bool WiFiConfig::StartAP() +{ + ESP3DOutput output(ESP_ALL_CLIENTS); + //Sanity check + if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.disconnect(); + } + if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.softAPdisconnect(); + } + WiFi.enableSTA (false); + WiFi.mode(WIFI_AP); + //Set Sleep Mode to none +#if defined (ARDUINO_ARCH_ESP8266) + WiFi.setSleepMode(WIFI_NONE_SLEEP); + //No API for it + //if(!WiFi.hostname(NetConfig::hostname(true))){ + // output.printERROR("Set hostname AP failed"); + //} +#endif //ARDUINO_ARCH_ESP8266 + + String SSID = Settings_ESP3D::read_string(ESP_AP_SSID); + String password = Settings_ESP3D::read_string(ESP_AP_PASSWORD); + //channel + int8_t channel = Settings_ESP3D::read_byte (ESP_AP_CHANNEL); + //IP + int32_t IP = Settings_ESP3D::read_IP(ESP_AP_IP_VALUE); + IPAddress ip(IP); + IPAddress mask(DEFAULT_AP_MASK_VALUE); + //Set static IP + + if (!WiFi.softAPConfig(ip, ip, mask)) { + output.printERROR("Set IP to AP failed"); + } else { + output.printMSG(ip.toString().c_str()); + } + //Start AP + if(WiFi.softAP(SSID.c_str(), (password.length() > 0)?password.c_str():nullptr, channel)) { + String stmp = "AP SSID: '" + SSID; + if (password.length() > 0) { + stmp +="' is started and protected by password"; + } else { + stmp +=" is started not protected by passord"; + } + output.printMSG(stmp.c_str()); + //must be done after starting AP not before +#if defined (ARDUINO_ARCH_ESP32) + WiFi.setSleep(false); + if (!WiFi.softAPsetHostname(NetConfig::hostname(true))) { + output.printERROR("Set hostname AP failed"); + } +#endif //ARDUINO_ARCH_ESP32 + return true; + } else { + output.printERROR("Starting AP failed"); + return false; + } +} + +bool WiFiConfig::started() +{ + return (WiFi.getMode() != WIFI_OFF); +} + +/** + * begin WiFi setup + */ +bool WiFiConfig::begin() +{ + bool res = false; + end(); + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printMSG("Starting WiFi"); + int8_t wifiMode =Settings_ESP3D::read_byte(ESP_RADIO_MODE); + if (wifiMode == ESP_WIFI_AP) { + res = StartAP(); + } else if (wifiMode == ESP_WIFI_STA) { + res = StartSTA(); + //AP is backup mode + if(!res) { + res = StartAP(); + } + } + //prepare the scan + //WiFi.scanNetworks (true); + return res; +} + +/** + * End WiFi + */ + +void WiFiConfig::end() +{ + //Sanity check + if((WiFi.getMode() == WIFI_STA) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.disconnect(true); + } + if((WiFi.getMode() == WIFI_AP) || (WiFi.getMode() == WIFI_AP_STA)) { + WiFi.softAPdisconnect(true); + } + WiFi.mode(WIFI_OFF); + ESP3DOutput output(ESP_ALL_CLIENTS); + output.printMSG("WiFi Off"); +} + +/** + * Handle not critical actions that must be done in sync environement + */ + +void WiFiConfig::handle() +{ +} + +const char* WiFiConfig::getSleepModeString () +{ +#ifdef ARDUINO_ARCH_ESP32 + if (WiFi.getSleep()) { + return "Modem"; + } else { + return "None"; + } +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + WiFiSleepType_t ps_type = WiFi.getSleepMode(); + if (ps_type == WIFI_NONE_SLEEP) { + return "None"; + } else if (ps_type == WIFI_LIGHT_SLEEP) { + return "Light"; + } else if (ps_type == WIFI_MODEM_SLEEP) { + return "Modem"; + } else { + return "???"; + } +#endif //ARDUINO_ARCH_ESP8266 +} + +const char* WiFiConfig::getPHYModeString (uint8_t wifimode) +{ +#ifdef ARDUINO_ARCH_ESP32 + uint8_t PhyMode; + esp_wifi_get_protocol ((wifimode == WIFI_STA)?ESP_IF_WIFI_STA:ESP_IF_WIFI_AP, &PhyMode); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + (void)wifimode; + WiFiPhyMode_t PhyMode = WiFi.getPhyMode(); +#endif //ARDUINO_ARCH_ESP8266 + if (PhyMode == (WIFI_PHY_MODE_11G) ) { + return "11g"; + } else if (PhyMode == (WIFI_PHY_MODE_11B) ) { + return "11b"; + } else if (PhyMode == (WIFI_PHY_MODE_11N) ) { + return "11n"; + } else { + return "???"; + } +} + +bool WiFiConfig::is_AP_visible() +{ +#ifdef ARDUINO_ARCH_ESP32 + wifi_config_t conf; + esp_wifi_get_config (ESP_IF_WIFI_AP, &conf); + return (conf.ap.ssid_hidden == 0); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + struct softap_config apconfig; + wifi_softap_get_config (&apconfig); + return (apconfig.ssid_hidden == 0); +#endif //ARDUINO_ARCH_ESP8266 +} + +const char * WiFiConfig::AP_SSID() +{ + static String ssid; +#ifdef ARDUINO_ARCH_ESP32 + wifi_config_t conf; + esp_wifi_get_config (ESP_IF_WIFI_AP, &conf); + ssid = (const char*) conf.ap.ssid; +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + struct softap_config apconfig; + wifi_softap_get_config (&apconfig); + ssid = (const char*) apconfig.ssid; +#endif //ARDUINO_ARCH_ESP8266 + return ssid.c_str(); +} + +const char * WiFiConfig::AP_Auth_String() +{ + uint8_t mode = 0; +#ifdef ARDUINO_ARCH_ESP32 + wifi_config_t conf; + esp_wifi_get_config (ESP_IF_WIFI_AP, &conf); + mode = conf.ap.authmode; + +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + struct softap_config apconfig; + wifi_softap_get_config (&apconfig); + mode = apconfig.authmode; +#endif //ARDUINO_ARCH_ESP8266 + if (mode == AUTH_OPEN) { + return "None"; + } else if (mode == AUTH_WEP) { + return "WEP"; + } else if (mode == AUTH_WPA_PSK) { + return "WPA"; + } else if (mode == AUTH_WPA2_PSK) { + return "WPA2"; + } else { + return "WPA/WPA2"; + } +} + +const char * WiFiConfig::AP_Gateway_String() +{ + static String tmp; +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_ip_info_t ip_AP; + tcpip_adapter_get_ip_info (TCPIP_ADAPTER_IF_AP, &ip_AP); + tmp = IPAddress (ip_AP.gw.addr).toString(); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + struct ip_info ip_AP; + if (!wifi_get_ip_info (SOFTAP_IF, &ip_AP)) { + log_esp3d("Error getting gateway ip"); + } + tmp = IPAddress (ip_AP.gw).toString(); +#endif //ARDUINO_ARCH_ESP8266 + return tmp.c_str(); +} + +const char * WiFiConfig::AP_Mask_String() +{ + static String tmp; +#ifdef ARDUINO_ARCH_ESP32 + tcpip_adapter_ip_info_t ip_AP; + tcpip_adapter_get_ip_info (TCPIP_ADAPTER_IF_AP, &ip_AP); + tmp = IPAddress (ip_AP.netmask.addr).toString(); +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + struct ip_info ip_AP; + if (!wifi_get_ip_info (SOFTAP_IF, &ip_AP)) { + log_esp3d("Error getting mask ip"); + } + tmp = IPAddress (ip_AP.netmask).toString(); +#endif //ARDUINO_ARCH_ESP8266 + return tmp.c_str(); +} + +const char * WiFiConfig::getConnectedSTA(uint8_t * totalcount, bool reset) +{ + static uint8_t count = 0; + static uint8_t current = 0; + static String data; + data = ""; + +#ifdef ARDUINO_ARCH_ESP32 + if (current > count) { + current =0; + } + static wifi_sta_list_t station; + static tcpip_adapter_sta_list_t tcpip_sta_list; + if (reset) { + count = 0; + } + if (count == 0) { + current = 0; + esp_wifi_ap_get_sta_list (&station); + tcpip_adapter_get_sta_list (&station, &tcpip_sta_list); + count = station.num; + } + if (count > 0) { + data = IPAddress (tcpip_sta_list.sta[current].ip.addr).toString(); + data += "("; + data += NetConfig::mac2str(tcpip_sta_list.sta[current].mac); + data += ")"; + current++; + } + +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 + static struct station_info * station; + if (current > count) { + current = 0; + count = 0; + } + if (reset) { + count = 0; + } + if (count == 0) { + current = 0; + station = wifi_softap_get_station_info(); + struct station_info * station_tmp = station; + while (station) { + //go next record + count++; + station = STAILQ_NEXT (station, next); + } + station = station_tmp; + } + if ((count > 0) && station) { + data = IPAddress ((const uint8_t *) &station->ip).toString(); + data += "("; + data += NetConfig::mac2str(station->bssid); + data += ")"; + current++; + station = STAILQ_NEXT (station, next); + if ((current == count) || !station) { + wifi_softap_free_station_info(); + } + } +#endif //ARDUINO_ARCH_ESP8266 + if(totalcount) { + *totalcount = count; + } + return data.c_str(); +} + +#endif // WIFI_FEATURE + diff --git a/esp3d/src/modules/wifi/wificonfig.h b/esp3d/src/modules/wifi/wificonfig.h new file mode 100644 index 00000000..d2b2ba0b --- /dev/null +++ b/esp3d/src/modules/wifi/wificonfig.h @@ -0,0 +1,81 @@ +/* + wificonfig.h - wifi functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//boundaries +#define MAX_SSID_LENGTH 32 +#define MIN_SSID_LENGTH 1 +#define MIN_CHANNEL 1 +#define MAX_CHANNEL 14 +#define MAX_PASSWORD_LENGTH 64 +//min size of password is 0 or upper than 8 char +//0 is special case so let's put 8 +#define MIN_PASSWORD_LENGTH 8 +#ifdef ARDUINO_ARCH_ESP32 +#include +#define WIFI_NONE_SLEEP WIFI_PS_NONE +#define WIFI_LIGHT_SLEEP WIFI_PS_MIN_MODEM +#define WIFI_MODEM_SLEEP WIFI_PS_MAX_MODEM +#define WIFI_PHY_MODE_11B WIFI_PROTOCOL_11B +#define WIFI_PHY_MODE_11G WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G +#define WIFI_PHY_MODE_11N WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N +#define AUTH_OPEN WIFI_AUTH_OPEN +#define AUTH_WEP WIFI_AUTH_WEP +#define AUTH_WPA_PSK WIFI_AUTH_WPA_PSK +#define AUTH_WPA2_PSK WIFI_AUTH_WPA2_PSK +#define AUTH_WPA_WPA2_PSK WIFI_AUTH_WPA_WPA2_PSK +#define ENC_TYPE_NONE AUTH_OPEN +#define WiFiMode_t wifi_mode_t +#endif //ARDUINO_ARCH_ESP32 +#ifdef ARDUINO_ARCH_ESP8266 +#include +#endif //ARDUINO_ARCH_ESP8266 + +#ifndef _WIFI_CONFIG_H +#define _WIFI_CONFIG_H + +class WiFiConfig +{ +public: + WiFiConfig(); + ~WiFiConfig(); + static bool isPasswordValid (const char * password); + static bool isSSIDValid (const char * ssid); + static bool StartAP(); + static bool StartSTA(); + static void StopWiFi(); + static int32_t getSignal (int32_t RSSI); + static const char* getSleepModeString (); + static const char* getPHYModeString (uint8_t wifimode); + static bool is_AP_visible(); + static const char * AP_SSID(); + static const char * hostname(); + static const char * AP_Auth_String(); + static const char * AP_Gateway_String(); + static const char * AP_Mask_String(); + static const char* getConnectedSTA(uint8_t * totalcount = NULL, bool reset = false); + static bool started(); + static bool begin(); + static void end(); + static void handle(); +private : + static bool ConnectSTA2AP(); +}; + +#endif //_WIFI_CONFIG_H diff --git a/libraries/ESP32SSDP/ESP32SSDP.cpp b/libraries/ESP32SSDP/ESP32SSDP.cpp new file mode 100644 index 00000000..e862b146 --- /dev/null +++ b/libraries/ESP32SSDP/ESP32SSDP.cpp @@ -0,0 +1,477 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include +#include "ESP32SSDP.h" +#include "WiFiUdp.h" +#include + +//#define DEBUG_SSDP Serial + +#define SSDP_INTERVAL 1200 +#define SSDP_PORT 1900 +#define SSDP_METHOD_SIZE 10 +#define SSDP_URI_SIZE 2 +#define SSDP_BUFFER_SIZE 64 +#define SSDP_MULTICAST_TTL 2 +static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250); + + + +static const char _ssdp_response_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "EXT:\r\n"; + +static const char _ssdp_notify_template[] PROGMEM = + "NOTIFY * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "NTS: ssdp:alive\r\n"; + +static const char _ssdp_packet_template[] PROGMEM = + "%s" // _ssdp_response_template / _ssdp_notify_template + "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL + "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber + "USN: uuid:%s\r\n" // _uuid + "%s: %s\r\n" // "NT" or "ST", _deviceType + "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL + "\r\n"; + +static const char _ssdp_schema_template[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/xml\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "\r\n" + "" + "" + "" + "1" + "0" + "" + "http://%u.%u.%u.%u:%u/" // WiFi.localIP(), _port + "" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "uuid:%s" + "" +// "" +// "" +// "image/png" +// "48" +// "48" +// "24" +// "icon48.png" +// "" +// "" +// "image/png" +// "120" +// "120" +// "24" +// "icon120.png" +// "" +// "" + "\r\n" + "\r\n"; + +struct SSDPTimer { + ETSTimer timer; +}; + +SSDPClass::SSDPClass() : +_server(0), +_timer(0), +_port(80), +_ttl(SSDP_MULTICAST_TTL), +_respondToPort(0), +_pending(false), +_delay(0), +_process_time(0), +_notify_time(0) +{ + _uuid[0] = '\0'; + _modelNumber[0] = '\0'; + sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1"); + _friendlyName[0] = '\0'; + _presentationURL[0] = '\0'; + _serialNumber[0] = '\0'; + _modelName[0] = '\0'; + _modelURL[0] = '\0'; + _manufacturer[0] = '\0'; + _manufacturerURL[0] = '\0'; + sprintf(_schemaURL, "ssdp/schema.xml"); +} + +SSDPClass::~SSDPClass(){ + end(); +} + +void SSDPClass::end(){ + if(!_server) { + return; + } +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf_P(PSTR("SSDP end ... ")); +#endif + // undo all initializations done in begin(), in reverse order + _stopTimer(); + _server->stop(); + delete (_server); + _server = 0; +} + +IPAddress SSDPClass::localIP(){ + tcpip_adapter_ip_info_t ip; + if (WiFi.getMode() == WIFI_STA) { + if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip)) { + return IPAddress(); + } + } else if (WiFi.getMode() == WIFI_OFF) { + if (tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip)) { + return IPAddress(); + } + } + return IPAddress(ip.ip.addr); +} + +bool SSDPClass::begin(){ + _pending = false; + end(); + uint32_t chipId = ((uint16_t) (ESP.getEfuseMac() >> 32)); + sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", + (uint16_t) ((chipId >> 16) & 0xff), + (uint16_t) ((chipId >> 8) & 0xff), + (uint16_t) chipId & 0xff ); + assert(nullptr == _server); + _server = new WiFiUDP; +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid); +#endif + + + _server = new WiFiUDP; + if (!(_server->beginMulticast(IPAddress(SSDP_MULTICAST_ADDR), SSDP_PORT))) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Error begin"); +#endif + return false; + } + + _startTimer(); + + return true; +} + +void SSDPClass::_send(ssdp_method_t method){ + char buffer[1460]; + IPAddress ip = localIP(); + + char valueBuffer[strlen_P(_ssdp_notify_template)+1]; + strcpy_P(valueBuffer, (method == NONE)?_ssdp_response_template:_ssdp_notify_template); + + int len = snprintf_P(buffer, sizeof(buffer), + _ssdp_packet_template, + valueBuffer, + SSDP_INTERVAL, + _modelName, _modelNumber, + _uuid, + (method == NONE)?"ST":"NT", + _deviceType, + ip[0], ip[1], ip[2], ip[3], _port, _schemaURL + ); + if(len < 0) return; + IPAddress remoteAddr; + uint16_t remotePort; + if(method == NONE) { + remoteAddr = _respondToAddr; + remotePort = _respondToPort; +#ifdef DEBUG_SSDP + DEBUG_SSDP.print("Sending Response to "); +#endif + } else { + remoteAddr = IPAddress(SSDP_MULTICAST_ADDR); + remotePort = SSDP_PORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Sending Notify to "); +#endif + } +#ifdef DEBUG_SSDP + DEBUG_SSDP.print(remoteAddr); + DEBUG_SSDP.print(":"); + DEBUG_SSDP.println(remotePort); +#endif + _server->beginPacket(remoteAddr, remotePort); + _server->println(buffer); + _server->endPacket(); +} + +void SSDPClass::schema(WiFiClient client){ + IPAddress ip = localIP(); + char buffer[strlen_P(_ssdp_schema_template)+1]; + strcpy_P(buffer, _ssdp_schema_template); + client.printf(buffer, + ip[0], ip[1], ip[2], ip[3], _port, + _deviceType, + _friendlyName, + _presentationURL, + _serialNumber, + _modelName, + _modelNumber, + _modelURL, + _manufacturer, + _manufacturerURL, + _uuid + ); +} + +void SSDPClass::_update(){ + int nbBytes =0; + char * packetBuffer = nullptr; + + if(!_pending && _server) { + ssdp_method_t method = NONE; + nbBytes= _server->parsePacket(); + typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states; + states state = METHOD; + typedef enum {START, MAN, ST, MX} headers; + headers header = START; + + uint8_t cursor = 0; + uint8_t cr = 0; + + char buffer[SSDP_BUFFER_SIZE] = {0}; + packetBuffer = new char[nbBytes +1]; + int message_size=_server->read(packetBuffer,nbBytes); + int process_pos = 0; + packetBuffer[message_size]='\0'; + _respondToAddr = _server->remoteIP(); + _respondToPort = _server->remotePort(); +#ifdef DEBUG_SSDP + if (message_size) { + DEBUG_SSDP.println("****************************************************"); + DEBUG_SSDP.println(_server->remoteIP()); + DEBUG_SSDP.println(packetBuffer); + DEBUG_SSDP.println("****************************************************"); + } +#endif + while(process_pos < message_size){ + + char c = packetBuffer[process_pos]; + process_pos++; + (c == '\r' || c == '\n') ? cr++ : cr = 0; +#ifdef DEBUG_SSDP + if ((c == '\r' || c == '\n') && (cr < 2)) DEBUG_SSDP.println(buffer); +#endif + switch(state){ + case METHOD: + if(c == ' '){ + if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH; + + if(method == NONE) state = ABORT; + else state = URI; + cursor = 0; + + } else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case URI: + if(c == ' '){ + if(strcmp(buffer, "*")) state = ABORT; + else state = PROTO; + cursor = 0; + } else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case PROTO: + if(cr == 2){ state = KEY; cursor = 0; } + break; + case KEY: + if(cr == 4){ _pending = true; _process_time = millis(); } + else if(c == ' '){ cursor = 0; state = VALUE; } + else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + break; + case VALUE: + if(cr == 2){ + switch(header){ + case START: + break; + case MAN: +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer); +#endif + break; + case ST: + if(strcmp(buffer, "ssdp:all")){ + state = ABORT; +#ifdef DEBUG_SSDP + DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer); +#endif + } + // if the search type matches our type, we should respond instead of ABORT + if(strcasecmp(buffer, _deviceType) == 0){ + _pending = true; + _process_time = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("the search type matches our type"); +#endif + state = KEY; + } + break; + case MX: + _delay = random(0, atoi(buffer)) * 1000L; + break; + } + + if(state != ABORT){ state = KEY; header = START; cursor = 0; } + } else if(c != '\r' && c != '\n'){ + if(header == START){ + if(strncmp(buffer, "MA", 2) == 0) header = MAN; + else if(strcmp(buffer, "ST") == 0) header = ST; + else if(strcmp(buffer, "MX") == 0) header = MX; + } + + if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; } + } + break; + case ABORT: + _pending = false; _delay = 0; + break; + } + } + } + if(packetBuffer) delete packetBuffer; + if(_pending && (millis() - _process_time) > _delay){ + _pending = false; _delay = 0; +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send None"); +#endif + _send(NONE); + } else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){ + _notify_time = millis(); + #ifdef DEBUG_SSDP + DEBUG_SSDP.println("Send Notify"); +#endif + _send(NOTIFY); + } else { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Do not sent"); +#endif + } + + if (_pending) { + _server->flush(); + } + +} + +void SSDPClass::setSchemaURL(const char *url){ + strlcpy(_schemaURL, url, sizeof(_schemaURL)); +} + +void SSDPClass::setHTTPPort(uint16_t port){ + _port = port; +} + +void SSDPClass::setDeviceType(const char *deviceType){ + strlcpy(_deviceType, deviceType, sizeof(_deviceType)); +} + +void SSDPClass::setName(const char *name){ + strlcpy(_friendlyName, name, sizeof(_friendlyName)); +} + +void SSDPClass::setURL(const char *url){ + strlcpy(_presentationURL, url, sizeof(_presentationURL)); +} + +void SSDPClass::setSerialNumber(const char *serialNumber){ + strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber)); +} + +void SSDPClass::setSerialNumber(const uint32_t serialNumber){ + snprintf(_serialNumber, sizeof(uint32_t)*2+1, "%08X", serialNumber); +} + +void SSDPClass::setModelName(const char *name){ + strlcpy(_modelName, name, sizeof(_modelName)); +} + +void SSDPClass::setModelNumber(const char *num){ + strlcpy(_modelNumber, num, sizeof(_modelNumber)); +} + +void SSDPClass::setModelURL(const char *url){ + strlcpy(_modelURL, url, sizeof(_modelURL)); +} + +void SSDPClass::setManufacturer(const char *name){ + strlcpy(_manufacturer, name, sizeof(_manufacturer)); +} + +void SSDPClass::setManufacturerURL(const char *url){ + strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL)); +} + +void SSDPClass::setTTL(const uint8_t ttl){ + _ttl = ttl; +} + +void SSDPClass::_onTimerStatic(SSDPClass* self) { +#ifdef DEBUG_SSDP + DEBUG_SSDP.println("Update"); +#endif + self->_update(); +} + +void SSDPClass::_startTimer() { + _stopTimer(); + _timer= new SSDPTimer(); + ETSTimer* tm = &(_timer->timer); + const int interval = 1000; + ets_timer_disarm(tm); + ets_timer_setfn(tm, reinterpret_cast(&SSDPClass::_onTimerStatic), reinterpret_cast(this)); + ets_timer_arm(tm, interval, 1 /* repeat */); +} + +void SSDPClass::_stopTimer() { + if(!_timer){ + return; + } + ETSTimer* tm = &(_timer->timer); + ets_timer_disarm(tm); + delete _timer; + _timer = nullptr; +} + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +SSDPClass SSDP; +#endif diff --git a/libraries/ESP32SSDP/ESP32SSDP.h b/libraries/ESP32SSDP/ESP32SSDP.h new file mode 100644 index 00000000..796c4fe1 --- /dev/null +++ b/libraries/ESP32SSDP/ESP32SSDP.h @@ -0,0 +1,128 @@ +/* +ESP32 Simple Service Discovery +Copyright (c) 2015 Hristo Gochkov + +Original (Arduino) version by Filippo Sallemi, July 23, 2014. +Can be found at: https://github.com/nomadnt/uSSDP + +License (MIT license): + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef ESP32SSDP_H +#define ESP32SSDP_H + +#include +#include +#include + +#define SSDP_UUID_SIZE 37 +#define SSDP_SCHEMA_URL_SIZE 64 +#define SSDP_DEVICE_TYPE_SIZE 64 +#define SSDP_FRIENDLY_NAME_SIZE 64 +#define SSDP_SERIAL_NUMBER_SIZE 32 +#define SSDP_PRESENTATION_URL_SIZE 128 +#define SSDP_MODEL_NAME_SIZE 64 +#define SSDP_MODEL_URL_SIZE 128 +#define SSDP_MODEL_VERSION_SIZE 32 +#define SSDP_MANUFACTURER_SIZE 64 +#define SSDP_MANUFACTURER_URL_SIZE 128 + +typedef enum { + NONE, + SEARCH, + NOTIFY +} ssdp_method_t; + + +struct SSDPTimer; + +class SSDPClass{ + public: + SSDPClass(); + ~SSDPClass(); + + bool begin(); + void end(); + + void schema(WiFiClient client); + + void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); } + void setDeviceType(const char *deviceType); + void setName(const String& name) { setName(name.c_str()); } + void setName(const char *name); + void setURL(const String& url) { setURL(url.c_str()); } + void setURL(const char *url); + void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); } + void setSchemaURL(const char *url); + void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); } + void setSerialNumber(const char *serialNumber); + void setSerialNumber(const uint32_t serialNumber); + void setModelName(const String& name) { setModelName(name.c_str()); } + void setModelName(const char *name); + void setModelNumber(const String& num) { setModelNumber(num.c_str()); } + void setModelNumber(const char *num); + void setModelURL(const String& url) { setModelURL(url.c_str()); } + void setModelURL(const char *url); + void setManufacturer(const String& name) { setManufacturer(name.c_str()); } + void setManufacturer(const char *name); + void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); } + void setManufacturerURL(const char *url); + void setHTTPPort(uint16_t port); + void setTTL(uint8_t ttl); + + protected: + void _send(ssdp_method_t method); + void _update(); + void _startTimer(); + void _stopTimer(); + static void _onTimerStatic(SSDPClass* self); + IPAddress localIP(); + WiFiUDP *_server; + SSDPTimer* _timer; + uint16_t _port; + uint8_t _ttl; + + IPAddress _respondToAddr; + uint16_t _respondToPort; + + bool _pending; + unsigned short _delay; + unsigned long _process_time; + unsigned long _notify_time; + + char _schemaURL[SSDP_SCHEMA_URL_SIZE]; + char _uuid[SSDP_UUID_SIZE]; + char _deviceType[SSDP_DEVICE_TYPE_SIZE]; + char _friendlyName[SSDP_FRIENDLY_NAME_SIZE]; + char _serialNumber[SSDP_SERIAL_NUMBER_SIZE]; + char _presentationURL[SSDP_PRESENTATION_URL_SIZE]; + char _manufacturer[SSDP_MANUFACTURER_SIZE]; + char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE]; + char _modelName[SSDP_MODEL_NAME_SIZE]; + char _modelURL[SSDP_MODEL_URL_SIZE]; + char _modelNumber[SSDP_MODEL_VERSION_SIZE]; +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP) +extern SSDPClass SSDP; +#endif + +#endif diff --git a/libraries/ESP32SSDP/README.rst b/libraries/ESP32SSDP/README.rst new file mode 100644 index 00000000..b2c92c75 --- /dev/null +++ b/libraries/ESP32SSDP/README.rst @@ -0,0 +1,22 @@ +ESP32 Simple Service Discovery Copyright (c) 2015 Hristo Gochkov +Original (Arduino) version by Filippo Sallemi, July 23, 2014. Can be +found at: https://github.com/nomadnt/uSSDP + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libraries/ESP32SSDP/examples/SSDP/SSDP.ino b/libraries/ESP32SSDP/examples/SSDP/SSDP.ino new file mode 100644 index 00000000..8014a3a5 --- /dev/null +++ b/libraries/ESP32SSDP/examples/SSDP/SSDP.ino @@ -0,0 +1,51 @@ +#include +#include +#include + +const char* ssid = "********"; +const char* password = "********"; + +WebServer HTTP(80); + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println("Starting WiFi..."); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if(WiFi.waitForConnectResult() == WL_CONNECTED){ + + Serial.printf("Starting HTTP...\n"); + HTTP.on("/index.html", HTTP_GET, [](){ + HTTP.send(200, "text/plain", "Hello World!"); + }); + HTTP.on("/description.xml", HTTP_GET, [](){ + SSDP.schema(HTTP.client()); + }); + HTTP.begin(); + + Serial.printf("Starting SSDP...\n"); + SSDP.setSchemaURL("description.xml"); + SSDP.setHTTPPort(80); + SSDP.setName("Philips hue clone"); + SSDP.setSerialNumber("001788102201"); + SSDP.setURL("index.html"); + SSDP.setModelName("Philips hue bridge 2012"); + SSDP.setModelNumber("929000226503"); + SSDP.setModelURL("http://www.meethue.com"); + SSDP.setManufacturer("Royal Philips Electronics"); + SSDP.setManufacturerURL("http://www.philips.com"); + SSDP.begin(); + + Serial.printf("Ready!\n"); + } else { + Serial.printf("WiFi Failed\n"); + while(1) delay(100); + } +} + +void loop() { + HTTP.handleClient(); + delay(1); +} diff --git a/libraries/ESP32SSDP/keywords.txt b/libraries/ESP32SSDP/keywords.txt new file mode 100644 index 00000000..241d3414 --- /dev/null +++ b/libraries/ESP32SSDP/keywords.txt @@ -0,0 +1,53 @@ +####################################### +# Syntax Coloring Map For Ultrasound +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266SSDP KEYWORD1 +SSDP KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +schema KEYWORD2 +setName KEYWORD2 +setURL KEYWORD2 +setHTTPPort KEYWORD2 +setSchemaURL KEYWORD2 +setSerialNumber KEYWORD2 +setModelName KEYWORD2 +setModelNumber KEYWORD2 +setModelURL KEYWORD2 +setManufacturer KEYWORD2 +setManufacturerURL KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +SSDP_INTERVAL LITERAL1 +SSDP_PORT LITERAL1 +SSDP_METHOD_SIZE LITERAL1 +SSDP_URI_SIZE LITERAL1 +SSDP_BUFFER_SIZE LITERAL1 +SSDP_BASE_SIZE LITERAL1 +SSDP_FRIENDLY_NAME_SIZE LITERAL1 +SSDP_SERIAL_NUMBER_SIZE LITERAL1 +SSDP_PRESENTATION_URL_SIZE LITERAL1 +SSDP_MODEL_NAME_SIZE LITERAL1 +SSDP_MODEL_URL_SIZE LITERAL1 +SSDP_MODEL_VERSION_SIZE LITERAL1 +SSDP_MANUFACTURER_SIZE LITERAL1 +SSDP_MANUFACTURER_URL_SIZE LITERAL1 +SEARCH LITERAL1 +NOTIFY LITERAL1 +BASIC LITERAL1 +MANAGEABLE LITERAL1 +SOLARPROTECTIONBLIND LITERAL1 +DIGITALSECURITYCAMERA LITERAL1 +HVAC LITERAL1 +LIGHTINGCONTROL LITERAL1 diff --git a/libraries/ESP32SSDP/library.properties b/libraries/ESP32SSDP/library.properties new file mode 100644 index 00000000..e37cd254 --- /dev/null +++ b/libraries/ESP32SSDP/library.properties @@ -0,0 +1,9 @@ +name=ESP32SSPD +version=1.0 +author=Me-No-Dev +maintainer=Me-No-Dev +sentence=Simple SSDP library for ESP32 +paragraph=Only for ESP32 +category=Communication +url= +architectures=esp32 diff --git a/libraries/arduinoWebSockets/.gitignore b/libraries/arduinoWebSockets/.gitignore new file mode 100644 index 00000000..44b2c85f --- /dev/null +++ b/libraries/arduinoWebSockets/.gitignore @@ -0,0 +1,29 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +/tests/webSocketServer/node_modules diff --git a/libraries/arduinoWebSockets/.travis.yml b/libraries/arduinoWebSockets/.travis.yml new file mode 100644 index 00000000..14693dd8 --- /dev/null +++ b/libraries/arduinoWebSockets/.travis.yml @@ -0,0 +1,40 @@ +sudo: false +language: bash +os: + - linux +env: + matrix: + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80" IDE_VERSION=1.6.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,FlashSize=1M0,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5 + - CPU="esp8266" BOARD="esp8266com:esp8266:generic:CpuFrequency=80,Debug=Serial1" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.6.5 + - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5 + +script: + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz + - tar xf arduino-$IDE_VERSION-linux64.tar.xz + - mv arduino-$IDE_VERSION $HOME/arduino_ide + - export PATH="$HOME/arduino_ide:$PATH" + - which arduino + - mkdir -p $HOME/Arduino/libraries + - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets + - source $TRAVIS_BUILD_DIR/travis/common.sh + - get_core $CPU + - cd $TRAVIS_BUILD_DIR + - arduino --board $BOARD --save-prefs + - arduino --get-pref sketchbook.path + - build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU + +notifications: + email: + on_success: change + on_failure: change + webhooks: + urls: + - https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37 + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: false # default: false diff --git a/libraries/arduinoWebSockets/LICENSE b/libraries/arduinoWebSockets/LICENSE new file mode 100644 index 00000000..f166cc57 --- /dev/null +++ b/libraries/arduinoWebSockets/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/libraries/arduinoWebSockets/README.md b/libraries/arduinoWebSockets/README.md new file mode 100644 index 00000000..63eef3e2 --- /dev/null +++ b/libraries/arduinoWebSockets/README.md @@ -0,0 +1,98 @@ +WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets) +=========================================== + +a WebSocket Server and Client for Arduino based on RFC6455. + + +##### Supported features of RFC6455 ##### + - text frame + - binary frame + - connection close + - ping + - pong + - continuation frame + +##### Limitations ##### + - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define + - max output length has no limit (the hardware is the limit) + - Client send big frames with mask 0x00000000 (on AVR all frames) + - continuation frame reassembly need to be handled in the application code + + ##### Limitations for Async ##### + - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround. + - wss / SSL is not possible. + +##### Supported Hardware ##### + - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/) + - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32) + - ESP31B + - Particle with STM32 ARM Cortex M3 + - ATmega328 with Ethernet Shield (ATmega branch) + - ATmega328 with enc28j60 (ATmega branch) + - ATmega2560 with Ethernet Shield (ATmega branch) + - ATmega2560 with enc28j60 (ATmega branch) + +###### Note: ###### + + version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch. + + Arduino for AVR not supports std namespace of c++. + +### wss / SSL ### + supported for: + - wss client on the ESP8266 + - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets + by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a + sample Nginx server configuration file to enable this. + +### ESP Async TCP ### + +This libary can run in Async TCP mode on the ESP. + +The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define). + +[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required. + + +### High Level Client API ### + + - `begin` : Initiate connection sequence to the websocket host. +``` +void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); +void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + ``` + - `onEvent`: Callback to handle for websocket events + + ``` + void onEvent(WebSocketClientEvent cbEvent); + ``` + + - `WebSocketClientEvent`: Handler for websocket events + ``` + void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length) + ``` +Where `WStype_t type` is defined as: + ``` + typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, + } WStype_t; + ``` + +### Issues ### +Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues + +[![Join the chat at https://gitter.im/Links2004/arduinoWebSockets](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +### License and credits ### + +The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE) + +[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE). diff --git a/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf b/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf new file mode 100644 index 00000000..ec5aa89f --- /dev/null +++ b/libraries/arduinoWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf @@ -0,0 +1,83 @@ +# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0) + +# proxy cache location +proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off; + +# webserver proxy +server { + + # general server parameters + listen 50080; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # proxy caching configuration + proxy_cache ESP8266_cache; + proxy_cache_revalidate on; + proxy_cache_min_uses 1; + proxy_cache_use_stale off; + proxy_cache_lock on; + # proxy_cache_bypass $http_cache_control; + # include the sessionId cookie value as part of the cache key - keeps the cache per user + # proxy_cache_key $proxy_host$request_uri$cookie_sessionId; + + # header pass through configuration + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # ESP8266 custom headers which identify to the device that it's running through an SSL proxy + proxy_set_header X-SSL On; + proxy_set_header X-SSL-WebserverPort 50080; + proxy_set_header X-SSL-WebsocketPort 50081; + + # extra debug headers + add_header X-Proxy-Cache $upstream_cache_status; + add_header X-Forwarded-For $proxy_add_x_forwarded_for; + + # actual proxying configuration + proxy_ssl_session_reuse on; + # target the IP address of the device with proxy_pass + proxy_pass http://192.168.0.20; + proxy_read_timeout 90; + } + } + +# websocket proxy +server { + + # general server parameters + listen 50081; + server_name myDomain.net; + access_log /opt/var/log/nginx/myDomain.net.wss.access.log; + + # SSL configuration + ssl on; + ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem; + ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem; + ssl_session_cache builtin:1000 shared:SSL:10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; + ssl_prefer_server_ciphers on; + + location / { + + # websocket upgrade tunnel configuration + proxy_pass http://192.168.0.20:81; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 86400; + } + } diff --git a/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino b/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino new file mode 100644 index 00000000..9d49d149 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino @@ -0,0 +1,84 @@ +/* + * WebSocketClientAVR.ino + * + * Created on: 10.12.2015 + * + */ + +#include + +#include +#include + +#include + + + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +WebSocketsClient webSocket; + + + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + Serial.println("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + Serial.print("[WSc] Connected to url: "); + Serial.println((char *)payload); + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + Serial.print("[WSc] get text: "); + Serial.println((char *)payload); + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + Serial.print("[WSc] get binary length: "); + Serial.println(length); + // hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial) {} + + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + // try to congifure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } + + webSocket.begin("192.168.0.123", 8011); + webSocket.onEvent(webSocketEvent); + +} + + +void loop() +{ + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino new file mode 100644 index 00000000..41a2b703 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,106 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 00000000..dc3427b1 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,102 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include +#include + +#include + + +WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino b/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino new file mode 100644 index 00000000..7ebf8cab --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,100 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include + +#include + +WiFiMulti WiFiMulti; +WebSocketsServer webSocket = WebSocketsServer(81); + +HardwareSerial Serial1(2); + +#define USE_SERIAL Serial1 + +void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) { + const uint8_t* src = (const uint8_t*) mem; + USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); + for(uint32_t i = 0; i < len; i++) { + if(i % cols == 0) { + USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); + } + USE_SERIAL.printf("%02X ", *src); + src++; + } + USE_SERIAL.printf("\n"); +} + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino new file mode 100644 index 00000000..b990c13a --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino @@ -0,0 +1,92 @@ +/* + * WebSocketClient.ino + * + * Created on: 24.05.2015 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // server address, port and URL + webSocket.begin("192.168.0.123", 81, "/"); + + // event handler + webSocket.onEvent(webSocketEvent); + + // use HTTP Basic Authorization this is optional remove if not needed + webSocket.setAuthorization("user", "Password"); + + // try ever 5000 again if connection has failed + webSocket.setReconnectInterval(5000); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino new file mode 100644 index 00000000..d45060e9 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino @@ -0,0 +1,88 @@ +/* + * WebSocketClientSSL.ino + * + * Created on: 10.12.2015 + * + * note SSL is only possible with the ESP8266 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + // send message to server when Connected + webSocket.sendTXT("Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSSL("192.168.0.123", 81); + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino new file mode 100644 index 00000000..40e343e2 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino @@ -0,0 +1,113 @@ +/* + * WebSocketClientSocketIO.ino + * + * Created on: 06.06.2016 + * + */ + +#include + +#include +#include + +#include + +#include + +ESP8266WiFiMulti WiFiMulti; +WebSocketsClient webSocket; + + +#define USE_SERIAL Serial1 + +#define MESSAGE_INTERVAL 30000 +#define HEARTBEAT_INTERVAL 25000 + +uint64_t messageTimestamp = 0; +uint64_t heartbeatTimestamp = 0; +bool isConnected = false; + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + isConnected = false; + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + isConnected = true; + + // send message to server when Connected + // socket.io upgrade confirmation message (required) + webSocket.sendTXT("5"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + // send message to server + // webSocket.sendTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + //WiFi.disconnect(); + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.beginSocketIO("192.168.0.123", 81); + //webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization + webSocket.onEvent(webSocketEvent); + +} + +void loop() { + webSocket.loop(); + + if(isConnected) { + + uint64_t now = millis(); + + if(now - messageTimestamp > MESSAGE_INTERVAL) { + messageTimestamp = now; + // example socket.io message with type "messageType" and JSON payload + webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]"); + } + if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) { + heartbeatTimestamp = now; + // socket.io heartbeat message + webSocket.sendTXT("2"); + } + } +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino new file mode 100644 index 00000000..a0eb011f --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino @@ -0,0 +1,149 @@ +/* + WebSocketClientStomp.ino + + Example for connecting and maintining a connection with a STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 25.09.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// URL for STOMP endpoint. +// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket". +const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +/** + * STOMP messages need to be NULL-terminated (i.e., \0 or \u0000). + * However, when we send a String or a char[] array without specifying + * a length, the size of the message payload is derived by strlen() internally, + * thus dropping any NULL values appended to the "msg"-String. + * + * To solve this, we first convert the String to a NULL terminated char[] array + * via "c_str" and set the length of the payload to include the NULL value. + */ +void sendMessage(String & msg) { + webSocket.sendTXT(msg.c_str(), msg.length() + 1); +} + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + + String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n"; + sendMessage(msg); + } + break; + case WStype_TEXT: + { + // ##################### + // handle STOMP protocol + // ##################### + + String text = (char*) payload; + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (text.startsWith("CONNECTED")) { + + // subscribe to some channels + + String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n"; + sendMessage(msg); + delay(1000); + + // and send a message + + msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}"; + sendMessage(msg); + delay(1000); + + } else { + + // do something with messages + + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // connect to websocket + webSocket.begin(ws_host, ws_port, stompUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino new file mode 100644 index 00000000..cb0c45be --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino @@ -0,0 +1,150 @@ +/* + WebSocketClientStompOverSockJs.ino + + Example for connecting and maintining a connection with a SockJS+STOMP websocket connection. + In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html). + + Created on: 18.07.2017 + Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de +*/ + +// PRE + +#define USE_SERIAL Serial + + +// LIBRARIES + +#include +#include + +#include +#include + + +// SETTINGS + +const char* wlan_ssid = "yourssid"; +const char* wlan_password = "somepassword"; + +const char* ws_host = "the.host.net"; +const int ws_port = 80; + +// base URL for SockJS (websocket) connection +// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36): +// ws://://<3digits>//websocket +// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/". +const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!! + + +// VARIABLES + +WebSocketsClient webSocket; + + +// FUNCTIONS + +void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { + + switch (type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[WSc] Disconnected!\n"); + break; + case WStype_CONNECTED: + { + USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload); + } + break; + case WStype_TEXT: + { + // ##################### + // handle SockJs+STOMP protocol + // ##################### + + String text = (char*) payload; + + USE_SERIAL.printf("[WSc] get text: %s\n", payload); + + if (payload[0] == 'h') { + + USE_SERIAL.println("Heartbeat!"); + + } else if (payload[0] == 'o') { + + // on open connection + char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + + } else if (text.startsWith("a[\"CONNECTED")) { + + // subscribe to some channels + + char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + + // and send a message + + msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]"; + webSocket.sendTXT(msg); + delay(1000); + } + + break; + } + case WStype_BIN: + USE_SERIAL.printf("[WSc] get binary length: %u\n", length); + hexdump(payload, length); + + // send data to server + // webSocket.sendBIN(payload, length); + break; + } + +} + +void setup() { + + // setup serial + + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + // USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + + + // connect to WiFi + + USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ..."); + WiFi.mode(WIFI_STA); + WiFi.begin(wlan_ssid, wlan_password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + USE_SERIAL.print("."); + } + USE_SERIAL.println(" success."); + USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP()); + + + // ##################### + // create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36) + // ##################### + String socketUrl = ws_baseurl; + socketUrl += random(0, 999); + socketUrl += "/"; + socketUrl += random(0, 999999); // should be a random string, but this works (see ) + socketUrl += "/websocket"; + + // connect to websocket + webSocket.begin(ws_host, ws_port, socketUrl); + webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config + // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino new file mode 100644 index 00000000..1ac3002d --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: + { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + // send message to client + // webSocket.sendTXT(num, "message here"); + + // send data to all connected clients + // webSocket.broadcastTXT("message here"); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + + // send message to client + // webSocket.sendBIN(num, payload, length); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino new file mode 100644 index 00000000..5fed1a95 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino @@ -0,0 +1,132 @@ +/* + * WebSocketServerAllFunctionsDemo.ino + * + * Created on: 10.05.2018 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +unsigned long last_10sec = 0; +unsigned int counter = 0; + +void loop() { + unsigned long t = millis(); + webSocket.loop(); + server.handleClient(); + + if((t - last_10sec) > 10 * 1000) { + counter++; + bool ping = (counter % 2); + int i = webSocket.connectedClients(ping); + USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping); + last_10sec = millis(); + } +} diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino new file mode 100644 index 00000000..84c9775d --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino @@ -0,0 +1,94 @@ +/* + * WebSocketServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial + +String fragmentBuffer = ""; + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + break; + case WStype_BIN: + USE_SERIAL.printf("[%u] get binary length: %u\n", num, length); + hexdump(payload, length); + break; + + // Fragmentation / continuation opcode handling + // case WStype_FRAGMENT_BIN_START: + case WStype_FRAGMENT_TEXT_START: + fragmentBuffer = (char*)payload; + USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload); + break; + case WStype_FRAGMENT: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload); + break; + case WStype_FRAGMENT_FIN: + fragmentBuffer += (char*)payload; + USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload); + USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str()); + break; + } + +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino new file mode 100644 index 00000000..8bc646c4 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino @@ -0,0 +1,86 @@ +/* + * WebSocketServerHttpHeaderValidation.ino + * + * Created on: 08.06.2016 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +#define USE_SERIAL Serial1 + +const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId + +/* + * Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade + * based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=|" + */ +bool isCookieValid(String rawCookieHeaderValue) { + + if (rawCookieHeaderValue.indexOf("sessionId") != -1) { + String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|")); + unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10); + return sessionId == validSessionId; + } + return false; +} + +/* + * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader + */ +bool validateHttpHeader(String headerName, String headerValue) { + + //assume a true response for any headers not handled by this validator + bool valid = true; + + if(headerName.equalsIgnoreCase("Cookie")) { + //if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function + valid = isCookieValid(headerValue); + } + + return valid; +} + +void setup() { + // USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //Serial.setDebugOutput(true); + USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + //connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time + const char * headerkeys[] = { "Cookie" }; + size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*); + webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount); + webSocket.begin(); +} + +void loop() { + webSocket.loop(); +} + diff --git a/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino new file mode 100644 index 00000000..8f32e753 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino @@ -0,0 +1,121 @@ +/* + * WebSocketServer_LEDcontrol.ino + * + * Created on: 26.11.2015 + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LED_RED 15 +#define LED_GREEN 12 +#define LED_BLUE 13 + +#define USE_SERIAL Serial + + +ESP8266WiFiMulti WiFiMulti; + +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + + switch(type) { + case WStype_DISCONNECTED: + USE_SERIAL.printf("[%u] Disconnected!\n", num); + break; + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + case WStype_TEXT: + USE_SERIAL.printf("[%u] get Text: %s\n", num, payload); + + if(payload[0] == '#') { + // we get RGB data + + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + analogWrite(LED_RED, ((rgb >> 16) & 0xFF)); + analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF)); + analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF)); + } + + break; + } + +} + +void setup() { + //USE_SERIAL.begin(921600); + USE_SERIAL.begin(115200); + + //USE_SERIAL.setDebugOutput(true); + + USE_SERIAL.println(); + USE_SERIAL.println(); + USE_SERIAL.println(); + + for(uint8_t t = 4; t > 0; t--) { + USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t); + USE_SERIAL.flush(); + delay(1000); + } + + pinMode(LED_RED, OUTPUT); + pinMode(LED_GREEN, OUTPUT); + pinMode(LED_BLUE, OUTPUT); + + digitalWrite(LED_RED, 1); + digitalWrite(LED_GREEN, 1); + digitalWrite(LED_BLUE, 1); + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + // start webSocket server + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + if(MDNS.begin("esp8266")) { + USE_SERIAL.println("MDNS responder started"); + } + + // handle index + server.on("/", []() { + // send index.html + server.send(200, "text/html", "LED Control:

R:
G:
B:
"); + }); + + server.begin(); + + // Add service to MDNS + MDNS.addService("http", "tcp", 80); + MDNS.addService("ws", "tcp", 81); + + digitalWrite(LED_RED, 0); + digitalWrite(LED_GREEN, 0); + digitalWrite(LED_BLUE, 0); + +} + +void loop() { + webSocket.loop(); + server.handleClient(); +} diff --git a/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp b/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp new file mode 100644 index 00000000..461228f3 --- /dev/null +++ b/libraries/arduinoWebSockets/examples/particle/ParticleWebSocketClient/application.cpp @@ -0,0 +1,46 @@ +/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there. +* Then, copy src files under particleWebSocket folder. +*/ + +#include "application.h" +#include "particleWebSocket/WebSocketsClient.h" + +WebSocketsClient webSocket; + +void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) +{ + switch (type) + { + case WStype_DISCONNECTED: + Serial.printlnf("[WSc] Disconnected!"); + break; + case WStype_CONNECTED: + Serial.printlnf("[WSc] Connected to URL: %s", payload); + webSocket.sendTXT("Connected\r\n"); + break; + case WStype_TEXT: + Serial.printlnf("[WSc] get text: %s", payload); + break; + case WStype_BIN: + Serial.printlnf("[WSc] get binary length: %u", length); + break; + } +} + +void setup() +{ + Serial.begin(9600); + + WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP); + WiFi.connect(); + + webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212"); + webSocket.onEvent(webSocketEvent); +} + +void loop() +{ + webSocket.sendTXT("Hello world!"); + delay(500); + webSocket.loop(); +} diff --git a/libraries/arduinoWebSockets/library.json b/libraries/arduinoWebSockets/library.json new file mode 100644 index 00000000..0dbd62d2 --- /dev/null +++ b/libraries/arduinoWebSockets/library.json @@ -0,0 +1,25 @@ +{ + "name": "WebSockets", + "description": "WebSocket Server and Client for Arduino based on RFC6455", + "keywords": "wifi, http, web, server, client, websocket", + "authors": [ + { + "name": "Markus Sattler", + "url": "https://github.com/Links2004", + "maintainer": true + } + ], + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets.git" + }, + "version": "2.1.0", + "license": "LGPL-2.1", + "export": { + "exclude": [ + "tests" + ] + }, + "frameworks": "arduino", + "platforms": "atmelavr, espressif8266, espressif32" +} diff --git a/libraries/arduinoWebSockets/library.properties b/libraries/arduinoWebSockets/library.properties new file mode 100644 index 00000000..14e1fe95 --- /dev/null +++ b/libraries/arduinoWebSockets/library.properties @@ -0,0 +1,9 @@ +name=WebSockets +version=2.1.0 +author=Markus Sattler +maintainer=Markus Sattler +sentence=WebSockets for Arduino (Server + Client) +paragraph=use 2.x.x for ESP and 1.3 for AVR +category=Communication +url=https://github.com/Links2004/arduinoWebSockets +architectures=* diff --git a/libraries/arduinoWebSockets/src/WebSockets.cpp b/libraries/arduinoWebSockets/src/WebSockets.cpp new file mode 100644 index 00000000..f41cacbe --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSockets.cpp @@ -0,0 +1,654 @@ +/** + * @file WebSockets.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "WebSockets.h" + +#ifdef ESP8266 +#include +#endif + +extern "C" { +#ifdef CORE_HAS_LIBB64 +#include +#else +#include "libb64/cencode_inc.h" +#endif +} + +#ifdef ESP8266 +#include +#elif defined(ESP32) +#include +#else + +extern "C" { +#include "libsha1/libsha1.h" +} + +#endif + + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param code uint16_t see RFC + * @param reason ptr to the disconnect reason message + * @param reasonLen length of the disconnect reason message + */ +void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code); + if(client->status == WSC_CONNECTED && code) { + if(reason) { + sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen); + } else { + uint8_t buffer[2]; + buffer[0] = ((code >> 8) & 0xFF); + buffer[1] = (code & 0xFF); + sendFrame(client, WSop_close, &buffer[0], 2); + } + } + clientDisconnect(client); +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * ptr to the payload + * @param length size_t length of the payload + * @param mask bool add dummy mask to the frame (needed for web browser) + * @param fin bool can be used to send data in more then one frame (set fin on the last frame) + * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!) + * @return true if ok + */ +bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool mask, bool fin, bool headerToPayload) { + + if(client->tcp && !client->tcp->connected()) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num); + return false; + } + + if(client->status != WSC_CONNECTED) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num); + return false; + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, mask, length, headerToPayload); + + if(opcode == WSop_text) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0))); + } + + uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 }; + + uint8_t headerSize; + uint8_t * headerPtr; + uint8_t * payloadPtr = payload; + bool useInternBuffer = false; + bool ret = true; + + // calculate header Size + if(length < 126) { + headerSize = 2; + } else if(length < 0xFFFF) { + headerSize = 4; + } else { + headerSize = 10; + } + + if(mask) { + headerSize += 4; + } + +#ifdef WEBSOCKETS_USE_BIG_MEM + // only for ESP since AVR has less HEAP + // try to send data in one TCP package (only if some free Heap is there) + if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) { + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num); + uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE); + if(dataPtr) { + memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length); + headerToPayload = true; + useInternBuffer = true; + payloadPtr = dataPtr; + } + } +#endif + + // set Header Pointer + if(headerToPayload) { + // calculate offset in payload + headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize)); + } else { + headerPtr = &buffer[0]; + } + + // create header + + // byte 0 + *headerPtr = 0x00; + if(fin) { + *headerPtr |= bit(7); ///< set Fin + } + *headerPtr |= opcode; ///< set opcode + headerPtr++; + + // byte 1 + *headerPtr = 0x00; + if(mask) { + *headerPtr |= bit(7); ///< set mask + } + + if(length < 126) { + *headerPtr |= length; + headerPtr++; + } else if(length < 0xFFFF) { + *headerPtr |= 126; + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } else { + // Normally we never get here (to less memory) + *headerPtr |= 127; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = 0x00; + headerPtr++; + *headerPtr = ((length >> 24) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 16) & 0xFF); + headerPtr++; + *headerPtr = ((length >> 8) & 0xFF); + headerPtr++; + *headerPtr = (length & 0xFF); + headerPtr++; + } + + if(mask) { + if(useInternBuffer) { + // if we use a Intern Buffer we can modify the data + // by this fact its possible the do the masking + for(uint8_t x = 0; x < sizeof(maskKey); x++) { + maskKey[x] = random(0xFF); + *headerPtr = maskKey[x]; + headerPtr++; + } + + uint8_t * dataMaskPtr; + + if(headerToPayload) { + dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE); + } else { + dataMaskPtr = payloadPtr; + } + + for(size_t x = 0; x < length; x++) { + dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]); + } + + } else { + *headerPtr = maskKey[0]; + headerPtr++; + *headerPtr = maskKey[1]; + headerPtr++; + *headerPtr = maskKey[2]; + headerPtr++; + *headerPtr = maskKey[3]; + headerPtr++; + } + } + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + if(headerToPayload) { + // header has be added to payload + // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings + // offset in payload is calculatetd 14 - headerSize + if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) { + ret = false; + } + } else { + // send header + if(write(client, &buffer[0], headerSize) != headerSize) { + ret = false; + } + + if(payloadPtr && length > 0) { + // send payload + if(write(client, &payloadPtr[0], length) != length) { + ret = false; + } + } + } + + DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start)); + +#ifdef WEBSOCKETS_USE_BIG_MEM + if(useInternBuffer && payloadPtr) { + free(payloadPtr); + } +#endif + + return ret; +} + +/** + * callen when HTTP header is done + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::headerDone(WSclient_t * client) { + client->status = WSC_CONNECTED; + client->cWsRXsize = 0; + DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num); +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; + handleWebsocket(client); +#endif +} + +/** + * handle the WebSocket stream + * @param client WSclient_t * ptr to the client struct + */ +void WebSockets::handleWebsocket(WSclient_t * client) { + if(client->cWsRXsize == 0) { + handleWebsocketCb(client); + } +} + +/** + * wait for + * @param client + * @param size + */ +bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) { + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + if(size > WEBSOCKETS_MAX_HEADER_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size); + return false; + } + + if(client->cWsRXsize >= size) { + return true; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize); + readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok); + if(ok) { + client->cWsRXsize = size; + server->handleWebsocketCb(client); + } else { + DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num); + client->cWsRXsize = 0; + // timeout or error + server->clientDisconnect(client, 1002); + } + }, this, size, std::placeholders::_1, std::placeholders::_2)); + return false; +} + +void WebSockets::handleWebsocketCb(WSclient_t * client) { + + if(!client->tcp || !client->tcp->connected()) { + return; + } + + uint8_t * buffer = client->cWsHeader; + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + uint8_t * payload = NULL; + + uint8_t headerLen = 2; + + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + // split first 2 bytes in the data + header->fin = ((*buffer >> 7) & 0x01); + header->rsv1 = ((*buffer >> 6) & 0x01); + header->rsv2 = ((*buffer >> 5) & 0x01); + header->rsv3 = ((*buffer >> 4) & 0x01); + header->opCode = (WSopcode_t) (*buffer & 0x0F); + buffer++; + + header->mask = ((*buffer >> 7) & 0x01); + header->payloadLen = (WSopcode_t) (*buffer & 0x7F); + buffer++; + + if(header->payloadLen == 126) { + headerLen += 2; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->payloadLen = buffer[0] << 8 | buffer[1]; + buffer += 2; + } else if(header->payloadLen == 127) { + headerLen += 8; + // read 64bit integer as length + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + + if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { + // really too big! + header->payloadLen = 0xFFFFFFFF; + } else { + header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; + } + buffer += 8; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode); + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen); + + if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen); + clientDisconnect(client, 1009); + return; + } + + if(header->mask) { + headerLen += 4; + if(!handleWebsocketWaitFor(client, headerLen)) { + return; + } + header->maskKey = buffer; + buffer += 4; + } + + if(header->payloadLen > 0) { + // if text data we need one more + payload = (uint8_t *) malloc(header->payloadLen + 1); + + if(!payload) { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen); + clientDisconnect(client, 1011); + return; + } + readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload)); + } else { + handleWebsocketPayloadCb(client, true, NULL); + } +} + +void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) { + + WSMessageHeader_t * header = &client->cWsHeaderDecode; + if(ok) { + if(header->payloadLen > 0) { + payload[header->payloadLen] = 0x00; + + if(header->mask) { + //decode XOR + for(size_t i = 0; i < header->payloadLen; i++) { + payload[i] = (payload[i] ^ header->maskKey[i % 4]); + } + } + } + + switch(header->opCode) { + case WSop_text: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload); + // no break here! + case WSop_binary: + case WSop_continuation: + messageReceived(client, header->opCode, payload, header->payloadLen, header->fin); + break; + case WSop_ping: + // send pong back + sendFrame(client, WSop_pong, payload, header->payloadLen, true); + break; + case WSop_pong: + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : ""); + break; + case WSop_close: { + uint16_t reasonCode = 1000; + if(header->payloadLen >= 2) { + reasonCode = payload[0] << 8 | payload[1]; + } + + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode); + if(header->payloadLen > 2) { + DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2)); + } else { + DEBUG_WEBSOCKETS("\n"); + } + clientDisconnect(client, 1000); + } + break; + default: + clientDisconnect(client, 1002); + break; + } + + if(payload) { + free(payload); + } + + // reset input + client->cWsRXsize = 0; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + //register callback for next message + handleWebsocketWaitFor(client, 2); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num); + free(payload); + clientDisconnect(client, 1002); + } +} + +/** + * generate the key for Sec-WebSocket-Accept + * @param clientKey String + * @return String Accept Key + */ +String WebSockets::acceptKey(String & clientKey) { + uint8_t sha1HashBin[20] = { 0 }; +#ifdef ESP8266 + sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]); +#elif defined(ESP32) + String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + esp_sha(SHA1, (unsigned char*)data.c_str(), data.length(), &sha1HashBin[0]); +#else + clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + SHA1_CTX ctx; + SHA1Init(&ctx); + SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length()); + SHA1Final(&sha1HashBin[0], &ctx); +#endif + + String key = base64_encode(sha1HashBin, 20); + key.trim(); + + return key; +} + +/** + * base64_encode + * @param data uint8_t * + * @param length size_t + * @return base64 encoded String + */ +String WebSockets::base64_encode(uint8_t * data, size_t length) { + size_t size = ((length * 1.6f) + 1); + char * buffer = (char *) malloc(size); + if(buffer) { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + * read x byte from tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return true if ok + */ +bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + if(!client->tcp || !client->tcp->connected()) { + return false; + } + + client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) { + if(cb) { + cb(client, ok); + } + }, client, std::placeholders::_1, cb)); + +#else + unsigned long t = millis(); + size_t len; + DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[readCb] tcp is null!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[readCb] not connected!\n"); + if(cb) { + cb(client, false); + } + return false; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t)); + if(cb) { + cb(client, false); + } + return false; + } + + if(!client->tcp->available()) { +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + continue; + } + + len = client->tcp->read((uint8_t*) out, n); + if(len) { + t = millis(); + out += len; + n -= len; + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + if(cb) { + cb(client, true); + } +#endif + return true; +} + +/** + * write x byte to tcp or get timeout + * @param client WSclient_t * + * @param out uint8_t * data buffer + * @param n size_t byte count + * @return bytes send + */ +size_t WebSockets::write(WSclient_t * client, uint8_t *out, size_t n) { + if(out == NULL) return 0; + if(client == NULL) return 0; + unsigned long t = millis(); + size_t len = 0; + size_t total = 0; + DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t); + while(n > 0) { + if(client->tcp == NULL) { + DEBUG_WEBSOCKETS("[write] tcp is null!\n"); + break; + } + + if(!client->tcp->connected()) { + DEBUG_WEBSOCKETS("[write] not connected!\n"); + break; + } + + if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) { + DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t)); + break; + } + + len = client->tcp->write((const uint8_t*)out, n); + if(len) { + t = millis(); + out += len; + n -= len; + total += len; + //DEBUG_WEBSOCKETS("write %d left %d!\n", len, n); + } else { + //DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return total; +} + +size_t WebSockets::write(WSclient_t * client, const char *out) { + if(client == NULL) return 0; + if(out == NULL) return 0; + return write(client, (uint8_t*)out, strlen(out)); +} diff --git a/libraries/arduinoWebSockets/src/WebSockets.h b/libraries/arduinoWebSockets/src/WebSockets.h new file mode 100644 index 00000000..d4bb3110 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSockets.h @@ -0,0 +1,312 @@ +/** + * @file WebSockets.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef WEBSOCKETS_H_ +#define WEBSOCKETS_H_ + +#ifdef STM32_DEVICE +#include +#define bit(b) (1UL << (b)) // Taken directly from Arduino.h +#else +#include +#include +#endif + +#ifdef ARDUINO_ARCH_AVR +#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++. +#error Use Version 1.x.x. (ATmega branch) +#else +#include +#endif + +#define NODEBUG_WEBSOCKETS + +#ifndef NODEBUG_WEBSOCKETS +#ifdef DEBUG_ESP_PORT +#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ ) +#else +//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ ) +#endif +#endif + + +#ifndef DEBUG_WEBSOCKETS +#define DEBUG_WEBSOCKETS(...) +#define NODEBUG_WEBSOCKETS +#endif + +#if defined(ESP8266) || defined(ESP32) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP ESP.getFreeHeap() +// moves all Header strings to Flash (~300 Byte) +//#define WEBSOCKETS_SAVE_RAM + +#elif defined(STM32_DEVICE) + +#define WEBSOCKETS_MAX_DATA_SIZE (15*1024) +#define WEBSOCKETS_USE_BIG_MEM +#define GET_FREE_HEAP System.freeMemory() + +#else + +//atmega328p has only 2KB ram! +#define WEBSOCKETS_MAX_DATA_SIZE (1024) +// moves all Header strings to Flash +#define WEBSOCKETS_SAVE_RAM + +#endif + + +#define WEBSOCKETS_TCP_TIMEOUT (2000) + +#define NETWORK_ESP8266_ASYNC (0) +#define NETWORK_ESP8266 (1) +#define NETWORK_W5100 (2) +#define NETWORK_ENC28J60 (3) +#define NETWORK_ESP32 (4) + +// max size of the WS Message Header +#define WEBSOCKETS_MAX_HEADER_SIZE (14) + +#if !defined(WEBSOCKETS_NETWORK_TYPE) +// select Network type based +#if defined(ESP8266) || defined(ESP31B) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266 +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC +//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#elif defined(ESP32) +#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32 + +#else +#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100 + +#endif +#endif + +// Includes and defined based on Network Type +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +// Note: +// No SSL/WSS support for client in Async mode +// TLS lib need a sync interface! + + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#include +#elif defined(ESP31B) +#include +#else +#error "network type ESP8266 ASYNC only possible on the ESP mcu!" +#endif + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer +#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + +#if !defined(ESP8266) && !defined(ESP31B) +#error "network type ESP8266 only possible on the ESP mcu!" +#endif + +#ifdef ESP8266 +#include +#else +#include +#endif +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100) + +#ifdef STM32_DEVICE +#define WEBSOCKETS_NETWORK_CLASS TCPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer +#else +#include +#include +#define WEBSOCKETS_NETWORK_CLASS EthernetClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer +#endif + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60) + +#include +#define WEBSOCKETS_NETWORK_CLASS UIPClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer + +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + +#include +#include +#define WEBSOCKETS_NETWORK_CLASS WiFiClient +#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer + +#else +#error "no network type selected!" +#endif + +// moves all Header strings to Flash (~300 Byte) +#ifdef WEBSOCKETS_SAVE_RAM +#define WEBSOCKETS_STRING(var) F(var) +#else +#define WEBSOCKETS_STRING(var) var +#endif + +typedef enum { + WSC_NOT_CONNECTED, + WSC_HEADER, + WSC_CONNECTED +} WSclientsStatus_t; + +typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN, + WStype_FRAGMENT_TEXT_START, + WStype_FRAGMENT_BIN_START, + WStype_FRAGMENT, + WStype_FRAGMENT_FIN, +} WStype_t; + +typedef enum { + WSop_continuation = 0x00, ///< %x0 denotes a continuation frame + WSop_text = 0x01, ///< %x1 denotes a text frame + WSop_binary = 0x02, ///< %x2 denotes a binary frame + ///< %x3-7 are reserved for further non-control frames + WSop_close = 0x08, ///< %x8 denotes a connection close + WSop_ping = 0x09, ///< %x9 denotes a ping + WSop_pong = 0x0A ///< %xA denotes a pong + ///< %xB-F are reserved for further control frames +} WSopcode_t; + +typedef struct { + + bool fin; + bool rsv1; + bool rsv2; + bool rsv3; + + WSopcode_t opCode; + bool mask; + + size_t payloadLen; + + uint8_t * maskKey; +} WSMessageHeader_t; + +typedef struct { + uint8_t num; ///< connection number + + WSclientsStatus_t status; + + WEBSOCKETS_NETWORK_CLASS * tcp; + + bool isSocketIO; ///< client for socket.io server + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + bool isSSL; ///< run in ssl mode + WiFiClientSecure * ssl; +#endif + + String cUrl; ///< http url + uint16_t cCode; ///< http code + + bool cIsUpgrade; ///< Connection == Upgrade + bool cIsWebsocket; ///< Upgrade == websocket + + String cSessionId; ///< client Set-Cookie (session id) + String cKey; ///< client Sec-WebSocket-Key + String cAccept; ///< client Sec-WebSocket-Accept + String cProtocol; ///< client Sec-WebSocket-Protocol + String cExtensions; ///< client Sec-WebSocket-Extensions + uint16_t cVersion; ///< client Sec-WebSocket-Version + + uint8_t cWsRXsize; ///< State of the RX + uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer + WSMessageHeader_t cWsHeaderDecode; + + String base64Authorization; ///< Base64 encoded Auth request + String plainAuthorization; ///< Base64 encoded Auth request + + String extraHeaders; + + bool cHttpHeadersValid; ///< non-websocket http header validity indicator + size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + String cHttpLine; ///< HTTP header lines +#endif + +} WSclient_t; + + + +class WebSockets { + protected: +#ifdef __AVR__ + typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok); +#else + typedef std::function WSreadWaitCb; +#endif + + virtual void clientDisconnect(WSclient_t * client) = 0; + virtual bool clientIsConnected(WSclient_t * client) = 0; + + virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0; + + void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); + bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool mask = false, bool fin = true, bool headerToPayload = false); + + void headerDone(WSclient_t * client); + + void handleWebsocket(WSclient_t * client); + + bool handleWebsocketWaitFor(WSclient_t * client, size_t size); + void handleWebsocketCb(WSclient_t * client); + void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload); + + String acceptKey(String & clientKey); + String base64_encode(uint8_t * data, size_t length); + + bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb); + virtual size_t write(WSclient_t * client, uint8_t *out, size_t n); + size_t write(WSclient_t * client, const char *out); + + +}; + +#ifndef UNUSED +#define UNUSED(var) (void)(var) +#endif +#endif /* WEBSOCKETS_H_ */ diff --git a/libraries/arduinoWebSockets/src/WebSocketsClient.cpp b/libraries/arduinoWebSockets/src/WebSocketsClient.cpp new file mode 100644 index 00000000..f98822a8 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsClient.cpp @@ -0,0 +1,762 @@ +/** + * @file WebSocketsClient.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "WebSockets.h" +#include "WebSocketsClient.h" + +WebSocketsClient::WebSocketsClient() { + _cbEvent = NULL; + _client.num = 0; + _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://"); +} + +WebSocketsClient::~WebSocketsClient() { + disconnect(); +} + +/** + * calles to init the Websockets server + */ +void WebSocketsClient::begin(const char *host, uint16_t port, const char * url, const char * protocol) { + _host = host; + _port = port; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _fingerprint = ""; +#endif + + _client.num = 0; + _client.status = WSC_NOT_CONNECTED; + _client.tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + _client.isSSL = false; + _client.ssl = NULL; +#endif + _client.cUrl = url; + _client.cCode = 0; + _client.cIsUpgrade = false; + _client.cIsWebsocket = true; + _client.cKey = ""; + _client.cAccept = ""; + _client.cProtocol = protocol; + _client.cExtensions = ""; + _client.cVersion = 0; + _client.base64Authorization = ""; + _client.plainAuthorization = ""; + _client.isSocketIO = false; + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#else + // todo find better seed + randomSeed(millis()); +#endif +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + asyncConnect(); +#endif + + _lastConnectionFail = 0; + _reconnectInterval = 500; +} + +void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) { + begin(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) { + return begin(host.toString().c_str(), port, url, protocol); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) { + begin(host, port, url, protocol); + _client.isSSL = true; + _fingerprint = fingerprint; +} + +void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) { + beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str()); +} +#endif + +void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; +} + +void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) { + beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str()); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) { + begin(host, port, url, protocol); + _client.isSocketIO = true; + _client.isSSL = true; + _fingerprint = ""; +} + +void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) { + beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str()); +} +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsClient::loop(void) { + if(!clientIsConnected(&_client)) { + // do not flood the server + if((millis() - _lastConnectionFail) < _reconnectInterval) { + return; + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(_client.isSSL) { + DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); + if(_client.ssl) { + delete _client.ssl; + _client.ssl = NULL; + _client.tcp = NULL; + } + _client.ssl = new WiFiClientSecure(); + _client.tcp = _client.ssl; + } else { + DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); + if(_client.tcp) { + delete _client.tcp; + _client.tcp = NULL; + } + _client.tcp = new WiFiClient(); + } +#else + _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); +#endif + + if(!_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + if(_client.tcp->connect(_host.c_str(), _port)) { + connectedCb(); + _lastConnectionFail = 0; + } else { + connectFailedCb(); + _lastConnectionFail = millis(); + + } + } else { + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) { + _cbEvent = cbEvent; +} + +/** + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) { + if(length == 0) { + length = strlen((const char *) payload); + } + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) { + return sendTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsClient::sendTXT(const char * payload, size_t length) { + return sendTXT((uint8_t *) payload, length); +} + +bool WebSocketsClient::sendTXT(String & payload) { + return sendTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_binary, payload, length, true, true, headerToPayload); + } + return false; +} + +bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) { + return sendBIN((uint8_t *) payload, length); +} + +/** + * sends a WS ping to Server + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) { + if(clientIsConnected(&_client)) { + return sendFrame(&_client, WSop_ping, payload, length, true); + } + return false; +} + +bool WebSocketsClient::sendPing(String & payload) { + return sendPing((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsClient::disconnect(void) { + if(clientIsConnected(&_client)) { + WebSockets::clientDisconnect(&_client, 1000); + } +} + +/** + * set the Authorizatio for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsClient::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _client.base64Authorization = base64_encode((uint8_t *) auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsClient::setAuthorization(const char * auth) { + if(auth) { + //_client.base64Authorization = auth; + _client.plainAuthorization = auth; + } +} + +/** + * set extra headers for the http request; + * separate headers by "\r\n" + * @param extraHeaders const char * extraHeaders + */ +void WebSocketsClient::setExtraHeaders(const char * extraHeaders) { + _client.extraHeaders = extraHeaders; +} + +/** + * set the reconnect Interval + * how long to wait after a connection initiate failed + * @param time in ms + */ +void WebSocketsClient::setReconnectInterval(unsigned long time) { + _reconnectInterval = time; +} + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + UNUSED(client); + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::clientDisconnect(WSclient_t * client) { + + bool event = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + event = true; + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } + event = true; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cCode = 0; + client->cKey = ""; + client->cAccept = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + client->cSessionId = ""; + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); + if(event) { + runCbEvent(WStype_DISCONNECTED, NULL, 0); + } +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = conneted + */ +bool WebSocketsClient::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n"); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handel incomming data from Client + */ +void WebSocketsClient::handleClientData(void) { + int len = _client.tcp->available(); + if(len > 0) { + switch(_client.status) { + case WSC_HEADER: { + String headerLine = _client.tcp->readStringUntil('\n'); + handleHeader(&_client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(&_client); + break; + default: + WebSockets::clientDisconnect(&_client, 1002); + break; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); +#endif +} +#endif + +/** + * send the WebSocket header to Server + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::sendHeader(WSclient_t * client) { + + static const char * NEW_LINE = "\r\n"; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); + + uint8_t randomKey[16] = { 0 }; + + for(uint8_t i = 0; i < sizeof(randomKey); i++) { + randomKey[i] = random(0xFF); + } + + client->cKey = base64_encode(&randomKey[0], 16); + +#ifndef NODEBUG_WEBSOCKETS + unsigned long start = micros(); +#endif + + String handshake; + bool ws_header = true; + String url = client->cUrl; + + if(client->isSocketIO) { + if(client->cSessionId.length() == 0) { + url += WEBSOCKETS_STRING("&transport=polling"); + ws_header = false; + } else { + url += WEBSOCKETS_STRING("&transport=websocket&sid="); + url += client->cSessionId; + } + } + + handshake = WEBSOCKETS_STRING("GET "); + handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n" + "Host: "); + handshake += _host + ":" + _port + NEW_LINE; + + if(ws_header) { + handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: "); + handshake += client->cKey + NEW_LINE; + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake += client->cProtocol + NEW_LINE; + } + + if(client->cExtensions.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: "); + handshake += client->cExtensions + NEW_LINE; + } + } else { + handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n"); + } + + // add extra headers; by default this includes "Origin: file://" + if(client->extraHeaders) { + handshake += client->extraHeaders + NEW_LINE; + } + + handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n"); + + if(client->base64Authorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: Basic "); + handshake += client->base64Authorization + NEW_LINE; + } + + if(client->plainAuthorization.length() > 0) { + handshake += WEBSOCKETS_STRING("Authorization: "); + handshake += client->plainAuthorization + NEW_LINE; + } + + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t* )handshake.c_str()); + write(client, (uint8_t*) handshake.c_str(), handshake.length()); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start)); + +} + +/** + * handle the WebSocket header reading + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); + + if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) { + // "HTTP/1.1 101 Switching Protocols" + client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) { + client->cAccept = headerValue; + client->cAccept.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) { + if(headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";")); + } else { + client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1); + } + } + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); +#endif + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str()); + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n"); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str()); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + switch(client->cCode) { + case 101: ///< Switching Protocols + + break; + case 200: + if(client->isSocketIO) { + break; + } + case 403: ///< Forbidden + // todo handle login + default: ///< Server dont unterstand requrst + ok = false; + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode); + clientDisconnect(client); + _lastConnectionFail = millis(); + break; + } + } + + if(ok) { + + if(client->cAccept.length() == 0) { + ok = false; + } else { + // generate Sec-WebSocket-Accept key for check + String sKey = acceptKey(client->cKey); + if(sKey != client->cAccept) { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n"); + ok = false; + } + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); + headerDone(client); + + runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) { + sendHeader(client); + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); + _lastConnectionFail = millis(); + if(clientIsConnected(client)) { + write(client, "This is a webSocket client!"); + } + clientDisconnect(client); + } + } +} + +void WebSocketsClient::connectedCb() { + + DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; + + // reconnect + c->asyncConnect(); + + return true; + }, this, std::placeholders::_1, &_client)); +#endif + + _client.status = WSC_HEADER; + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _client.tcp->setNoDelay(true); + + if(_client.isSSL && _fingerprint.length()) { + if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { + DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); + WebSockets::clientDisconnect(&_client, 1000); + return; + } + } +#endif + + // send Header to Server + sendHeader(&_client); + +} + +void WebSocketsClient::connectFailedCb() { + DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + +void WebSocketsClient::asyncConnect() { + + DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n"); + + AsyncClient * tcpclient = new AsyncClient(); + + if(!tcpclient) { + DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n"); + return; + } + + tcpclient->onDisconnect([](void *obj, AsyncClient* c) { + c->free(); + delete c; + }); + + tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->_client.tcp = new AsyncTCPbuffer(tcp); + if(!ws->_client.tcp) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n"); + ws->connectFailedCb(); + return; + } + ws->connectedCb(); + }, this, std::placeholders::_2)); + + tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { + ws->connectFailedCb(); + + // reconnect + ws->asyncConnect(); + }, this, std::placeholders::_2)); + + if(!tcpclient->connect(_host.c_str(), _port)) { + connectFailedCb(); + delete tcpclient; + } + +} + +#endif diff --git a/libraries/arduinoWebSockets/src/WebSocketsClient.h b/libraries/arduinoWebSockets/src/WebSocketsClient.h new file mode 100644 index 00000000..61b8ea2a --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsClient.h @@ -0,0 +1,136 @@ +/** + * @file WebSocketsClient.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef WEBSOCKETSCLIENT_H_ +#define WEBSOCKETSCLIENT_H_ + +#include "WebSockets.h" + +class WebSocketsClient: private WebSockets { + public: +#ifdef __AVR__ + typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length); +#else + typedef std::function WebSocketClientEvent; +#endif + + + WebSocketsClient(void); + virtual ~WebSocketsClient(void); + + void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + void begin(String host, uint16_t port, String url = "/", String protocol = "arduino"); + void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino"); + void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino"); +#endif + + void beginSocketIO(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + void beginSocketIOSSL(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino"); + void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino"); +#endif + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketClientEvent cbEvent); + + bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const uint8_t * payload, size_t length = 0); + bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(const char * payload, size_t length = 0); + bool sendTXT(String & payload); + + bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t * payload = NULL, size_t length = 0); + bool sendPing(String & payload); + + void disconnect(void); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + void setExtraHeaders(const char * extraHeaders = NULL); + + void setReconnectInterval(unsigned long time); + + protected: + String _host; + uint16_t _port; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + String _fingerprint; +#endif + WSclient_t _client; + + WebSocketClientEvent _cbEvent; + + unsigned long _lastConnectionFail; + unsigned long _reconnectInterval; + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleClientData(void); +#endif + + void sendHeader(WSclient_t * client); + void handleHeader(WSclient_t * client, String * headerLine); + + void connectedCb(); + void connectFailedCb(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + void asyncConnect(); +#endif + + /** + * called for sending a Event to the app + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(type, payload, length); + } + } + +}; + +#endif /* WEBSOCKETSCLIENT_H_ */ diff --git a/libraries/arduinoWebSockets/src/WebSocketsServer.cpp b/libraries/arduinoWebSockets/src/WebSocketsServer.cpp new file mode 100644 index 00000000..b6f950f4 --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsServer.cpp @@ -0,0 +1,873 @@ +/** + * @file WebSocketsServer.cpp + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "WebSockets.h" +#include "WebSocketsServer.h" + +WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) { + _port = port; + _origin = origin; + _protocol = protocol; + _runnning = false; + + _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->onClient([](void *s, AsyncClient* c){ + ((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c)); + }, this); +#endif + + _cbEvent = NULL; + + _httpHeaderValidationFunc = NULL; + _mandatoryHttpHeaders = NULL; + _mandatoryHttpHeaderCount = 0; + + memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX)); +} + + +WebSocketsServer::~WebSocketsServer() { + // disconnect all clients + close(); + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = 0; +} + +/** + * called to initialize the Websocket server + */ +void WebSocketsServer::begin(void) { + WSclient_t * client; + + // init client storage + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + client->num = i; + client->status = WSC_NOT_CONNECTED; + client->tcp = NULL; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->ssl = NULL; +#endif + client->cUrl = ""; + client->cCode = 0; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->base64Authorization = ""; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + } + +#ifdef ESP8266 + randomSeed(RANDOM_REG32); +#elif defined(ESP32) + #define DR_REG_RNG_BASE 0x3ff75144 + randomSeed(READ_PERI_REG(DR_REG_RNG_BASE)); +#else + // TODO find better seed + randomSeed(millis()); +#endif + + _runnning = true; + _server->begin(); + + DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n"); +} + +void WebSocketsServer::close(void) { + _runnning = false; + disconnect(); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + _server->close(); +#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + _server->end(); +#else + // TODO how to close server? +#endif + +} + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * called in arduino loop + */ +void WebSocketsServer::loop(void) { + if(_runnning) { + handleNewClients(); + handleClientData(); + } +} +#endif + +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { + _cbEvent = cbEvent; +} + +/* + * Sets the custom http header validator function + * @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function + * @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed + * @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array + */ +void WebSocketsServer::onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount) +{ + _httpHeaderValidationFunc = validationFunc; + + if (_mandatoryHttpHeaders) + delete[] _mandatoryHttpHeaders; + + _mandatoryHttpHeaderCount = mandatoryHttpHeaderCount; + _mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount]; + + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + _mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i]; + } +} + +/* + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + if(length == 0) { + length = strlen((const char *) payload); + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_text, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) { + return sendTXT(num, (uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) { + return sendTXT(num, (uint8_t *) payload, length); +} + +bool WebSocketsServer::sendTXT(uint8_t num, String & payload) { + return sendTXT(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send text data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + if(length == 0) { + length = strlen((const char *) payload); + } + + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_text, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) { + return broadcastTXT((uint8_t *) payload, length, headerToPayload); +} + +bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) { + return broadcastTXT((uint8_t *) payload, length); +} + +bool WebSocketsServer::broadcastTXT(String & payload) { + return broadcastTXT((uint8_t *) payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload); + } + return false; +} + +bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) { + return sendBIN(num, (uint8_t *) payload, length); +} + +/** + * send binary data to client all + * @param payload uint8_t * + * @param length size_t + * @param headerToPayload bool (see sendFrame for more details) + * @return true if ok + */ +bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_binary, payload, length, false, true, headerToPayload)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) { + return broadcastBIN((uint8_t *) payload, length); +} + + +/** + * sends a WS ping to Client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return false; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return sendFrame(client, WSop_ping, payload, length); + } + return false; +} + +bool WebSocketsServer::sendPing(uint8_t num, String & payload) { + return sendPing(num, (uint8_t *) payload.c_str(), payload.length()); +} + +/** + * sends a WS ping to all Client + * @param payload uint8_t * + * @param length size_t + * @return true if ping is send out + */ +bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) { + WSclient_t * client; + bool ret = true; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + if(!sendFrame(client, WSop_ping, payload, length)) { + ret = false; + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } + return ret; +} + +bool WebSocketsServer::broadcastPing(String & payload) { + return broadcastPing((uint8_t *) payload.c_str(), payload.length()); +} + + +/** + * disconnect all clients + */ +void WebSocketsServer::disconnect(void) { + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } + } +} + +/** + * disconnect one client + * @param num uint8_t client id + */ +void WebSocketsServer::disconnect(uint8_t num) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + WebSockets::clientDisconnect(client, 1000); + } +} + + +/* + * set the Authorization for the http request + * @param user const char * + * @param password const char * + */ +void WebSocketsServer::setAuthorization(const char * user, const char * password) { + if(user && password) { + String auth = user; + auth += ":"; + auth += password; + _base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length()); + } +} + +/** + * set the Authorizatio for the http request + * @param auth const char * base64 + */ +void WebSocketsServer::setAuthorization(const char * auth) { + if(auth) { + _base64Authorization = auth; + } +} + +/** + * count the connected clients (optional ping them) + * @param ping bool ping the connected clients + */ +int WebSocketsServer::connectedClients(bool ping) { + WSclient_t * client; + int count = 0; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(client->status == WSC_CONNECTED) { + if(ping != true || sendPing(i)) { + count++; + } + } + } + return count; +} + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) +/** + * get an IP for a client + * @param num uint8_t client id + * @return IPAddress + */ +IPAddress WebSocketsServer::remoteIP(uint8_t num) { + if(num < WEBSOCKETS_SERVER_CLIENT_MAX) { + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + return client->tcp->remoteIP(); + } + } + + return IPAddress(); +} +#endif + +//################################################################################# +//################################################################################# +//################################################################################# + +/** + * handle new client connection + * @param client + */ +bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { + WSclient_t * client; + // search free list entry for client + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + + // state is not connected or tcp connection is lost + if(!clientIsConnected(client)) { + + client->tcp = TCPclient; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + client->isSSL = false; + client->tcp->setNoDelay(true); +#endif +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + // set Timeout for readBytesUntil and readStringUntil + client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); +#endif + client->status = WSC_HEADER; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = client->tcp->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); +#endif + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { + DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); + + AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp; + if(*sl == obj) { + client->status = WSC_NOT_CONNECTED; + *sl = NULL; + } + return true; + }, this, std::placeholders::_1, client)); + + + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + + return true; + break; + } + } + return false; +} + +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) { + WStype_t type = WStype_ERROR; + + switch(opcode) { + case WSop_text: + type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START; + break; + case WSop_binary: + type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START; + break; + case WSop_continuation: + type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT; + break; + case WSop_close: + case WSop_ping: + case WSop_pong: + default: + break; + } + + runCbEvent(client->num, type, payload, length); + +} + +/** + * Disconnect an client + * @param client WSclient_t * ptr to the client struct + */ +void WebSocketsServer::clientDisconnect(WSclient_t * client) { + + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + if(client->isSSL && client->ssl) { + if(client->ssl->connected()) { + client->ssl->flush(); + client->ssl->stop(); + } + delete client->ssl; + client->ssl = NULL; + client->tcp = NULL; + } +#endif + + if(client->tcp) { + if(client->tcp->connected()) { +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + client->tcp->flush(); +#endif + client->tcp->stop(); + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->status = WSC_NOT_CONNECTED; +#else + delete client->tcp; +#endif + client->tcp = NULL; + } + + client->cUrl = ""; + client->cKey = ""; + client->cProtocol = ""; + client->cVersion = 0; + client->cIsUpgrade = false; + client->cIsWebsocket = false; + + client->cWsRXsize = 0; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->cHttpLine = ""; +#endif + + client->status = WSC_NOT_CONNECTED; + + DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); + + runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0); + +} + +/** + * get client state + * @param client WSclient_t * ptr to the client struct + * @return true = connected + */ +bool WebSocketsServer::clientIsConnected(WSclient_t * client) { + + if(!client->tcp) { + return false; + } + + if(client->tcp->connected()) { + if(client->status != WSC_NOT_CONNECTED) { + return true; + } + } else { + // client lost + if(client->status != WSC_NOT_CONNECTED) { + DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num); + // do cleanup + clientDisconnect(client); + } + } + + if(client->tcp) { + // do cleanup + DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num); + clientDisconnect(client); + } + + return false; +} +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) +/** + * Handle incoming Connection Request + */ +void WebSocketsServer::handleNewClients(void) { + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + while(_server->hasClient()) { +#endif + bool ok = false; + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + // store new connection + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#else + WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); +#endif + + if(!tcpClient) { + DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); + return; + } + + ok = newClient(tcpClient); + + if(!ok) { + // no free space to handle client +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress ip = tcpClient->remoteIP(); + DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); +#else + DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); +#endif + tcpClient->stop(); + } + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + delay(0); + } +#endif + +} + + +/** + * Handel incomming data from Client + */ +void WebSocketsServer::handleClientData(void) { + + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + int len = client->tcp->available(); + if(len > 0) { + //DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len); + switch(client->status) { + case WSC_HEADER: + { + String headerLine = client->tcp->readStringUntil('\n'); + handleHeader(client, &headerLine); + } + break; + case WSC_CONNECTED: + WebSockets::handleWebsocket(client); + break; + default: + WebSockets::clientDisconnect(client, 1002); + break; + } + } + } +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) + delay(0); +#endif + } +} +#endif + +/* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ +bool WebSocketsServer::hasMandatoryHeader(String headerName) { + for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) { + if (_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName)) + return true; + } + return false; +} + + +/** + * handles http header reading for WebSocket upgrade + * @param client WSclient_t * ///< pointer to the client struct + * @param headerLine String ///< the header being read / processed + */ +void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { + + static const char * NEW_LINE = "\r\n"; + + headerLine->trim(); // remove \r + + if(headerLine->length() > 0) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str()); + + // websocket requests always start with GET see rfc6455 + if(headerLine->startsWith("GET ")) { + + // cut URL out + client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4)); + + //reset non-websocket http header validation state for this client + client->cHttpHeadersValid = true; + client->cMandatoryHeadersCount = 0; + + } else if(headerLine->indexOf(':')) { + String headerName = headerLine->substring(0, headerLine->indexOf(':')); + String headerValue = headerLine->substring(headerLine->indexOf(':') + 1); + + // remove space in the beginning (RFC2616) + if(headerValue[0] == ' ') { + headerValue.remove(0, 1); + } + + if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) { + headerValue.toLowerCase(); + if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) { + client->cIsUpgrade = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) { + if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) { + client->cIsWebsocket = true; + } + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) { + client->cVersion = headerValue.toInt(); + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) { + client->cKey = headerValue; + client->cKey.trim(); // see rfc6455 + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) { + client->cProtocol = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) { + client->cExtensions = headerValue; + } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) { + client->base64Authorization = headerValue; + } else { + client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue); + if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) { + client->cMandatoryHeadersCount++; + } + } + + } else { + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); + } + + (*headerLine) = ""; +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) + client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); +#endif + } else { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str()); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount); + + bool ok = (client->cIsUpgrade && client->cIsWebsocket); + + if(ok) { + if(client->cUrl.length() == 0) { + ok = false; + } + if(client->cKey.length() == 0) { + ok = false; + } + if(client->cVersion != 13) { + ok = false; + } + if(!client->cHttpHeadersValid) { + ok = false; + } + if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) { + ok = false; + } + } + + if(_base64Authorization.length() > 0) { + String auth = WEBSOCKETS_STRING("Basic "); + auth += _base64Authorization; + if(auth != client->base64Authorization) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num); + handleAuthorizationFailed(client); + return; + } + } + + if(ok) { + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num); + + // generate Sec-WebSocket-Accept key + String sKey = acceptKey(client->cKey); + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str()); + + client->status = WSC_CONNECTED; + + String handshake = WEBSOCKETS_STRING("HTTP/1.1 101 Switching Protocols\r\n" + "Server: arduino-WebSocketsServer\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Accept: "); + handshake += sKey + NEW_LINE; + + if(_origin.length() > 0) { + handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: "); + handshake +=_origin + NEW_LINE; + } + + if(client->cProtocol.length() > 0) { + handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: "); + handshake +=_protocol + NEW_LINE; + } + + // header end + handshake += NEW_LINE; + + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t*)handshake.c_str()); + + write(client, (uint8_t*)handshake.c_str(), handshake.length()); + + headerDone(client); + + // send ping + WebSockets::sendFrame(client, WSop_ping); + + runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); + + } else { + handleNonWebsocketConnection(client); + } + } +} + + + diff --git a/libraries/arduinoWebSockets/src/WebSocketsServer.h b/libraries/arduinoWebSockets/src/WebSocketsServer.h new file mode 100644 index 00000000..db945a6f --- /dev/null +++ b/libraries/arduinoWebSockets/src/WebSocketsServer.h @@ -0,0 +1,212 @@ +/** + * @file WebSocketsServer.h + * @date 20.05.2015 + * @author Markus Sattler + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the WebSockets for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef WEBSOCKETSSERVER_H_ +#define WEBSOCKETSSERVER_H_ + +#include "WebSockets.h" + +#ifndef WEBSOCKETS_SERVER_CLIENT_MAX +#define WEBSOCKETS_SERVER_CLIENT_MAX (5) +#endif + + + + +class WebSocketsServer: protected WebSockets { +public: + +#ifdef __AVR__ + typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); + typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue); +#else + typedef std::function WebSocketServerEvent; + typedef std::function WebSocketServerHttpHeaderValFunc; +#endif + + WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino"); + virtual ~WebSocketsServer(void); + + void begin(void); + void close(void); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void loop(void); +#else + // Async interface not need a loop call + void loop(void) __attribute__ ((deprecated)) {} +#endif + + void onEvent(WebSocketServerEvent cbEvent); + void onValidateHttpHeader( + WebSocketServerHttpHeaderValFunc validationFunc, + const char* mandatoryHttpHeaders[], + size_t mandatoryHttpHeaderCount); + + + bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0); + bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false); + bool sendTXT(uint8_t num, const char * payload, size_t length = 0); + bool sendTXT(uint8_t num, String & payload); + + bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const uint8_t * payload, size_t length = 0); + bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false); + bool broadcastTXT(const char * payload, size_t length = 0); + bool broadcastTXT(String & payload); + + bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false); + bool sendBIN(uint8_t num, const uint8_t * payload, size_t length); + + bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false); + bool broadcastBIN(const uint8_t * payload, size_t length); + + bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0); + bool sendPing(uint8_t num, String & payload); + + bool broadcastPing(uint8_t * payload = NULL, size_t length = 0); + bool broadcastPing(String & payload); + + void disconnect(void); + void disconnect(uint8_t num); + + void setAuthorization(const char * user, const char * password); + void setAuthorization(const char * auth); + + int connectedClients(bool ping = false); + +#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) + IPAddress remoteIP(uint8_t num); +#endif + +protected: + uint16_t _port; + String _origin; + String _protocol; + String _base64Authorization; ///< Base64 encoded Auth request + String * _mandatoryHttpHeaders; + size_t _mandatoryHttpHeaderCount; + + WEBSOCKETS_NETWORK_SERVER_CLASS * _server; + + WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; + + WebSocketServerEvent _cbEvent; + WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc; + + bool _runnning; + + bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient); + + void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin); + + void clientDisconnect(WSclient_t * client); + bool clientIsConnected(WSclient_t * client); + +#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) + void handleNewClients(void); + void handleClientData(void); +#endif + + void handleHeader(WSclient_t * client, String * headerLine); + + /** + * called if a non Websocket connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleNonWebsocketConnection(WSclient_t * client) { + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); + client->tcp->write("HTTP/1.1 400 Bad Request\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 32\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n" + "This is a Websocket server only!"); + clientDisconnect(client); + } + + /** + * called if a non Authorization connection is coming in. + * Note: can be override + * @param client WSclient_t * ptr to the client struct + */ + virtual void handleAuthorizationFailed(WSclient_t *client) { + client->tcp->write("HTTP/1.1 401 Unauthorized\r\n" + "Server: arduino-WebSocket-Server\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 45\r\n" + "Connection: close\r\n" + "Sec-WebSocket-Version: 13\r\n" + "WWW-Authenticate: Basic realm=\"WebSocket Server\"" + "\r\n" + "This Websocket server requires Authorization!"); + clientDisconnect(client); + } + + /** + * called for sending a Event to the app + * @param num uint8_t + * @param type WStype_t + * @param payload uint8_t * + * @param length size_t + */ + virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { + if(_cbEvent) { + _cbEvent(num, type, payload, length); + } + } + + /* + * Called at client socket connect handshake negotiation time for each http header that is not + * a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*) + * If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the + * socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected + * This mechanism can be used to enable custom authentication schemes e.g. test the value + * of a session cookie to determine if a user is logged on / authenticated + */ + virtual bool execHttpHeaderValidation(String headerName, String headerValue) { + if(_httpHeaderValidationFunc) { + //return the value of the custom http header validation function + return _httpHeaderValidationFunc(headerName, headerValue); + } + //no custom http header validation so just assume all is good + return true; + } + +private: + /* + * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection + * @param headerName String ///< the name of the header being checked + */ + bool hasMandatoryHeader(String headerName); + +}; + + + +#endif /* WEBSOCKETSSERVER_H_ */ diff --git a/libraries/arduinoWebSockets/src/libb64/AUTHORS b/libraries/arduinoWebSockets/src/libb64/AUTHORS new file mode 100644 index 00000000..af687375 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/AUTHORS @@ -0,0 +1,7 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +Authors: +------- + +Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com diff --git a/libraries/arduinoWebSockets/src/libb64/LICENSE b/libraries/arduinoWebSockets/src/libb64/LICENSE new file mode 100644 index 00000000..a6b56069 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/LICENSE @@ -0,0 +1,29 @@ +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. \ No newline at end of file diff --git a/libraries/arduinoWebSockets/src/libb64/cdecode.c b/libraries/arduinoWebSockets/src/libb64/cdecode.c new file mode 100644 index 00000000..e135da24 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cdecode.c @@ -0,0 +1,98 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cdecode_inc.h" + +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + +#endif diff --git a/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h b/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h new file mode 100644 index 00000000..d0d7f489 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cdecode_inc.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/libraries/arduinoWebSockets/src/libb64/cencode.c b/libraries/arduinoWebSockets/src/libb64/cencode.c new file mode 100644 index 00000000..afe1463c --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cencode.c @@ -0,0 +1,119 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifdef ESP8266 +#include +#endif + +#if defined(ESP32) +#define CORE_HAS_LIBB64 +#endif + +#ifndef CORE_HAS_LIBB64 +#include "cencode_inc.h" + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = 0x00; + + return codechar - code_out; +} + +#endif diff --git a/libraries/arduinoWebSockets/src/libb64/cencode_inc.h b/libraries/arduinoWebSockets/src/libb64/cencode_inc.h new file mode 100644 index 00000000..c1e3464a --- /dev/null +++ b/libraries/arduinoWebSockets/src/libb64/cencode_inc.h @@ -0,0 +1,31 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; +} base64_encodestate; + +void base64_init_encodestate(base64_encodestate* state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ diff --git a/libraries/arduinoWebSockets/src/libsha1/libsha1.c b/libraries/arduinoWebSockets/src/libsha1/libsha1.c new file mode 100644 index 00000000..48f4df5a --- /dev/null +++ b/libraries/arduinoWebSockets/src/libsha1/libsha1.c @@ -0,0 +1,202 @@ +/* from valgrind tests */ + +/* ================ sha1.c ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#if !defined(ESP8266) && !defined(ESP32) + +#define SHA1HANDSOFF + +#include +#include +#include + +#include "libsha1.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; +#ifdef SHA1HANDSOFF + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ + CHAR64LONG16* block = (const CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) +{ + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + unsigned i; + unsigned char finalcount[8]; + unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + uint32_t t = context->count[i]; + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t; + } +#else + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } +#endif + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +/* ================ end of sha1.c ================ */ + + +#endif diff --git a/libraries/arduinoWebSockets/src/libsha1/libsha1.h b/libraries/arduinoWebSockets/src/libsha1/libsha1.h new file mode 100644 index 00000000..ee3718e1 --- /dev/null +++ b/libraries/arduinoWebSockets/src/libsha1/libsha1.h @@ -0,0 +1,21 @@ +/* ================ sha1.h ================ */ +/* +SHA-1 in C +By Steve Reid +100% Public Domain +*/ + +#if !defined(ESP8266) && !defined(ESP32) + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#endif diff --git a/libraries/arduinoWebSockets/tests/webSocket.html b/libraries/arduinoWebSockets/tests/webSocket.html new file mode 100644 index 00000000..66a27089 --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocket.html @@ -0,0 +1,49 @@ + + + + + + + +LED Control:
+
+R:
+G:
+B:
+ + \ No newline at end of file diff --git a/libraries/arduinoWebSockets/tests/webSocketServer/index.js b/libraries/arduinoWebSockets/tests/webSocketServer/index.js new file mode 100644 index 00000000..389e1930 --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocketServer/index.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node +var WebSocketServer = require('websocket').server; +var http = require('http'); + +var server = http.createServer(function(request, response) { + console.log((new Date()) + ' Received request for ' + request.url); + response.writeHead(404); + response.end(); +}); +server.listen(8011, function() { + console.log((new Date()) + ' Server is listening on port 8011'); +}); + +wsServer = new WebSocketServer({ + httpServer: server, + // You should not use autoAcceptConnections for production + // applications, as it defeats all standard cross-origin protection + // facilities built into the protocol and the browser. You should + // *always* verify the connection's origin and decide whether or not + // to accept it. + autoAcceptConnections: false +}); + +function originIsAllowed(origin) { + // put logic here to detect whether the specified origin is allowed. + return true; +} + +wsServer.on('request', function(request) { + + if (!originIsAllowed(request.origin)) { + // Make sure we only accept requests from an allowed origin + request.reject(); + console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); + return; + } + + var connection = request.accept('arduino', request.origin); + console.log((new Date()) + ' Connection accepted.'); + + connection.on('message', function(message) { + if (message.type === 'utf8') { + console.log('Received Message: ' + message.utf8Data); + // connection.sendUTF(message.utf8Data); + } + else if (message.type === 'binary') { + console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); + //connection.sendBytes(message.binaryData); + } + }); + + connection.on('close', function(reasonCode, description) { + console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.'); + }); + + connection.sendUTF("Hallo Client!"); +}); diff --git a/libraries/arduinoWebSockets/tests/webSocketServer/package.json b/libraries/arduinoWebSockets/tests/webSocketServer/package.json new file mode 100644 index 00000000..9538323e --- /dev/null +++ b/libraries/arduinoWebSockets/tests/webSocketServer/package.json @@ -0,0 +1,27 @@ +{ + "name": "webSocketServer", + "version": "1.0.0", + "description": "WebSocketServer for testing", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Links2004/arduinoWebSockets" + }, + "keywords": [ + "esp8266", + "websocket", + "arduino" + ], + "author": "Markus Sattler", + "license": "LGPLv2", + "bugs": { + "url": "https://github.com/Links2004/arduinoWebSockets/issues" + }, + "homepage": "https://github.com/Links2004/arduinoWebSockets", + "dependencies": { + "websocket": "^1.0.18" + } +} diff --git a/libraries/arduinoWebSockets/travis/common.sh b/libraries/arduinoWebSockets/travis/common.sh new file mode 100644 index 00000000..be959faf --- /dev/null +++ b/libraries/arduinoWebSockets/travis/common.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +function build_sketches() +{ + local arduino=$1 + local srcpath=$2 + local platform=$3 + local sketches=$(find $srcpath -name *.ino) + for sketch in $sketches; do + local sketchdir=$(dirname $sketch) + if [[ -f "$sketchdir/.$platform.skip" ]]; then + echo -e "\n\n ------------ Skipping $sketch ------------ \n\n"; + continue + fi + echo -e "\n\n ------------ Building $sketch ------------ \n\n"; + $arduino --verify $sketch; + local result=$? + if [ $result -ne 0 ]; then + echo "Build failed ($sketch) build verbose..." + $arduino --verify --verbose --preserve-temp-files $sketch + result=$? + fi + if [ $result -ne 0 ]; then + echo "Build failed ($1) $sketch" + return $result + fi + done +} + + +function get_core() +{ + echo Setup core for $1 + + cd $HOME/arduino_ide/hardware + + if [ "$1" = "esp8266" ] ; then + mkdir esp8266com + cd esp8266com + git clone https://github.com/esp8266/Arduino.git esp8266 + cd esp8266/tools + python get.py + fi + + if [ "$1" = "esp32" ] ; then + mkdir espressif + cd espressif + git clone https://github.com/espressif/arduino-esp32.git esp32 + cd esp32/tools + python get.py + fi + +}