Compare commits
	
		
			20 Commits
		
	
	
		
			Release-1.
			...
			030f010832
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 030f010832 | |||
| a2b7a1b10e | |||
| 10c098ad33 | |||
| 3c3feeb8b5 | |||
| 893a57707d | |||
| 790261b6b7 | |||
| 0b6c432c70 | |||
| c88b532978 | |||
| 27b8cb0166 | |||
| 56872ea856 | |||
| d3506fd479 | |||
| ba90e98565 | |||
| 7b2c853b0d | |||
| ed8f65b92e | |||
| 69e5249a9d | |||
| b9f3b39684 | |||
| 63f8c34d97 | |||
| d99216815a | |||
| e6b5c0e3f0 | |||
| 9aa266792d | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,3 +3,4 @@
 | 
			
		||||
.vscode/c_cpp_properties.json
 | 
			
		||||
.vscode/launch.json
 | 
			
		||||
.vscode/ipch
 | 
			
		||||
wifi_credentials.ini
 | 
			
		||||
							
								
								
									
										2
									
								
								lib/ESP Async WebServer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								lib/ESP Async WebServer/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
.vscode
 | 
			
		||||
.DS_Store
 | 
			
		||||
							
								
								
									
										1
									
								
								lib/ESP Async WebServer/.piopm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lib/ESP Async WebServer/.piopm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"type": "library", "name": "ESP Async WebServer", "version": "1.2.3", "spec": {"owner": "me-no-dev", "id": 306, "name": "ESP Async WebServer", "requirements": null, "uri": null}}
 | 
			
		||||
							
								
								
									
										46
									
								
								lib/ESP Async WebServer/.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								lib/ESP Async WebServer/.travis.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
 | 
			
		||||
language: python
 | 
			
		||||
 | 
			
		||||
os:
 | 
			
		||||
  - linux
 | 
			
		||||
 | 
			
		||||
git:
 | 
			
		||||
  depth: false
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
  - build
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  include:
 | 
			
		||||
 | 
			
		||||
    - name: "Build Arduino ESP32"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32
 | 
			
		||||
 | 
			
		||||
    - name: "Build Arduino ESP8266"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266
 | 
			
		||||
 | 
			
		||||
    - name: "Build Platformio ESP32"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp32 1 1
 | 
			
		||||
 | 
			
		||||
    - name: "Build Platformio ESP8266"
 | 
			
		||||
      if: tag IS blank AND (type = pull_request OR (type = push AND branch = master))
 | 
			
		||||
      stage: build
 | 
			
		||||
      script: bash $TRAVIS_BUILD_DIR/.github/scripts/on-push.sh esp8266 1 1
 | 
			
		||||
 | 
			
		||||
notifications:
 | 
			
		||||
  email:
 | 
			
		||||
    on_success: change
 | 
			
		||||
    on_failure: change
 | 
			
		||||
  webhooks:
 | 
			
		||||
    urls:
 | 
			
		||||
      - https://webhooks.gitter.im/e/60e65d0c78ea0a920347
 | 
			
		||||
    on_success: change  # options: [always|never|change] default: always
 | 
			
		||||
    on_failure: always  # options: [always|never|change] default: always
 | 
			
		||||
    on_start: never     # options: [always|never|change] default: always
 | 
			
		||||
							
								
								
									
										17
									
								
								lib/ESP Async WebServer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/ESP Async WebServer/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
set(COMPONENT_SRCDIRS
 | 
			
		||||
    "src"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(COMPONENT_ADD_INCLUDEDIRS
 | 
			
		||||
    "src"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(COMPONENT_REQUIRES
 | 
			
		||||
    "arduino-esp32"
 | 
			
		||||
    "AsyncTCP"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
register_component()
 | 
			
		||||
 | 
			
		||||
target_compile_definitions(${COMPONENT_TARGET} PUBLIC -DESP32)
 | 
			
		||||
target_compile_options(${COMPONENT_TARGET} PRIVATE -fno-rtti)
 | 
			
		||||
							
								
								
									
										1486
									
								
								lib/ESP Async WebServer/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1486
									
								
								lib/ESP Async WebServer/README.md
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								lib/ESP Async WebServer/_config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lib/ESP Async WebServer/_config.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
theme: jekyll-theme-cayman
 | 
			
		||||
							
								
								
									
										3
									
								
								lib/ESP Async WebServer/component.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/ESP Async WebServer/component.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
COMPONENT_ADD_INCLUDEDIRS := src
 | 
			
		||||
COMPONENT_SRCDIRS := src
 | 
			
		||||
CXXFLAGS += -fno-rtti
 | 
			
		||||
							
								
								
									
										3
									
								
								lib/ESP Async WebServer/keywords.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/ESP Async WebServer/keywords.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
JsonArray	KEYWORD1
 | 
			
		||||
add	KEYWORD2
 | 
			
		||||
createArray	KEYWORD3
 | 
			
		||||
							
								
								
									
										33
									
								
								lib/ESP Async WebServer/library.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								lib/ESP Async WebServer/library.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
{
 | 
			
		||||
  "name":"ESP Async WebServer",
 | 
			
		||||
  "description":"Asynchronous HTTP and WebSocket Server Library for ESP8266 and ESP32",
 | 
			
		||||
  "keywords":"http,async,websocket,webserver",
 | 
			
		||||
  "authors":
 | 
			
		||||
  {
 | 
			
		||||
    "name": "Hristo Gochkov",
 | 
			
		||||
    "maintainer": true
 | 
			
		||||
  },
 | 
			
		||||
  "repository":
 | 
			
		||||
  {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://github.com/me-no-dev/ESPAsyncWebServer.git"
 | 
			
		||||
  },
 | 
			
		||||
  "version": "1.2.3",
 | 
			
		||||
  "license": "LGPL-3.0",
 | 
			
		||||
  "frameworks": "arduino",
 | 
			
		||||
  "platforms": ["espressif8266", "espressif32"],
 | 
			
		||||
  "dependencies": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "ESPAsyncTCP",
 | 
			
		||||
      "platforms": "espressif8266"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "AsyncTCP",
 | 
			
		||||
      "platforms": "espressif32"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Hash",
 | 
			
		||||
      "platforms": "espressif8266"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								lib/ESP Async WebServer/library.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/ESP Async WebServer/library.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
name=ESP Async WebServer
 | 
			
		||||
version=1.2.3
 | 
			
		||||
author=Me-No-Dev
 | 
			
		||||
maintainer=Me-No-Dev
 | 
			
		||||
sentence=Async Web Server for ESP8266 and ESP31B
 | 
			
		||||
paragraph=Async Web Server for ESP8266 and ESP31B
 | 
			
		||||
category=Other
 | 
			
		||||
url=https://github.com/me-no-dev/ESPAsyncWebServer
 | 
			
		||||
architectures=*
 | 
			
		||||
							
								
								
									
										363
									
								
								lib/ESP Async WebServer/src/AsyncEventSource.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								lib/ESP Async WebServer/src/AsyncEventSource.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,363 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. 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 "Arduino.h"
 | 
			
		||||
#include "AsyncEventSource.h"
 | 
			
		||||
 | 
			
		||||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
  String ev = "";
 | 
			
		||||
 | 
			
		||||
  if(reconnect){
 | 
			
		||||
    ev += "retry: ";
 | 
			
		||||
    ev += String(reconnect);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(id){
 | 
			
		||||
    ev += "id: ";
 | 
			
		||||
    ev += String(id);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(event != NULL){
 | 
			
		||||
    ev += "event: ";
 | 
			
		||||
    ev += String(event);
 | 
			
		||||
    ev += "\r\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(message != NULL){
 | 
			
		||||
    size_t messageLen = strlen(message);
 | 
			
		||||
    char * lineStart = (char *)message;
 | 
			
		||||
    char * lineEnd;
 | 
			
		||||
    do {
 | 
			
		||||
      char * nextN = strchr(lineStart, '\n');
 | 
			
		||||
      char * nextR = strchr(lineStart, '\r');
 | 
			
		||||
      if(nextN == NULL && nextR == NULL){
 | 
			
		||||
        size_t llen = ((char *)message + messageLen) - lineStart;
 | 
			
		||||
        char * ldata = (char *)malloc(llen+1);
 | 
			
		||||
        if(ldata != NULL){
 | 
			
		||||
          memcpy(ldata, lineStart, llen);
 | 
			
		||||
          ldata[llen] = 0;
 | 
			
		||||
          ev += "data: ";
 | 
			
		||||
          ev += ldata;
 | 
			
		||||
          ev += "\r\n\r\n";
 | 
			
		||||
          free(ldata);
 | 
			
		||||
        }
 | 
			
		||||
        lineStart = (char *)message + messageLen;
 | 
			
		||||
      } else {
 | 
			
		||||
        char * nextLine = NULL;
 | 
			
		||||
        if(nextN != NULL && nextR != NULL){
 | 
			
		||||
          if(nextR < nextN){
 | 
			
		||||
            lineEnd = nextR;
 | 
			
		||||
            if(nextN == (nextR + 1))
 | 
			
		||||
              nextLine = nextN + 1;
 | 
			
		||||
            else
 | 
			
		||||
              nextLine = nextR + 1;
 | 
			
		||||
          } else {
 | 
			
		||||
            lineEnd = nextN;
 | 
			
		||||
            if(nextR == (nextN + 1))
 | 
			
		||||
              nextLine = nextR + 1;
 | 
			
		||||
            else
 | 
			
		||||
              nextLine = nextN + 1;
 | 
			
		||||
          }
 | 
			
		||||
        } else if(nextN != NULL){
 | 
			
		||||
          lineEnd = nextN;
 | 
			
		||||
          nextLine = nextN + 1;
 | 
			
		||||
        } else {
 | 
			
		||||
          lineEnd = nextR;
 | 
			
		||||
          nextLine = nextR + 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t llen = lineEnd - lineStart;
 | 
			
		||||
        char * ldata = (char *)malloc(llen+1);
 | 
			
		||||
        if(ldata != NULL){
 | 
			
		||||
          memcpy(ldata, lineStart, llen);
 | 
			
		||||
          ldata[llen] = 0;
 | 
			
		||||
          ev += "data: ";
 | 
			
		||||
          ev += ldata;
 | 
			
		||||
          ev += "\r\n";
 | 
			
		||||
          free(ldata);
 | 
			
		||||
        }
 | 
			
		||||
        lineStart = nextLine;
 | 
			
		||||
        if(lineStart == ((char *)message + messageLen))
 | 
			
		||||
          ev += "\r\n";
 | 
			
		||||
      }
 | 
			
		||||
    } while(lineStart < ((char *)message + messageLen));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
 | 
			
		||||
: _data(nullptr), _len(len), _sent(0), _acked(0)
 | 
			
		||||
{
 | 
			
		||||
  _data = (uint8_t*)malloc(_len+1);
 | 
			
		||||
  if(_data == nullptr){
 | 
			
		||||
    _len = 0;
 | 
			
		||||
  } else {
 | 
			
		||||
    memcpy(_data, data, len);
 | 
			
		||||
    _data[_len] = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceMessage::~AsyncEventSourceMessage() {
 | 
			
		||||
     if(_data != NULL)
 | 
			
		||||
        free(_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
 | 
			
		||||
  // If the whole message is now acked...
 | 
			
		||||
  if(_acked + len > _len){
 | 
			
		||||
     // Return the number of extra bytes acked (they will be carried on to the next message)
 | 
			
		||||
     const size_t extra = _acked + len - _len;
 | 
			
		||||
     _acked = _len;
 | 
			
		||||
     return extra;
 | 
			
		||||
  }
 | 
			
		||||
  // Return that no extra bytes left.
 | 
			
		||||
  _acked += len;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
 | 
			
		||||
  const size_t len = _len - _sent;
 | 
			
		||||
  if(client->space() < len){
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  size_t sent = client->add((const char *)_data, len);
 | 
			
		||||
  if(client->canSend())
 | 
			
		||||
    client->send();
 | 
			
		||||
  _sent += sent;
 | 
			
		||||
  return sent; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
 | 
			
		||||
: _messageQueue(LinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete  m; }))
 | 
			
		||||
{
 | 
			
		||||
  _client = request->client();
 | 
			
		||||
  _server = server;
 | 
			
		||||
  _lastId = 0;
 | 
			
		||||
  if(request->hasHeader("Last-Event-ID"))
 | 
			
		||||
    _lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str());
 | 
			
		||||
    
 | 
			
		||||
  _client->setRxTimeout(0);
 | 
			
		||||
  _client->onError(NULL, NULL);
 | 
			
		||||
  _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
 | 
			
		||||
  _client->onPoll([](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
 | 
			
		||||
  _client->onData(NULL, NULL);
 | 
			
		||||
  _client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
 | 
			
		||||
  _client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
 | 
			
		||||
 | 
			
		||||
  _server->_addClient(this);
 | 
			
		||||
  delete request;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceClient::~AsyncEventSourceClient(){
 | 
			
		||||
   _messageQueue.free();
 | 
			
		||||
  close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
 | 
			
		||||
  if(dataMessage == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
  if(!connected()){
 | 
			
		||||
    delete dataMessage;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _messageQueue.add(dataMessage);
 | 
			
		||||
 | 
			
		||||
  _runQueue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
 | 
			
		||||
  while(len && !_messageQueue.isEmpty()){
 | 
			
		||||
    len = _messageQueue.front()->ack(len, time);
 | 
			
		||||
    if(_messageQueue.front()->finished())
 | 
			
		||||
      _messageQueue.remove(_messageQueue.front());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _runQueue();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onPoll(){
 | 
			
		||||
  if(!_messageQueue.isEmpty()){
 | 
			
		||||
    _runQueue();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
 | 
			
		||||
  _client->close(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_onDisconnect(){
 | 
			
		||||
  _client = NULL;
 | 
			
		||||
  _server->_handleDisconnect(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::close(){
 | 
			
		||||
  if(_client != NULL)
 | 
			
		||||
    _client->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::write(const char * message, size_t len){
 | 
			
		||||
  _queueMessage(new AsyncEventSourceMessage(message, len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
  String ev = generateEventMessage(message, event, id, reconnect);
 | 
			
		||||
  _queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceClient::_runQueue(){
 | 
			
		||||
  while(!_messageQueue.isEmpty() && _messageQueue.front()->finished()){
 | 
			
		||||
    _messageQueue.remove(_messageQueue.front());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i)
 | 
			
		||||
  {
 | 
			
		||||
    if(!(*i)->sent())
 | 
			
		||||
      (*i)->send(_client);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Handler
 | 
			
		||||
 | 
			
		||||
AsyncEventSource::AsyncEventSource(const String& url)
 | 
			
		||||
  : _url(url)
 | 
			
		||||
  , _clients(LinkedList<AsyncEventSourceClient *>([](AsyncEventSourceClient *c){ delete c; }))
 | 
			
		||||
  , _connectcb(NULL)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
AsyncEventSource::~AsyncEventSource(){
 | 
			
		||||
  close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
 | 
			
		||||
  _connectcb = cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
 | 
			
		||||
  /*char * temp = (char *)malloc(2054);
 | 
			
		||||
  if(temp != NULL){
 | 
			
		||||
    memset(temp+1,' ',2048);
 | 
			
		||||
    temp[0] = ':';
 | 
			
		||||
    temp[2049] = '\r';
 | 
			
		||||
    temp[2050] = '\n';
 | 
			
		||||
    temp[2051] = '\r';
 | 
			
		||||
    temp[2052] = '\n';
 | 
			
		||||
    temp[2053] = 0;
 | 
			
		||||
    client->write((const char *)temp, 2053);
 | 
			
		||||
    free(temp);
 | 
			
		||||
  }*/
 | 
			
		||||
  
 | 
			
		||||
  _clients.add(client);
 | 
			
		||||
  if(_connectcb)
 | 
			
		||||
    _connectcb(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
 | 
			
		||||
  _clients.remove(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::close(){
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected())
 | 
			
		||||
      c->close();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pmb fix
 | 
			
		||||
size_t AsyncEventSource::avgPacketsWaiting() const {
 | 
			
		||||
  if(_clients.isEmpty())
 | 
			
		||||
    return 0;
 | 
			
		||||
  
 | 
			
		||||
  size_t    aql=0;
 | 
			
		||||
  uint32_t  nConnectedClients=0;
 | 
			
		||||
  
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected()) {
 | 
			
		||||
      aql+=c->packetsWaiting();
 | 
			
		||||
      ++nConnectedClients;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
//  return aql / nConnectedClients;
 | 
			
		||||
  return ((aql) + (nConnectedClients/2))/(nConnectedClients); // round up
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  String ev = generateEventMessage(message, event, id, reconnect);
 | 
			
		||||
  for(const auto &c: _clients){
 | 
			
		||||
    if(c->connected()) {
 | 
			
		||||
      c->write(ev.c_str(), ev.length());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSource::count() const {
 | 
			
		||||
  return _clients.count_if([](AsyncEventSourceClient *c){
 | 
			
		||||
    return c->connected();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->method() != HTTP_GET || !request->url().equals(_url)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  request->addInterestingHeader("Last-Event-ID");
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
    return request->requestAuthentication();
 | 
			
		||||
  request->send(new AsyncEventSourceResponse(this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Response
 | 
			
		||||
 | 
			
		||||
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
 | 
			
		||||
  _server = server;
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _contentType = "text/event-stream";
 | 
			
		||||
  _sendContentLength = false;
 | 
			
		||||
  addHeader("Cache-Control", "no-cache");
 | 
			
		||||
  addHeader("Connection","keep-alive");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  String out = _assembleHead(request->version());
 | 
			
		||||
  request->client()->write(out.c_str(), _headLength);
 | 
			
		||||
  _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
 | 
			
		||||
  if(len){
 | 
			
		||||
    new AsyncEventSourceClient(request, _server);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										116
									
								
								lib/ESP Async WebServer/src/AsyncEventSource.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								lib/ESP Async WebServer/src/AsyncEventSource.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. 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 ASYNCEVENTSOURCE_H_
 | 
			
		||||
#define ASYNCEVENTSOURCE_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
class AsyncEventSource;
 | 
			
		||||
class AsyncEventSourceResponse;
 | 
			
		||||
class AsyncEventSourceClient;
 | 
			
		||||
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data; 
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    //size_t _ack;
 | 
			
		||||
    size_t _acked; 
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSourceMessage(const char * data, size_t len);
 | 
			
		||||
    ~AsyncEventSourceMessage();
 | 
			
		||||
    size_t ack(size_t len, uint32_t time __attribute__((unused)));
 | 
			
		||||
    size_t send(AsyncClient *client);
 | 
			
		||||
    bool finished(){ return _acked == _len; }
 | 
			
		||||
    bool sent() { return _sent == _len; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceClient {
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient *_client;
 | 
			
		||||
    AsyncEventSource *_server;
 | 
			
		||||
    uint32_t _lastId;
 | 
			
		||||
    LinkedList<AsyncEventSourceMessage *> _messageQueue;
 | 
			
		||||
    void _queueMessage(AsyncEventSourceMessage *dataMessage);
 | 
			
		||||
    void _runQueue();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
 | 
			
		||||
    ~AsyncEventSourceClient();
 | 
			
		||||
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    void close();
 | 
			
		||||
    void write(const char * message, size_t len);
 | 
			
		||||
    void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
 | 
			
		||||
    bool connected() const { return (_client != NULL) && _client->connected(); }
 | 
			
		||||
    uint32_t lastId() const { return _lastId; }
 | 
			
		||||
    size_t  packetsWaiting() const { return _messageQueue.length(); }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onPoll(); 
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSource: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
    String _url;
 | 
			
		||||
    LinkedList<AsyncEventSourceClient *> _clients;
 | 
			
		||||
    ArEventHandlerFunction _connectcb;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSource(const String& url);
 | 
			
		||||
    ~AsyncEventSource();
 | 
			
		||||
 | 
			
		||||
    const char * url() const { return _url.c_str(); }
 | 
			
		||||
    void close();
 | 
			
		||||
    void onConnect(ArEventHandlerFunction cb);
 | 
			
		||||
    void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
 | 
			
		||||
    size_t count() const; //number clinets connected
 | 
			
		||||
    size_t  avgPacketsWaiting() const;
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _addClient(AsyncEventSourceClient * client);
 | 
			
		||||
    void _handleDisconnect(AsyncEventSourceClient * client);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncEventSourceResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
    AsyncEventSource *_server;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncEventSourceResponse(AsyncEventSource *server);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCEVENTSOURCE_H_ */
 | 
			
		||||
							
								
								
									
										252
									
								
								lib/ESP Async WebServer/src/AsyncJson.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								lib/ESP Async WebServer/src/AsyncJson.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
			
		||||
// AsyncJson.h
 | 
			
		||||
/*
 | 
			
		||||
  Async Response to use with ArduinoJson and AsyncWebServer
 | 
			
		||||
  Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
 | 
			
		||||
 | 
			
		||||
  Example of callback in use
 | 
			
		||||
 | 
			
		||||
   server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
 | 
			
		||||
 | 
			
		||||
    AsyncJsonResponse * response = new AsyncJsonResponse();
 | 
			
		||||
    JsonObject& root = response->getRoot();
 | 
			
		||||
    root["key1"] = "key number one";
 | 
			
		||||
    JsonObject& nested = root.createNestedObject("nested");
 | 
			
		||||
    nested["key1"] = "key number one";
 | 
			
		||||
 | 
			
		||||
    response->setLength();
 | 
			
		||||
    request->send(response);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  --------------------
 | 
			
		||||
 | 
			
		||||
  Async Request to use with ArduinoJson and AsyncWebServer
 | 
			
		||||
  Written by Arsène von Wyss (avonwyss)
 | 
			
		||||
 | 
			
		||||
  Example
 | 
			
		||||
 | 
			
		||||
  AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
 | 
			
		||||
  handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
 | 
			
		||||
    JsonObject& jsonObj = json.as<JsonObject>();
 | 
			
		||||
    // ...
 | 
			
		||||
  });
 | 
			
		||||
  server.addHandler(handler);
 | 
			
		||||
  
 | 
			
		||||
*/
 | 
			
		||||
#ifndef ASYNC_JSON_H_
 | 
			
		||||
#define ASYNC_JSON_H_
 | 
			
		||||
#include <ArduinoJson.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include <Print.h>
 | 
			
		||||
 | 
			
		||||
#if ARDUINOJSON_VERSION_MAJOR == 5
 | 
			
		||||
  #define ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
#else
 | 
			
		||||
  #define DYNAMIC_JSON_DOCUMENT_SIZE  1024
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
constexpr const char* JSON_MIMETYPE = "application/json";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Json Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class ChunkPrint : public Print {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t* _destination;
 | 
			
		||||
    size_t _to_skip;
 | 
			
		||||
    size_t _to_write;
 | 
			
		||||
    size_t _pos;
 | 
			
		||||
  public:
 | 
			
		||||
    ChunkPrint(uint8_t* destination, size_t from, size_t len)
 | 
			
		||||
      : _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
 | 
			
		||||
    virtual ~ChunkPrint(){}
 | 
			
		||||
    size_t write(uint8_t c){
 | 
			
		||||
      if (_to_skip > 0) {
 | 
			
		||||
        _to_skip--;
 | 
			
		||||
        return 1;
 | 
			
		||||
      } else if (_to_write > 0) {
 | 
			
		||||
        _to_write--;
 | 
			
		||||
        _destination[_pos++] = c;
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
    size_t write(const uint8_t *buffer, size_t size)
 | 
			
		||||
    {
 | 
			
		||||
      return this->Print::write(buffer, size);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncJsonResponse: public AsyncAbstractResponse {
 | 
			
		||||
  protected:
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
    DynamicJsonBuffer _jsonBuffer;
 | 
			
		||||
#else
 | 
			
		||||
    DynamicJsonDocument _jsonBuffer;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    JsonVariant _root;
 | 
			
		||||
    bool _isValid;
 | 
			
		||||
 | 
			
		||||
  public:    
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
    AsyncJsonResponse(bool isArray=false): _isValid{false} {
 | 
			
		||||
      _code = 200;
 | 
			
		||||
      _contentType = JSON_MIMETYPE;
 | 
			
		||||
      if(isArray)
 | 
			
		||||
        _root = _jsonBuffer.createArray();
 | 
			
		||||
      else
 | 
			
		||||
        _root = _jsonBuffer.createObject();
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    AsyncJsonResponse(bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
 | 
			
		||||
      _code = 200;
 | 
			
		||||
      _contentType = JSON_MIMETYPE;
 | 
			
		||||
      if(isArray)
 | 
			
		||||
        _root = _jsonBuffer.createNestedArray();
 | 
			
		||||
      else
 | 
			
		||||
        _root = _jsonBuffer.createNestedObject();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    ~AsyncJsonResponse() {}
 | 
			
		||||
    JsonVariant & getRoot() { return _root; }
 | 
			
		||||
    bool _sourceValid() const { return _isValid; }
 | 
			
		||||
    size_t setLength() {
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
      _contentLength = _root.measureLength();
 | 
			
		||||
#else
 | 
			
		||||
      _contentLength = measureJson(_root);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
      if (_contentLength) { _isValid = true; }
 | 
			
		||||
      return _contentLength;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   size_t getSize() { return _jsonBuffer.size(); }
 | 
			
		||||
 | 
			
		||||
    size_t _fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
      ChunkPrint dest(data, _sentLength, len);
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
      _root.printTo( dest ) ;
 | 
			
		||||
#else
 | 
			
		||||
      serializeJson(_root, dest);
 | 
			
		||||
#endif
 | 
			
		||||
      return len;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PrettyAsyncJsonResponse: public AsyncJsonResponse {	
 | 
			
		||||
public:
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
	PrettyAsyncJsonResponse (bool isArray=false) : AsyncJsonResponse{isArray} {}
 | 
			
		||||
#else
 | 
			
		||||
	PrettyAsyncJsonResponse (bool isArray=false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
 | 
			
		||||
#endif
 | 
			
		||||
	size_t setLength () {
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
		_contentLength = _root.measurePrettyLength ();
 | 
			
		||||
#else
 | 
			
		||||
		_contentLength = measureJsonPretty(_root);
 | 
			
		||||
#endif
 | 
			
		||||
		if (_contentLength) {_isValid = true;}
 | 
			
		||||
		return _contentLength;
 | 
			
		||||
	}
 | 
			
		||||
	size_t _fillBuffer (uint8_t *data, size_t len) {
 | 
			
		||||
		ChunkPrint dest (data, _sentLength, len);
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY
 | 
			
		||||
		_root.prettyPrintTo (dest);
 | 
			
		||||
#else
 | 
			
		||||
		serializeJsonPretty(_root, dest);
 | 
			
		||||
#endif
 | 
			
		||||
		return len;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, JsonVariant &json)> ArJsonRequestHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackJsonWebHandler: public AsyncWebHandler {
 | 
			
		||||
private:
 | 
			
		||||
protected:
 | 
			
		||||
  const String _uri;
 | 
			
		||||
  WebRequestMethodComposite _method;
 | 
			
		||||
  ArJsonRequestHandlerFunction _onRequest;
 | 
			
		||||
  size_t _contentLength;
 | 
			
		||||
#ifndef ARDUINOJSON_5_COMPATIBILITY   
 | 
			
		||||
  const size_t maxJsonBufferSize;
 | 
			
		||||
#endif
 | 
			
		||||
  size_t _maxContentLength;
 | 
			
		||||
public:
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY      
 | 
			
		||||
  AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest) 
 | 
			
		||||
  : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
 | 
			
		||||
#else
 | 
			
		||||
  AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize=DYNAMIC_JSON_DOCUMENT_SIZE) 
 | 
			
		||||
  : _uri(uri), _method(HTTP_POST|HTTP_PUT|HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
 | 
			
		||||
#endif
 | 
			
		||||
  
 | 
			
		||||
  void setMethod(WebRequestMethodComposite method){ _method = method; }
 | 
			
		||||
  void setMaxContentLength(int maxContentLength){ _maxContentLength = maxContentLength; }
 | 
			
		||||
  void onRequest(ArJsonRequestHandlerFunction fn){ _onRequest = fn; }
 | 
			
		||||
 | 
			
		||||
  virtual bool canHandle(AsyncWebServerRequest *request) override final{
 | 
			
		||||
    if(!_onRequest)
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if(!(_method & request->method()))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    if ( !request->contentType().equalsIgnoreCase(JSON_MIMETYPE) )
 | 
			
		||||
      return false;
 | 
			
		||||
 | 
			
		||||
    request->addInterestingHeader("ANY");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  virtual void handleRequest(AsyncWebServerRequest *request) override final {
 | 
			
		||||
    if(_onRequest) {
 | 
			
		||||
      if (request->_tempObject != NULL) {
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINOJSON_5_COMPATIBILITY    
 | 
			
		||||
        DynamicJsonBuffer jsonBuffer;
 | 
			
		||||
        JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
 | 
			
		||||
        if (json.success()) {
 | 
			
		||||
#else
 | 
			
		||||
        DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
 | 
			
		||||
        DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
 | 
			
		||||
        if(!error) {
 | 
			
		||||
          JsonVariant json = jsonBuffer.as<JsonVariant>();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
          _onRequest(request, json);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      request->send(_contentLength > _maxContentLength ? 413 : 400);
 | 
			
		||||
    } else {
 | 
			
		||||
      request->send(500);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
 | 
			
		||||
  }
 | 
			
		||||
  virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
 | 
			
		||||
    if (_onRequest) {
 | 
			
		||||
      _contentLength = total;
 | 
			
		||||
      if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
 | 
			
		||||
        request->_tempObject = malloc(total);
 | 
			
		||||
      }
 | 
			
		||||
      if (request->_tempObject != NULL) {
 | 
			
		||||
        memcpy((uint8_t*)(request->_tempObject) + index, data, len);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1299
									
								
								lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1299
									
								
								lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										350
									
								
								lib/ESP Async WebServer/src/AsyncWebSocket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								lib/ESP Async WebServer/src/AsyncWebSocket.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,350 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 ASYNCWEBSOCKET_H_
 | 
			
		||||
#define ASYNCWEBSOCKET_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#define WS_MAX_QUEUED_MESSAGES 32
 | 
			
		||||
#else
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#define WS_MAX_QUEUED_MESSAGES 8
 | 
			
		||||
#endif
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
#include "AsyncWebSynchronization.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
#include <Hash.h>
 | 
			
		||||
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
 | 
			
		||||
#include <../src/Hash.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#define DEFAULT_MAX_WS_CLIENTS 8
 | 
			
		||||
#else
 | 
			
		||||
#define DEFAULT_MAX_WS_CLIENTS 4
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocket;
 | 
			
		||||
class AsyncWebSocketResponse;
 | 
			
		||||
class AsyncWebSocketClient;
 | 
			
		||||
class AsyncWebSocketControl;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    /** Message type as defined by enum AwsFrameType.
 | 
			
		||||
     * Note: Applications will only see WS_TEXT and WS_BINARY.
 | 
			
		||||
     * All other types are handled by the library. */
 | 
			
		||||
    uint8_t message_opcode;
 | 
			
		||||
    /** Frame number of a fragmented message. */
 | 
			
		||||
    uint32_t num;
 | 
			
		||||
    /** Is this the last frame in a fragmented message ?*/
 | 
			
		||||
    uint8_t final;
 | 
			
		||||
    /** Is this frame masked? */
 | 
			
		||||
    uint8_t masked;
 | 
			
		||||
    /** Message type as defined by enum AwsFrameType.
 | 
			
		||||
     * This value is the same as message_opcode for non-fragmented
 | 
			
		||||
     * messages, but may also be WS_CONTINUATION in a fragmented message. */
 | 
			
		||||
    uint8_t opcode;
 | 
			
		||||
    /** Length of the current frame.
 | 
			
		||||
     * This equals the total length of the message if num == 0 && final == true */
 | 
			
		||||
    uint64_t len;
 | 
			
		||||
    /** Mask key */
 | 
			
		||||
    uint8_t mask[4];
 | 
			
		||||
    /** Offset of the data inside the current frame. */
 | 
			
		||||
    uint64_t index;
 | 
			
		||||
} AwsFrameInfo;
 | 
			
		||||
 | 
			
		||||
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
 | 
			
		||||
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
 | 
			
		||||
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
 | 
			
		||||
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMessageBuffer {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    bool _lock; 
 | 
			
		||||
    uint32_t _count;  
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketMessageBuffer();
 | 
			
		||||
    AsyncWebSocketMessageBuffer(size_t size);
 | 
			
		||||
    AsyncWebSocketMessageBuffer(uint8_t * data, size_t size); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer &); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBuffer &&); 
 | 
			
		||||
    ~AsyncWebSocketMessageBuffer(); 
 | 
			
		||||
    void operator ++(int i) { _count++; }
 | 
			
		||||
    void operator --(int i) {  if (_count > 0) { _count--; } ;  }
 | 
			
		||||
    bool reserve(size_t size);
 | 
			
		||||
    void lock() { _lock = true; }
 | 
			
		||||
    void unlock() { _lock = false; }
 | 
			
		||||
    uint8_t * get() { return _data; }
 | 
			
		||||
    size_t length() { return _len; }
 | 
			
		||||
    uint32_t count() { return _count; }
 | 
			
		||||
    bool canDelete() { return (!_count && !_lock); } 
 | 
			
		||||
 | 
			
		||||
    friend AsyncWebSocket; 
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMessage {
 | 
			
		||||
  protected:
 | 
			
		||||
    uint8_t _opcode;
 | 
			
		||||
    bool _mask;
 | 
			
		||||
    AwsMessageStatus _status;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketMessage():_opcode(WS_TEXT),_mask(false),_status(WS_MSG_ERROR){}
 | 
			
		||||
    virtual ~AsyncWebSocketMessage(){}
 | 
			
		||||
    virtual void ack(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))){}
 | 
			
		||||
    virtual size_t send(AsyncClient *client __attribute__((unused))){ return 0; }
 | 
			
		||||
    virtual bool finished(){ return _status != WS_MSG_SENDING; }
 | 
			
		||||
    virtual bool betweenFrames() const { return false; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketBasicMessage: public AsyncWebSocketMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    size_t _ack;
 | 
			
		||||
    size_t _acked;
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
public:
 | 
			
		||||
    AsyncWebSocketBasicMessage(const char * data, size_t len, uint8_t opcode=WS_TEXT, bool mask=false);
 | 
			
		||||
    AsyncWebSocketBasicMessage(uint8_t opcode=WS_TEXT, bool mask=false);
 | 
			
		||||
    virtual ~AsyncWebSocketBasicMessage() override;
 | 
			
		||||
    virtual bool betweenFrames() const override { return _acked == _ack; }
 | 
			
		||||
    virtual void ack(size_t len, uint32_t time) override ;
 | 
			
		||||
    virtual size_t send(AsyncClient *client) override ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketMultiMessage: public AsyncWebSocketMessage {
 | 
			
		||||
  private:
 | 
			
		||||
    uint8_t * _data;
 | 
			
		||||
    size_t _len;
 | 
			
		||||
    size_t _sent;
 | 
			
		||||
    size_t _ack;
 | 
			
		||||
    size_t _acked;
 | 
			
		||||
    AsyncWebSocketMessageBuffer * _WSbuffer; 
 | 
			
		||||
public:
 | 
			
		||||
    AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuffer * buffer, uint8_t opcode=WS_TEXT, bool mask=false); 
 | 
			
		||||
    virtual ~AsyncWebSocketMultiMessage() override;
 | 
			
		||||
    virtual bool betweenFrames() const override { return _acked == _ack; }
 | 
			
		||||
    virtual void ack(size_t len, uint32_t time) override ;
 | 
			
		||||
    virtual size_t send(AsyncClient *client) override ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncWebSocketClient {
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient *_client;
 | 
			
		||||
    AsyncWebSocket *_server;
 | 
			
		||||
    uint32_t _clientId;
 | 
			
		||||
    AwsClientStatus _status;
 | 
			
		||||
 | 
			
		||||
    LinkedList<AsyncWebSocketControl *> _controlQueue;
 | 
			
		||||
    LinkedList<AsyncWebSocketMessage *> _messageQueue;
 | 
			
		||||
 | 
			
		||||
    uint8_t _pstate;
 | 
			
		||||
    AwsFrameInfo _pinfo;
 | 
			
		||||
 | 
			
		||||
    uint32_t _lastMessageTime;
 | 
			
		||||
    uint32_t _keepAlivePeriod;
 | 
			
		||||
 | 
			
		||||
    void _queueMessage(AsyncWebSocketMessage *dataMessage);
 | 
			
		||||
    void _queueControl(AsyncWebSocketControl *controlMessage);
 | 
			
		||||
    void _runQueue();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    void *_tempObject;
 | 
			
		||||
 | 
			
		||||
    AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
 | 
			
		||||
    ~AsyncWebSocketClient();
 | 
			
		||||
 | 
			
		||||
    //client id increments for the given server
 | 
			
		||||
    uint32_t id(){ return _clientId; }
 | 
			
		||||
    AwsClientStatus status(){ return _status; }
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    AsyncWebSocket *server(){ return _server; }
 | 
			
		||||
    AwsFrameInfo const &pinfo() const { return _pinfo; }
 | 
			
		||||
 | 
			
		||||
    IPAddress remoteIP();
 | 
			
		||||
    uint16_t  remotePort();
 | 
			
		||||
 | 
			
		||||
    //control frames
 | 
			
		||||
    void close(uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void ping(uint8_t *data=NULL, size_t len=0);
 | 
			
		||||
 | 
			
		||||
    //set auto-ping period in seconds. disabled if zero (default)
 | 
			
		||||
    void keepAlivePeriod(uint16_t seconds){
 | 
			
		||||
      _keepAlivePeriod = seconds * 1000;
 | 
			
		||||
    }
 | 
			
		||||
    uint16_t keepAlivePeriod(){
 | 
			
		||||
      return (uint16_t)(_keepAlivePeriod / 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //data packets
 | 
			
		||||
    void message(AsyncWebSocketMessage *message){ _queueMessage(message); }
 | 
			
		||||
    bool queueIsFull();
 | 
			
		||||
 | 
			
		||||
    size_t printf(const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#ifndef ESP32
 | 
			
		||||
    size_t printf_P(PGM_P formatP, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#endif
 | 
			
		||||
    void text(const char * message, size_t len);
 | 
			
		||||
    void text(const char * message);
 | 
			
		||||
    void text(uint8_t * message, size_t len);
 | 
			
		||||
    void text(char * message);
 | 
			
		||||
    void text(const String &message);
 | 
			
		||||
    void text(const __FlashStringHelper *data);
 | 
			
		||||
    void text(AsyncWebSocketMessageBuffer *buffer); 
 | 
			
		||||
 | 
			
		||||
    void binary(const char * message, size_t len);
 | 
			
		||||
    void binary(const char * message);
 | 
			
		||||
    void binary(uint8_t * message, size_t len);
 | 
			
		||||
    void binary(char * message);
 | 
			
		||||
    void binary(const String &message);
 | 
			
		||||
    void binary(const __FlashStringHelper *data, size_t len);
 | 
			
		||||
    void binary(AsyncWebSocketMessageBuffer *buffer); 
 | 
			
		||||
 | 
			
		||||
    bool canSend() { return _messageQueue.length() < WS_MAX_QUEUED_MESSAGES; }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onError(int8_t);
 | 
			
		||||
    void _onPoll();
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
    void _onData(void *pbuf, size_t plen);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;
 | 
			
		||||
 | 
			
		||||
//WebServer Handler implementation that plays the role of a socket server
 | 
			
		||||
class AsyncWebSocket: public AsyncWebHandler {
 | 
			
		||||
  public:
 | 
			
		||||
    typedef LinkedList<AsyncWebSocketClient *> AsyncWebSocketClientLinkedList;
 | 
			
		||||
  private:
 | 
			
		||||
    String _url;
 | 
			
		||||
    AsyncWebSocketClientLinkedList _clients;
 | 
			
		||||
    uint32_t _cNextId;
 | 
			
		||||
    AwsEventHandler _eventHandler;
 | 
			
		||||
    bool _enabled;
 | 
			
		||||
    AsyncWebLock _lock;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocket(const String& url);
 | 
			
		||||
    ~AsyncWebSocket();
 | 
			
		||||
    const char * url() const { return _url.c_str(); }
 | 
			
		||||
    void enable(bool e){ _enabled = e; }
 | 
			
		||||
    bool enabled() const { return _enabled; }
 | 
			
		||||
    bool availableForWriteAll();
 | 
			
		||||
    bool availableForWrite(uint32_t id);
 | 
			
		||||
 | 
			
		||||
    size_t count() const;
 | 
			
		||||
    AsyncWebSocketClient * client(uint32_t id);
 | 
			
		||||
    bool hasClient(uint32_t id){ return client(id) != NULL; }
 | 
			
		||||
 | 
			
		||||
    void close(uint32_t id, uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void closeAll(uint16_t code=0, const char * message=NULL);
 | 
			
		||||
    void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
 | 
			
		||||
 | 
			
		||||
    void ping(uint32_t id, uint8_t *data=NULL, size_t len=0);
 | 
			
		||||
    void pingAll(uint8_t *data=NULL, size_t len=0); //  done
 | 
			
		||||
 | 
			
		||||
    void text(uint32_t id, const char * message, size_t len);
 | 
			
		||||
    void text(uint32_t id, const char * message);
 | 
			
		||||
    void text(uint32_t id, uint8_t * message, size_t len);
 | 
			
		||||
    void text(uint32_t id, char * message);
 | 
			
		||||
    void text(uint32_t id, const String &message);
 | 
			
		||||
    void text(uint32_t id, const __FlashStringHelper *message);
 | 
			
		||||
 | 
			
		||||
    void textAll(const char * message, size_t len);
 | 
			
		||||
    void textAll(const char * message);
 | 
			
		||||
    void textAll(uint8_t * message, size_t len);
 | 
			
		||||
    void textAll(char * message);
 | 
			
		||||
    void textAll(const String &message);
 | 
			
		||||
    void textAll(const __FlashStringHelper *message); //  need to convert
 | 
			
		||||
    void textAll(AsyncWebSocketMessageBuffer * buffer); 
 | 
			
		||||
 | 
			
		||||
    void binary(uint32_t id, const char * message, size_t len);
 | 
			
		||||
    void binary(uint32_t id, const char * message);
 | 
			
		||||
    void binary(uint32_t id, uint8_t * message, size_t len);
 | 
			
		||||
    void binary(uint32_t id, char * message);
 | 
			
		||||
    void binary(uint32_t id, const String &message);
 | 
			
		||||
    void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
 | 
			
		||||
 | 
			
		||||
    void binaryAll(const char * message, size_t len);
 | 
			
		||||
    void binaryAll(const char * message);
 | 
			
		||||
    void binaryAll(uint8_t * message, size_t len);
 | 
			
		||||
    void binaryAll(char * message);
 | 
			
		||||
    void binaryAll(const String &message);
 | 
			
		||||
    void binaryAll(const __FlashStringHelper *message, size_t len);
 | 
			
		||||
    void binaryAll(AsyncWebSocketMessageBuffer * buffer); 
 | 
			
		||||
 | 
			
		||||
    void message(uint32_t id, AsyncWebSocketMessage *message);
 | 
			
		||||
    void messageAll(AsyncWebSocketMultiMessage *message);
 | 
			
		||||
 | 
			
		||||
    size_t printf(uint32_t id, const char *format, ...)  __attribute__ ((format (printf, 3, 4)));
 | 
			
		||||
    size_t printfAll(const char *format, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
#ifndef ESP32
 | 
			
		||||
    size_t printf_P(uint32_t id, PGM_P formatP, ...)  __attribute__ ((format (printf, 3, 4)));
 | 
			
		||||
#endif
 | 
			
		||||
    size_t printfAll_P(PGM_P formatP, ...)  __attribute__ ((format (printf, 2, 3)));
 | 
			
		||||
 | 
			
		||||
    //event listener
 | 
			
		||||
    void onEvent(AwsEventHandler handler){
 | 
			
		||||
      _eventHandler = handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //system callbacks (do not call)
 | 
			
		||||
    uint32_t _getNextId(){ return _cNextId++; }
 | 
			
		||||
    void _addClient(AsyncWebSocketClient * client);
 | 
			
		||||
    void _handleDisconnect(AsyncWebSocketClient * client);
 | 
			
		||||
    void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //  messagebuffer functions/objects. 
 | 
			
		||||
    AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0); 
 | 
			
		||||
    AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size); 
 | 
			
		||||
    LinkedList<AsyncWebSocketMessageBuffer *> _buffers;
 | 
			
		||||
    void _cleanBuffers(); 
 | 
			
		||||
 | 
			
		||||
    AsyncWebSocketClientLinkedList getClients() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//WebServer response to authenticate the socket and detach the tcp client from the web server request
 | 
			
		||||
class AsyncWebSocketResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
    AsyncWebSocket *_server;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSOCKET_H_ */
 | 
			
		||||
							
								
								
									
										87
									
								
								lib/ESP Async WebServer/src/AsyncWebSynchronization.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								lib/ESP Async WebServer/src/AsyncWebSynchronization.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
#ifndef ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
#define ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
 | 
			
		||||
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
 | 
			
		||||
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
 | 
			
		||||
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
 | 
			
		||||
class AsyncWebLock
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  SemaphoreHandle_t _lock;
 | 
			
		||||
  mutable void *_lockedBy;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLock() {
 | 
			
		||||
    _lock = xSemaphoreCreateBinary();
 | 
			
		||||
    _lockedBy = NULL;
 | 
			
		||||
    xSemaphoreGive(_lock);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLock() {
 | 
			
		||||
    vSemaphoreDelete(_lock);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool lock() const {
 | 
			
		||||
    extern void *pxCurrentTCB;
 | 
			
		||||
    if (_lockedBy != pxCurrentTCB) {
 | 
			
		||||
      xSemaphoreTake(_lock, portMAX_DELAY);
 | 
			
		||||
      _lockedBy = pxCurrentTCB;
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void unlock() const {
 | 
			
		||||
    _lockedBy = NULL;
 | 
			
		||||
    xSemaphoreGive(_lock);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
// This is the 8266 version of the Sync Lock which is currently unimplemented
 | 
			
		||||
class AsyncWebLock
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLock() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLock() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool lock() const {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void unlock() const {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class AsyncWebLockGuard
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
  const AsyncWebLock *_lock;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  AsyncWebLockGuard(const AsyncWebLock &l) {
 | 
			
		||||
    if (l.lock()) {
 | 
			
		||||
      _lock = &l;
 | 
			
		||||
    } else {
 | 
			
		||||
      _lock = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~AsyncWebLockGuard() {
 | 
			
		||||
    if (_lock) {
 | 
			
		||||
      _lock->unlock();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // ASYNCWEBSYNCHRONIZATION_H_
 | 
			
		||||
							
								
								
									
										465
									
								
								lib/ESP Async WebServer/src/ESPAsyncWebServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								lib/ESP Async WebServer/src/ESPAsyncWebServer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,465 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 _ESPAsyncWebServer_H_
 | 
			
		||||
#define _ESPAsyncWebServer_H_
 | 
			
		||||
 | 
			
		||||
#include "Arduino.h"
 | 
			
		||||
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include "FS.h"
 | 
			
		||||
 | 
			
		||||
#include "StringArray.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#elif defined(ESP8266)
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#else
 | 
			
		||||
#error Platform not supported
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define DEBUGF(...) //Serial.printf(__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
class AsyncWebServer;
 | 
			
		||||
class AsyncWebServerRequest;
 | 
			
		||||
class AsyncWebServerResponse;
 | 
			
		||||
class AsyncWebHeader;
 | 
			
		||||
class AsyncWebParameter;
 | 
			
		||||
class AsyncWebRewrite;
 | 
			
		||||
class AsyncWebHandler;
 | 
			
		||||
class AsyncStaticWebHandler;
 | 
			
		||||
class AsyncCallbackWebHandler;
 | 
			
		||||
class AsyncResponseStream;
 | 
			
		||||
 | 
			
		||||
#ifndef WEBSERVER_H
 | 
			
		||||
typedef enum {
 | 
			
		||||
  HTTP_GET     = 0b00000001,
 | 
			
		||||
  HTTP_POST    = 0b00000010,
 | 
			
		||||
  HTTP_DELETE  = 0b00000100,
 | 
			
		||||
  HTTP_PUT     = 0b00001000,
 | 
			
		||||
  HTTP_PATCH   = 0b00010000,
 | 
			
		||||
  HTTP_HEAD    = 0b00100000,
 | 
			
		||||
  HTTP_OPTIONS = 0b01000000,
 | 
			
		||||
  HTTP_ANY     = 0b01111111,
 | 
			
		||||
} WebRequestMethod;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
 | 
			
		||||
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
 | 
			
		||||
 | 
			
		||||
typedef uint8_t WebRequestMethodComposite;
 | 
			
		||||
typedef std::function<void(void)> ArDisconnectHandler;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PARAMETER :: Chainable object to hold GET/POST and FILE parameters
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebParameter {
 | 
			
		||||
  private:
 | 
			
		||||
    String _name;
 | 
			
		||||
    String _value;
 | 
			
		||||
    size_t _size;
 | 
			
		||||
    bool _isForm;
 | 
			
		||||
    bool _isFile;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
 | 
			
		||||
    const String& name() const { return _name; }
 | 
			
		||||
    const String& value() const { return _value; }
 | 
			
		||||
    size_t size() const { return _size; }
 | 
			
		||||
    bool isPost() const { return _isForm; }
 | 
			
		||||
    bool isFile() const { return _isFile; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * HEADER :: Chainable object to hold the headers
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebHeader {
 | 
			
		||||
  private:
 | 
			
		||||
    String _name;
 | 
			
		||||
    String _value;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
 | 
			
		||||
    AsyncWebHeader(const String& data): _name(), _value(){
 | 
			
		||||
      if(!data) return;
 | 
			
		||||
      int index = data.indexOf(':');
 | 
			
		||||
      if (index < 0) return;
 | 
			
		||||
      _name = data.substring(0, index);
 | 
			
		||||
      _value = data.substring(index + 2);
 | 
			
		||||
    }
 | 
			
		||||
    ~AsyncWebHeader(){}
 | 
			
		||||
    const String& name() const { return _name; }
 | 
			
		||||
    const String& value() const { return _value; }
 | 
			
		||||
    String toString() const { return String(_name+": "+_value+"\r\n"); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
 | 
			
		||||
 | 
			
		||||
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
 | 
			
		||||
typedef std::function<String(const String&)> AwsTemplateProcessor;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServerRequest {
 | 
			
		||||
  using File = fs::File;
 | 
			
		||||
  using FS = fs::FS;
 | 
			
		||||
  friend class AsyncWebServer;
 | 
			
		||||
  friend class AsyncCallbackWebHandler;
 | 
			
		||||
  private:
 | 
			
		||||
    AsyncClient* _client;
 | 
			
		||||
    AsyncWebServer* _server;
 | 
			
		||||
    AsyncWebHandler* _handler;
 | 
			
		||||
    AsyncWebServerResponse* _response;
 | 
			
		||||
    StringArray _interestingHeaders;
 | 
			
		||||
    ArDisconnectHandler _onDisconnectfn;
 | 
			
		||||
 | 
			
		||||
    String _temp;
 | 
			
		||||
    uint8_t _parseState;
 | 
			
		||||
 | 
			
		||||
    uint8_t _version;
 | 
			
		||||
    WebRequestMethodComposite _method;
 | 
			
		||||
    String _url;
 | 
			
		||||
    String _host;
 | 
			
		||||
    String _contentType;
 | 
			
		||||
    String _boundary;
 | 
			
		||||
    String _authorization;
 | 
			
		||||
    RequestedConnectionType _reqconntype;
 | 
			
		||||
    void _removeNotInterestingHeaders();
 | 
			
		||||
    bool _isDigest;
 | 
			
		||||
    bool _isMultipart;
 | 
			
		||||
    bool _isPlainPost;
 | 
			
		||||
    bool _expectingContinue;
 | 
			
		||||
    size_t _contentLength;
 | 
			
		||||
    size_t _parsedLength;
 | 
			
		||||
 | 
			
		||||
    LinkedList<AsyncWebHeader *> _headers;
 | 
			
		||||
    LinkedList<AsyncWebParameter *> _params;
 | 
			
		||||
    LinkedList<String *> _pathParams;
 | 
			
		||||
 | 
			
		||||
    uint8_t _multiParseState;
 | 
			
		||||
    uint8_t _boundaryPosition;
 | 
			
		||||
    size_t _itemStartIndex;
 | 
			
		||||
    size_t _itemSize;
 | 
			
		||||
    String _itemName;
 | 
			
		||||
    String _itemFilename;
 | 
			
		||||
    String _itemType;
 | 
			
		||||
    String _itemValue;
 | 
			
		||||
    uint8_t *_itemBuffer;
 | 
			
		||||
    size_t _itemBufferIndex;
 | 
			
		||||
    bool _itemIsFile;
 | 
			
		||||
 | 
			
		||||
    void _onPoll();
 | 
			
		||||
    void _onAck(size_t len, uint32_t time);
 | 
			
		||||
    void _onError(int8_t error);
 | 
			
		||||
    void _onTimeout(uint32_t time);
 | 
			
		||||
    void _onDisconnect();
 | 
			
		||||
    void _onData(void *buf, size_t len);
 | 
			
		||||
 | 
			
		||||
    void _addParam(AsyncWebParameter*);
 | 
			
		||||
    void _addPathParam(const char *param);
 | 
			
		||||
 | 
			
		||||
    bool _parseReqHead();
 | 
			
		||||
    bool _parseReqHeader();
 | 
			
		||||
    void _parseLine();
 | 
			
		||||
    void _parsePlainPostChar(uint8_t data);
 | 
			
		||||
    void _parseMultipartPostByte(uint8_t data, bool last);
 | 
			
		||||
    void _addGetParams(const String& params);
 | 
			
		||||
 | 
			
		||||
    void _handleUploadStart();
 | 
			
		||||
    void _handleUploadByte(uint8_t data, bool last);
 | 
			
		||||
    void _handleUploadEnd();
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    File _tempFile;
 | 
			
		||||
    void *_tempObject;
 | 
			
		||||
 | 
			
		||||
    AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
 | 
			
		||||
    ~AsyncWebServerRequest();
 | 
			
		||||
 | 
			
		||||
    AsyncClient* client(){ return _client; }
 | 
			
		||||
    uint8_t version() const { return _version; }
 | 
			
		||||
    WebRequestMethodComposite method() const { return _method; }
 | 
			
		||||
    const String& url() const { return _url; }
 | 
			
		||||
    const String& host() const { return _host; }
 | 
			
		||||
    const String& contentType() const { return _contentType; }
 | 
			
		||||
    size_t contentLength() const { return _contentLength; }
 | 
			
		||||
    bool multipart() const { return _isMultipart; }
 | 
			
		||||
    const char * methodToString() const;
 | 
			
		||||
    const char * requestedConnTypeToString() const;
 | 
			
		||||
    RequestedConnectionType requestedConnType() const { return _reqconntype; }
 | 
			
		||||
    bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
 | 
			
		||||
    void onDisconnect (ArDisconnectHandler fn);
 | 
			
		||||
 | 
			
		||||
    //hash is the string representation of:
 | 
			
		||||
    // base64(user:pass) for basic or
 | 
			
		||||
    // user:realm:md5(user:realm:pass) for digest
 | 
			
		||||
    bool authenticate(const char * hash);
 | 
			
		||||
    bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
 | 
			
		||||
    void requestAuthentication(const char * realm = NULL, bool isDigest = true);
 | 
			
		||||
 | 
			
		||||
    void setHandler(AsyncWebHandler *handler){ _handler = handler; }
 | 
			
		||||
    void addInterestingHeader(const String& name);
 | 
			
		||||
 | 
			
		||||
    void redirect(const String& url);
 | 
			
		||||
 | 
			
		||||
    void send(AsyncWebServerResponse *response);
 | 
			
		||||
    void send(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
 | 
			
		||||
    size_t headers() const;                     // get header count
 | 
			
		||||
    bool hasHeader(const String& name) const;   // check if header exists
 | 
			
		||||
    bool hasHeader(const __FlashStringHelper * data) const;   // check if header exists
 | 
			
		||||
 | 
			
		||||
    AsyncWebHeader* getHeader(const String& name) const;
 | 
			
		||||
    AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
 | 
			
		||||
    AsyncWebHeader* getHeader(size_t num) const;
 | 
			
		||||
 | 
			
		||||
    size_t params() const;                      // get arguments count
 | 
			
		||||
    bool hasParam(const String& name, bool post=false, bool file=false) const;
 | 
			
		||||
    bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
 | 
			
		||||
 | 
			
		||||
    AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
 | 
			
		||||
    AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; 
 | 
			
		||||
    AsyncWebParameter* getParam(size_t num) const;
 | 
			
		||||
 | 
			
		||||
    size_t args() const { return params(); }     // get arguments count
 | 
			
		||||
    const String& arg(const String& name) const; // get request argument value by name
 | 
			
		||||
    const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)    
 | 
			
		||||
    const String& arg(size_t i) const;           // get request argument value by number
 | 
			
		||||
    const String& argName(size_t i) const;       // get request argument name by number
 | 
			
		||||
    bool hasArg(const char* name) const;         // check if argument exists
 | 
			
		||||
    bool hasArg(const __FlashStringHelper * data) const;         // check if F(argument) exists
 | 
			
		||||
 | 
			
		||||
    const String& pathArg(size_t i) const;
 | 
			
		||||
 | 
			
		||||
    const String& header(const char* name) const;// get request header value by name
 | 
			
		||||
    const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)    
 | 
			
		||||
    const String& header(size_t i) const;        // get request header value by number
 | 
			
		||||
    const String& headerName(size_t i) const;    // get request header name by number
 | 
			
		||||
    String urlDecode(const String& text) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;
 | 
			
		||||
 | 
			
		||||
bool ON_STA_FILTER(AsyncWebServerRequest *request);
 | 
			
		||||
 | 
			
		||||
bool ON_AP_FILTER(AsyncWebServerRequest *request);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * REWRITE :: One instance can be handle any Request (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebRewrite {
 | 
			
		||||
  protected:
 | 
			
		||||
    String _from;
 | 
			
		||||
    String _toUrl;
 | 
			
		||||
    String _params;
 | 
			
		||||
    ArRequestFilterFunction _filter;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
 | 
			
		||||
      int index = _toUrl.indexOf('?');
 | 
			
		||||
      if (index > 0) {
 | 
			
		||||
        _params = _toUrl.substring(index +1);
 | 
			
		||||
        _toUrl = _toUrl.substring(0, index);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    virtual ~AsyncWebRewrite(){}
 | 
			
		||||
    AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
 | 
			
		||||
    bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
 | 
			
		||||
    const String& from(void) const { return _from; }
 | 
			
		||||
    const String& toUrl(void) const { return _toUrl; }
 | 
			
		||||
    const String& params(void) const { return _params; }
 | 
			
		||||
    virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * HANDLER :: One instance can be attached to any Request (done by the Server)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
class AsyncWebHandler {
 | 
			
		||||
  protected:
 | 
			
		||||
    ArRequestFilterFunction _filter;
 | 
			
		||||
    String _username;
 | 
			
		||||
    String _password;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebHandler():_username(""), _password(""){}
 | 
			
		||||
    AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
 | 
			
		||||
    AsyncWebHandler& setAuthentication(const char *username, const char *password){  _username = String(username);_password = String(password); return *this; };
 | 
			
		||||
    bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
 | 
			
		||||
    virtual ~AsyncWebHandler(){}
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request  __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final  __attribute__((unused))){}
 | 
			
		||||
    virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
 | 
			
		||||
    virtual bool isRequestHandlerTrivial(){return true;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * RESPONSE :: One instance is created for each Request (attached by the Handler)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
 | 
			
		||||
} WebResponseState;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServerResponse {
 | 
			
		||||
  protected:
 | 
			
		||||
    int _code;
 | 
			
		||||
    LinkedList<AsyncWebHeader *> _headers;
 | 
			
		||||
    String _contentType;
 | 
			
		||||
    size_t _contentLength;
 | 
			
		||||
    bool _sendContentLength;
 | 
			
		||||
    bool _chunked;
 | 
			
		||||
    size_t _headLength;
 | 
			
		||||
    size_t _sentLength;
 | 
			
		||||
    size_t _ackedLength;
 | 
			
		||||
    size_t _writtenLength;
 | 
			
		||||
    WebResponseState _state;
 | 
			
		||||
    const char* _responseCodeToString(int code);
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebServerResponse();
 | 
			
		||||
    virtual ~AsyncWebServerResponse();
 | 
			
		||||
    virtual void setCode(int code);
 | 
			
		||||
    virtual void setContentLength(size_t len);
 | 
			
		||||
    virtual void setContentType(const String& type);
 | 
			
		||||
    virtual void addHeader(const String& name, const String& value);
 | 
			
		||||
    virtual String _assembleHead(uint8_t version);
 | 
			
		||||
    virtual bool _started() const;
 | 
			
		||||
    virtual bool _finished() const;
 | 
			
		||||
    virtual bool _failed() const;
 | 
			
		||||
    virtual bool _sourceValid() const;
 | 
			
		||||
    virtual void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SERVER :: One instance
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
 | 
			
		||||
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
 | 
			
		||||
 | 
			
		||||
class AsyncWebServer {
 | 
			
		||||
  protected:
 | 
			
		||||
    AsyncServer _server;
 | 
			
		||||
    LinkedList<AsyncWebRewrite*> _rewrites;
 | 
			
		||||
    LinkedList<AsyncWebHandler*> _handlers;
 | 
			
		||||
    AsyncCallbackWebHandler* _catchAllHandler;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncWebServer(uint16_t port);
 | 
			
		||||
    ~AsyncWebServer();
 | 
			
		||||
 | 
			
		||||
    void begin();
 | 
			
		||||
    void end();
 | 
			
		||||
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
    void onSslFileRequest(AcSSlFileHandler cb, void* arg);
 | 
			
		||||
    void beginSecure(const char *cert, const char *private_key_file, const char *password);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
 | 
			
		||||
    bool removeRewrite(AsyncWebRewrite* rewrite);
 | 
			
		||||
    AsyncWebRewrite& rewrite(const char* from, const char* to);
 | 
			
		||||
 | 
			
		||||
    AsyncWebHandler& addHandler(AsyncWebHandler* handler);
 | 
			
		||||
    bool removeHandler(AsyncWebHandler* handler);
 | 
			
		||||
  
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
 | 
			
		||||
    AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
 | 
			
		||||
 | 
			
		||||
    AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
 | 
			
		||||
 | 
			
		||||
    void onNotFound(ArRequestHandlerFunction fn);  //called when handler is not assigned
 | 
			
		||||
    void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
 | 
			
		||||
    void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
 | 
			
		||||
 | 
			
		||||
    void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody 
 | 
			
		||||
  
 | 
			
		||||
    void _handleDisconnect(AsyncWebServerRequest *request);
 | 
			
		||||
    void _attachHandler(AsyncWebServerRequest *request);
 | 
			
		||||
    void _rewriteRequest(AsyncWebServerRequest *request);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DefaultHeaders {
 | 
			
		||||
  using headers_t = LinkedList<AsyncWebHeader *>;
 | 
			
		||||
  headers_t _headers;
 | 
			
		||||
  
 | 
			
		||||
  DefaultHeaders()
 | 
			
		||||
  :_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
 | 
			
		||||
  {}
 | 
			
		||||
public:
 | 
			
		||||
  using ConstIterator = headers_t::ConstIterator;
 | 
			
		||||
 | 
			
		||||
  void addHeader(const String& name, const String& value){
 | 
			
		||||
    _headers.add(new AsyncWebHeader(name, value));
 | 
			
		||||
  }  
 | 
			
		||||
  
 | 
			
		||||
  ConstIterator begin() const { return _headers.begin(); }
 | 
			
		||||
  ConstIterator end() const { return _headers.end(); }
 | 
			
		||||
 | 
			
		||||
  DefaultHeaders(DefaultHeaders const &) = delete;
 | 
			
		||||
  DefaultHeaders &operator=(DefaultHeaders const &) = delete;
 | 
			
		||||
  static DefaultHeaders &Instance() {
 | 
			
		||||
    static DefaultHeaders instance;
 | 
			
		||||
    return instance;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "WebResponseImpl.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
#include "AsyncWebSocket.h"
 | 
			
		||||
#include "AsyncEventSource.h"
 | 
			
		||||
 | 
			
		||||
#endif /* _AsyncWebServer_H_ */
 | 
			
		||||
							
								
								
									
										544
									
								
								lib/ESP Async WebServer/src/SPIFFSEditor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										544
									
								
								lib/ESP Async WebServer/src/SPIFFSEditor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,544 @@
 | 
			
		||||
#include "SPIFFSEditor.h"
 | 
			
		||||
#include <FS.h>
 | 
			
		||||
 | 
			
		||||
//File: edit.htm.gz, Size: 4151
 | 
			
		||||
#define edit_htm_gz_len 4151
 | 
			
		||||
const uint8_t edit_htm_gz[] PROGMEM = {
 | 
			
		||||
 0x1F, 0x8B, 0x08, 0x08, 0xB8, 0x94, 0xB1, 0x59, 0x00, 0x03, 0x65, 0x64, 0x69, 0x74, 0x2E, 0x68,
 | 
			
		||||
 0x74, 0x6D, 0x00, 0xB5, 0x3A, 0x0B, 0x7B, 0xDA, 0xB8, 0xB2, 0x7F, 0xC5, 0x71, 0xCF, 0x66, 0xED,
 | 
			
		||||
 0x83, 0x31, 0x90, 0xA4, 0xD9, 0xD6, 0xC4, 0xC9, 0x42, 0x92, 0x36, 0x6D, 0xF3, 0x6A, 0x80, 0xB6,
 | 
			
		||||
 0x69, 0x4F, 0xEE, 0x7E, 0xC2, 0x16, 0xA0, 0xC6, 0x96, 0x5D, 0x5B, 0x0E, 0x49, 0x59, 0xFE, 0xFB,
 | 
			
		||||
 0x9D, 0x91, 0x6C, 0xB0, 0x09, 0x69, 0x77, 0xCF, 0xBD, 0xBB, 0xDD, 0x2D, 0x92, 0x46, 0x33, 0x9A,
 | 
			
		||||
 0x19, 0xCD, 0x53, 0xDE, 0xBD, 0x8D, 0xA3, 0x8B, 0xC3, 0xFE, 0xF5, 0xE5, 0xB1, 0x36, 0x11, 0x61,
 | 
			
		||||
 0xB0, 0xBF, 0x87, 0x7F, 0x6B, 0x01, 0xE1, 0x63, 0x97, 0xF2, 0xFD, 0x3D, 0xC1, 0x44, 0x40, 0xF7,
 | 
			
		||||
 0x8F, 0x7B, 0x97, 0xDA, 0xB1, 0xCF, 0x44, 0x94, 0xEC, 0x35, 0xD4, 0xCA, 0x5E, 0x2A, 0x1E, 0x02,
 | 
			
		||||
 0xAA, 0x85, 0xD4, 0x67, 0xC4, 0x4D, 0xBD, 0x84, 0xC2, 0x66, 0xDB, 0x0B, 0x67, 0xDF, 0xEB, 0x8C,
 | 
			
		||||
 0xFB, 0xF4, 0xDE, 0xD9, 0x6E, 0x36, 0xDB, 0x71, 0x94, 0x32, 0xC1, 0x22, 0xEE, 0x90, 0x61, 0x1A,
 | 
			
		||||
 0x05, 0x99, 0xA0, 0xED, 0x80, 0x8E, 0x84, 0xF3, 0x3C, 0xBE, 0x6F, 0x0F, 0xA3, 0xC4, 0xA7, 0x89,
 | 
			
		||||
 0xD3, 0x8A, 0xEF, 0x35, 0x00, 0x31, 0x5F, 0x7B, 0xB6, 0xB3, 0xB3, 0xD3, 0x1E, 0x12, 0xEF, 0x76,
 | 
			
		||||
 0x9C, 0x44, 0x19, 0xF7, 0xEB, 0x5E, 0x14, 0x44, 0x89, 0xF3, 0x6C, 0xF4, 0x1C, 0xFF, 0xB4, 0x7D,
 | 
			
		||||
 0x96, 0xC6, 0x01, 0x79, 0x70, 0x78, 0xC4, 0x29, 0xE0, 0xDE, 0xD7, 0xD3, 0x09, 0xF1, 0xA3, 0xA9,
 | 
			
		||||
 0xD3, 0xD4, 0x9A, 0x5A, 0xAB, 0x09, 0x44, 0x92, 0xF1, 0x90, 0x18, 0x4D, 0x0B, 0xFF, 0xD8, 0x3B,
 | 
			
		||||
 0x66, 0x7B, 0x14, 0x71, 0x51, 0x4F, 0xD9, 0x77, 0xEA, 0xB4, 0xB6, 0xE0, 0x34, 0x39, 0x1D, 0x91,
 | 
			
		||||
 0x90, 0x05, 0x0F, 0x4E, 0x4A, 0x78, 0x5A, 0x4F, 0x69, 0xC2, 0x46, 0x6A, 0x79, 0x4A, 0xD9, 0x78,
 | 
			
		||||
 0x22, 0x9C, 0xDF, 0x9A, 0xCD, 0x39, 0xF0, 0xAF, 0x65, 0xC1, 0x2C, 0x60, 0x29, 0x20, 0xA3, 0x78,
 | 
			
		||||
 0xEA, 0x3C, 0x11, 0xC5, 0x4E, 0x53, 0xB1, 0xDE, 0x6C, 0x87, 0x24, 0x19, 0x33, 0x0E, 0x83, 0x98,
 | 
			
		||||
 0xF8, 0x3E, 0xE3, 0x63, 0x47, 0xA1, 0x05, 0x6C, 0xB6, 0x90, 0x36, 0xA1, 0x01, 0x11, 0xEC, 0x8E,
 | 
			
		||||
 0xB6, 0x43, 0xC6, 0xEB, 0x53, 0xE6, 0x8B, 0x89, 0xB3, 0x0B, 0x3C, 0xB6, 0xBD, 0x2C, 0x49, 0x41,
 | 
			
		||||
 0xA6, 0x38, 0x62, 0x5C, 0xD0, 0x44, 0xA2, 0xA5, 0x31, 0xE1, 0xB3, 0x5C, 0x54, 0x54, 0x40, 0x21,
 | 
			
		||||
 0x27, 0xE3, 0x01, 0xE3, 0xB4, 0x3E, 0x0C, 0x22, 0xEF, 0x76, 0x71, 0xD2, 0x6E, 0x7C, 0x9F, 0x9F,
 | 
			
		||||
 0xE5, 0x4C, 0xA2, 0x3B, 0x9A, 0xCC, 0x96, 0xEA, 0x92, 0xD8, 0x15, 0x60, 0x85, 0x34, 0xA5, 0x74,
 | 
			
		||||
 0x6E, 0x8B, 0xBB, 0x0C, 0xA0, 0x96, 0xFC, 0x05, 0x29, 0x17, 0xFC, 0x2F, 0x45, 0x5A, 0x11, 0x5C,
 | 
			
		||||
 0xA1, 0x30, 0x1E, 0x67, 0x62, 0xF6, 0xF8, 0x2A, 0xA3, 0x98, 0x78, 0x4C, 0x3C, 0xA0, 0xFC, 0xB0,
 | 
			
		||||
 0x6D, 0x86, 0xBA, 0x04, 0xAC, 0x24, 0x24, 0x81, 0x86, 0x3A, 0xD7, 0x3E, 0xD0, 0xC4, 0x27, 0x9C,
 | 
			
		||||
 0x58, 0x9D, 0x84, 0x91, 0xC0, 0xEA, 0x2D, 0xB5, 0x5E, 0x0F, 0xA3, 0xEF, 0xF5, 0x0C, 0xC6, 0x30,
 | 
			
		||||
 0x0F, 0xA8, 0x27, 0x94, 0x92, 0xE1, 0x1E, 0x86, 0xB7, 0x4C, 0x3C, 0x06, 0x3C, 0x5A, 0x28, 0xA9,
 | 
			
		||||
 0x4B, 0x2A, 0x69, 0xA2, 0x2E, 0xB0, 0x25, 0xD5, 0x83, 0x1C, 0x4B, 0xC9, 0x95, 0x50, 0xF5, 0x61,
 | 
			
		||||
 0x24, 0x44, 0x14, 0x4A, 0x93, 0x5B, 0x08, 0xAC, 0x49, 0xAB, 0x79, 0xF1, 0xE8, 0x46, 0xD6, 0x6B,
 | 
			
		||||
 0xBF, 0x44, 0xBE, 0x0D, 0x7A, 0x15, 0xCC, 0x23, 0x41, 0x9D, 0x04, 0x6C, 0xCC, 0x9D, 0x90, 0xF9,
 | 
			
		||||
 0x7E, 0x40, 0x4B, 0x56, 0xEB, 0x64, 0x49, 0x60, 0xF8, 0x44, 0x10, 0x87, 0x85, 0x64, 0x4C, 0x1B,
 | 
			
		||||
 0x31, 0x1F, 0x03, 0x34, 0xA5, 0xBB, 0x3B, 0x16, 0xFB, 0xD0, 0xBD, 0xB8, 0x9A, 0x36, 0xDF, 0xBD,
 | 
			
		||||
 0x1E, 0x47, 0x1D, 0xF8, 0xE7, 0xBC, 0x37, 0x98, 0x1C, 0x0F, 0xC6, 0x30, 0xEA, 0xE2, 0xB4, 0xF3,
 | 
			
		||||
 0xFE, 0xB0, 0xF3, 0x1E, 0x7E, 0x0E, 0x5B, 0xB5, 0xAF, 0xA3, 0x6F, 0xB8, 0xD0, 0x7D, 0xED, 0x77,
 | 
			
		||||
 0xFB, 0x83, 0xE3, 0x4E, 0xE7, 0x5D, 0xE3, 0xCD, 0xF9, 0xF4, 0xE3, 0xBB, 0x5D, 0x04, 0x77, 0x83,
 | 
			
		||||
 0xE6, 0xD5, 0x87, 0x49, 0x73, 0xB0, 0xF5, 0x32, 0xF4, 0x4F, 0xFC, 0x89, 0x17, 0x0E, 0x3A, 0xEF,
 | 
			
		||||
 0x3F, 0x5E, 0xDD, 0x5D, 0x87, 0x83, 0x71, 0xEF, 0x63, 0x6B, 0xF2, 0x79, 0xEB, 0x43, 0xEF, 0xF3,
 | 
			
		||||
 0xC7, 0x57, 0xB7, 0xF4, 0xD3, 0xC9, 0xDB, 0xCF, 0xFD, 0x29, 0x20, 0x1C, 0x45, 0xBD, 0xC1, 0x55,
 | 
			
		||||
 0xF7, 0x43, 0x77, 0xFC, 0xB9, 0xEB, 0x1D, 0xDF, 0x0F, 0x83, 0xF3, 0xEE, 0xEB, 0xCE, 0xB0, 0xB3,
 | 
			
		||||
 0xE5, 0x51, 0x3A, 0xEE, 0x5F, 0x75, 0xB3, 0x37, 0xEF, 0x2E, 0xC6, 0x8C, 0x4D, 0x7A, 0x9F, 0xCF,
 | 
			
		||||
 0xFB, 0xDE, 0xE1, 0xF3, 0xD3, 0xC1, 0x49, 0x87, 0x4D, 0xCE, 0xDF, 0x5E, 0x35, 0x6F, 0x5F, 0xBF,
 | 
			
		||||
 0x3B, 0x3C, 0xF2, 0xAE, 0xDF, 0x5E, 0xEF, 0x1E, 0x6D, 0x37, 0x7E, 0xFB, 0xED, 0xCC, 0xBF, 0x60,
 | 
			
		||||
 0xBC, 0x7F, 0xF7, 0xBD, 0x33, 0x3E, 0x9C, 0xBE, 0x78, 0x48, 0xFB, 0x93, 0x37, 0x77, 0xBC, 0xF1,
 | 
			
		||||
 0x21, 0xFA, 0xFA, 0xE6, 0xE1, 0x0C, 0xFE, 0xBB, 0xBC, 0xAC, 0x0D, 0x7B, 0xAD, 0x74, 0xF0, 0xFE,
 | 
			
		||||
 0xCD, 0x87, 0xAD, 0xF4, 0xE5, 0xF3, 0xB8, 0x7B, 0x74, 0x74, 0x17, 0x0E, 0x2F, 0x1B, 0xA1, 0x7F,
 | 
			
		||||
 0x3B, 0x12, 0x2F, 0xB6, 0x45, 0x7C, 0x3D, 0xCE, 0x3E, 0x7F, 0x7B, 0xFE, 0x76, 0xD2, 0xB8, 0xA0,
 | 
			
		||||
 0xE4, 0x7A, 0x52, 0x7B, 0xF8, 0xFE, 0xF0, 0x62, 0xD2, 0x3F, 0xB9, 0x3B, 0x0F, 0xC8, 0xFD, 0xF9,
 | 
			
		||||
 0xB9, 0xF7, 0x3D, 0xAC, 0x05, 0xE4, 0xE5, 0x45, 0x3F, 0x20, 0x49, 0x6B, 0xE0, 0x77, 0x1A, 0xB5,
 | 
			
		||||
 0xC3, 0xAD, 0xCE, 0x8E, 0x48, 0xAE, 0x0E, 0xF9, 0xD1, 0xF6, 0xD7, 0xDE, 0x8B, 0x6E, 0xB7, 0x15,
 | 
			
		||||
 0x0D, 0xBF, 0x6D, 0xBD, 0xBE, 0xDD, 0x7D, 0x3D, 0xD8, 0x7D, 0x3F, 0x7C, 0xDF, 0xE9, 0xED, 0x74,
 | 
			
		||||
 0x07, 0xE4, 0xBA, 0xF7, 0xBE, 0x33, 0xDA, 0x19, 0x4E, 0x26, 0xEF, 0xDE, 0xF5, 0x5F, 0xF9, 0x9D,
 | 
			
		||||
 0xEF, 0x49, 0xE7, 0x62, 0xDA, 0xB9, 0x3F, 0x1E, 0x74, 0x4E, 0x6A, 0xEF, 0x8E, 0xCF, 0x9A, 0xAD,
 | 
			
		||||
 0xDE, 0xF5, 0xF6, 0xF8, 0x6C, 0x77, 0xDA, 0x4D, 0x8F, 0x3B, 0xEF, 0xBB, 0xCD, 0xF1, 0xDB, 0x5A,
 | 
			
		||||
 0x48, 0x3E, 0x47, 0x87, 0xDB, 0xE3, 0x37, 0xBB, 0xEC, 0xF2, 0x9A, 0x74, 0xDE, 0x74, 0xDF, 0xA6,
 | 
			
		||||
 0xEC, 0x2A, 0x3C, 0x19, 0x34, 0x3B, 0x9D, 0xD3, 0x0B, 0xFA, 0xEA, 0x70, 0x9B, 0xBC, 0xDB, 0xF2,
 | 
			
		||||
 0x3E, 0x82, 0xFE, 0x07, 0x9F, 0xE8, 0x6F, 0xB5, 0xCE, 0xF4, 0xA2, 0x19, 0x78, 0x2F, 0x69, 0xFF,
 | 
			
		||||
 0xE4, 0xBA, 0x2F, 0x6F, 0xE7, 0x38, 0x78, 0xD5, 0xBF, 0xED, 0x65, 0xEF, 0xC3, 0xC3, 0x43, 0x53,
 | 
			
		||||
 0xE3, 0x51, 0x3D, 0xA1, 0x31, 0x25, 0xA2, 0x1C, 0xAE, 0x16, 0xFE, 0x01, 0xB6, 0xB5, 0xB4, 0xC2,
 | 
			
		||||
 0xDC, 0x4F, 0x05, 0xBD, 0x17, 0x75, 0x9F, 0x7A, 0x51, 0x42, 0xE4, 0x1E, 0x40, 0xA0, 0x09, 0x9A,
 | 
			
		||||
 0xD8, 0xFC, 0x77, 0x19, 0x3F, 0x35, 0x15, 0x3F, 0x35, 0xC2, 0x7D, 0xCD, 0x28, 0x1C, 0x01, 0x83,
 | 
			
		||||
 0x87, 0x4F, 0xEF, 0x98, 0x47, 0xEB, 0x31, 0xBB, 0xA7, 0x41, 0x5D, 0x22, 0x3B, 0x4D, 0x73, 0x26,
 | 
			
		||||
 0xFD, 0xAD, 0xD8, 0x46, 0x38, 0x98, 0x9A, 0xA4, 0x5A, 0x2C, 0xF8, 0x5F, 0x89, 0x47, 0x21, 0xB0,
 | 
			
		||||
 0x81, 0xCB, 0x84, 0xF8, 0xAB, 0x7C, 0x27, 0x4A, 0xEA, 0xC3, 0x6C, 0x3C, 0x62, 0xF7, 0xE0, 0xD0,
 | 
			
		||||
 0x23, 0xC6, 0x99, 0xA0, 0x5A, 0x2B, 0x9D, 0xFF, 0x5E, 0x90, 0xB9, 0xA5, 0x0F, 0xA3, 0x84, 0x84,
 | 
			
		||||
 0x34, 0xD5, 0xFE, 0x22, 0x99, 0xD9, 0x28, 0x89, 0xC2, 0x65, 0x10, 0x99, 0x8B, 0xA8, 0x34, 0x99,
 | 
			
		||||
 0xCF, 0x9F, 0x65, 0x71, 0x10, 0x11, 0x10, 0x73, 0x4D, 0xE4, 0x50, 0xF1, 0x34, 0x91, 0x6E, 0xB5,
 | 
			
		||||
 0x88, 0xAB, 0xB9, 0x9B, 0x6D, 0xA1, 0x5B, 0x96, 0xDD, 0x7A, 0x6B, 0x67, 0xE9, 0xBA, 0x75, 0xB9,
 | 
			
		||||
 0x17, 0xE3, 0xFD, 0x9A, 0x4C, 0x81, 0xF1, 0xA0, 0x14, 0xEE, 0x9E, 0x09, 0x50, 0xE9, 0x13, 0x87,
 | 
			
		||||
 0xCB, 0x43, 0xF2, 0xC8, 0xB0, 0x60, 0x40, 0x05, 0xEA, 0x96, 0x8C, 0xD4, 0x85, 0x24, 0xB0, 0x6F,
 | 
			
		||||
 0xFE, 0x8C, 0xCA, 0xBC, 0x67, 0x3D, 0x8B, 0x13, 0xB8, 0x0D, 0x3A, 0xFD, 0x11, 0xCD, 0x42, 0xA6,
 | 
			
		||||
 0x2A, 0x6D, 0x45, 0x53, 0x65, 0xBC, 0x5C, 0x84, 0x65, 0xDA, 0x93, 0xBC, 0x16, 0xA4, 0x1F, 0x4B,
 | 
			
		||||
 0x05, 0xE0, 0x05, 0x37, 0xCF, 0x91, 0x9B, 0x1F, 0x6A, 0x75, 0x7B, 0xF7, 0x97, 0x9C, 0x87, 0x9D,
 | 
			
		||||
 0xE6, 0x2F, 0x73, 0x3B, 0xDF, 0x5B, 0xA4, 0xE4, 0x56, 0x13, 0xFE, 0x29, 0x32, 0xEF, 0x8B, 0x25,
 | 
			
		||||
 0x0B, 0xC3, 0xE7, 0xF8, 0xA7, 0x60, 0x10, 0xE9, 0x94, 0x80, 0xDB, 0x3B, 0x2F, 0x5F, 0xF8, 0xC3,
 | 
			
		||||
 0x02, 0x98, 0x0B, 0xF6, 0x24, 0x3C, 0x21, 0x3E, 0xCB, 0x52, 0xE7, 0x79, 0xF3, 0x97, 0x5C, 0x9F,
 | 
			
		||||
 0x5B, 0x3B, 0x28, 0xFB, 0xE2, 0x2E, 0x71, 0xB2, 0xB4, 0xD8, 0x34, 0x66, 0x5C, 0xDB, 0x4A, 0x35,
 | 
			
		||||
 0xBC, 0x6F, 0x92, 0x2C, 0x0C, 0xB3, 0x92, 0xED, 0xE7, 0xBF, 0x2F, 0x4D, 0x13, 0xF7, 0xCF, 0x9A,
 | 
			
		||||
 0xBF, 0xCC, 0x44, 0x02, 0xD9, 0x64, 0x04, 0xB9, 0xC6, 0x49, 0x22, 0x41, 0x04, 0x35, 0x9A, 0xE6,
 | 
			
		||||
 0x1C, 0x84, 0x5B, 0x03, 0xD8, 0xDE, 0x6D, 0xFA, 0x74, 0x6C, 0xCE, 0xE7, 0x7B, 0x0D, 0x99, 0xD7,
 | 
			
		||||
 0xA0, 0x6C, 0xF1, 0x12, 0x16, 0x8B, 0xFD, 0x51, 0xC6, 0x3D, 0xE4, 0x41, 0x1B, 0x53, 0x83, 0x9A,
 | 
			
		||||
 0xB3, 0x84, 0x8A, 0x2C, 0xE1, 0x9A, 0x1F, 0x79, 0x19, 0x1A, 0xBB, 0x3D, 0xA6, 0xE2, 0x58, 0xD9,
 | 
			
		||||
 0x7D, 0xF7, 0xE1, 0x8D, 0x0F, 0x3B, 0xE6, 0x0B, 0x04, 0x6F, 0x2D, 0x02, 0x38, 0x30, 0x9C, 0x97,
 | 
			
		||||
 0xE3, 0x54, 0xF6, 0x43, 0x82, 0x01, 0x22, 0xEF, 0xE8, 0x83, 0x41, 0x2D, 0xB1, 0x40, 0xA4, 0x36,
 | 
			
		||||
 0xAE, 0x1B, 0xC5, 0x2E, 0x80, 0x71, 0x73, 0x76, 0x07, 0x4A, 0x20, 0x2E, 0xFD, 0x22, 0x6E, 0x2C,
 | 
			
		||||
 0xE6, 0x72, 0xF8, 0x69, 0xE7, 0xBB, 0xC9, 0x1E, 0x3B, 0xA8, 0xB7, 0x1C, 0xB2, 0xCF, 0x0E, 0x5A,
 | 
			
		||||
 0xE0, 0x5E, 0x65, 0x6E, 0xE4, 0xB9, 0xAF, 0x58, 0x40, 0x07, 0xB9, 0xC3, 0xE1, 0x31, 0x48, 0x6C,
 | 
			
		||||
 0xB1, 0x85, 0x28, 0xE2, 0x5B, 0xCD, 0xE6, 0x86, 0x4B, 0x0F, 0x48, 0x00, 0x39, 0xCC, 0xD0, 0x8F,
 | 
			
		||||
 0xAF, 0xAE, 0x2E, 0xAE, 0xBE, 0xE8, 0x35, 0x5A, 0xD3, 0x6F, 0x1C, 0x4D, 0xAF, 0x71, 0xD3, 0x11,
 | 
			
		||||
 0x76, 0x42, 0x47, 0x09, 0x4D, 0x27, 0x97, 0x44, 0x4C, 0x8C, 0xD4, 0xBE, 0x23, 0x41, 0x56, 0x16,
 | 
			
		||||
 0x84, 0xA1, 0xDC, 0xC8, 0xA2, 0x70, 0x39, 0x9D, 0x6A, 0xAF, 0x40, 0xCD, 0x47, 0x90, 0xEA, 0xDA,
 | 
			
		||||
 0xC2, 0x26, 0x71, 0x4C, 0xB9, 0x6F, 0xE8, 0x31, 0x20, 0xEA, 0x16, 0x35, 0xAD, 0x84, 0x7E, 0xCB,
 | 
			
		||||
 0x68, 0x2A, 0x52, 0x1B, 0x2C, 0xD7, 0xD0, 0x2F, 0x07, 0x7D, 0xDD, 0xD2, 0x1B, 0xE8, 0x47, 0x3A,
 | 
			
		||||
 0xF0, 0x46, 0xCC, 0x39, 0x52, 0x89, 0x5C, 0xD0, 0xA4, 0x3E, 0xCC, 0xC0, 0xA0, 0xB8, 0x6E, 0xB6,
 | 
			
		||||
 0x23, 0x9B, 0x71, 0x4E, 0x93, 0x93, 0xFE, 0xD9, 0xA9, 0xAB, 0x5F, 0x29, 0x46, 0xB4, 0x53, 0x28,
 | 
			
		||||
 0x48, 0x74, 0x4B, 0x5E, 0x51, 0x7E, 0xC8, 0xE1, 0x84, 0x05, 0xBE, 0x11, 0x99, 0x6D, 0x24, 0xE1,
 | 
			
		||||
 0x49, 0x12, 0xB2, 0x40, 0x01, 0x0A, 0x9E, 0x2D, 0x1E, 0x62, 0xEA, 0xEA, 0x23, 0x50, 0x86, 0x6E,
 | 
			
		||||
 0x79, 0x76, 0x98, 0x05, 0x82, 0xC5, 0x01, 0x75, 0x37, 0x5A, 0x30, 0xE3, 0x60, 0x41, 0xAE, 0x8E,
 | 
			
		||||
 0xB9, 0x19, 0x61, 0xCC, 0x77, 0x75, 0x15, 0xA1, 0xF2, 0xB8, 0xB6, 0xEE, 0x14, 0x4F, 0x9D, 0x92,
 | 
			
		||||
 0x56, 0x4E, 0x49, 0xCB, 0xB8, 0x4A, 0xE0, 0x34, 0x3F, 0x18, 0xC3, 0x3C, 0xCE, 0xD4, 0x51, 0x05,
 | 
			
		||||
 0xCC, 0xA7, 0x23, 0x02, 0x9C, 0x7C, 0x40, 0x6D, 0xBA, 0x7A, 0x63, 0xDD, 0x41, 0xA9, 0x3A, 0xC8,
 | 
			
		||||
 0xAF, 0x6A, 0xC4, 0x2F, 0x6B, 0x44, 0xDD, 0xEE, 0x3A, 0x64, 0x5F, 0x21, 0x07, 0x55, 0xE4, 0xA0,
 | 
			
		||||
 0x8C, 0x7C, 0x28, 0x8D, 0x64, 0x1D, 0x72, 0xA0, 0x90, 0x93, 0x8A, 0x88, 0x89, 0x14, 0x51, 0x85,
 | 
			
		||||
 0xBD, 0x3A, 0x6A, 0x13, 0x05, 0xD2, 0xAD, 0xA4, 0x22, 0x66, 0x62, 0x83, 0x97, 0x92, 0x61, 0x40,
 | 
			
		||||
 0x7D, 0x77, 0xA3, 0x09, 0x33, 0x2C, 0xB6, 0xDD, 0xAD, 0xE6, 0x9A, 0x33, 0x12, 0x75, 0x46, 0x56,
 | 
			
		||||
 0x65, 0x30, 0x2B, 0x33, 0xA8, 0xF5, 0xC8, 0x1D, 0xD5, 0xD6, 0x31, 0x98, 0x99, 0x56, 0x60, 0x47,
 | 
			
		||||
 0xDC, 0x0B, 0x98, 0x77, 0xEB, 0x2E, 0xBD, 0xC5, 0x9C, 0xB1, 0x85, 0x85, 0x5A, 0x5C, 0x06, 0xBA,
 | 
			
		||||
 0x01, 0x94, 0x5E, 0x8B, 0xA5, 0x7C, 0x80, 0xFA, 0x9E, 0x5B, 0xD9, 0x5A, 0x02, 0xDC, 0xA6, 0xF7,
 | 
			
		||||
 0xD4, 0x3B, 0x8C, 0xC2, 0x90, 0xA0, 0xED, 0xA6, 0xC0, 0x41, 0x3E, 0xD1, 0xCD, 0xB9, 0x15, 0xAD,
 | 
			
		||||
 0xC5, 0x79, 0xC2, 0x45, 0x2C, 0x7F, 0x3D, 0x8B, 0x23, 0x03, 0x5C, 0xCE, 0xF5, 0x6C, 0xD4, 0x61,
 | 
			
		||||
 0x6A, 0x83, 0x1E, 0xC7, 0x62, 0xF2, 0x13, 0x17, 0x2A, 0x0C, 0x54, 0xA2, 0x7C, 0x69, 0xDE, 0x58,
 | 
			
		||||
 0x0B, 0x91, 0x56, 0x7C, 0xEA, 0xA2, 0xB7, 0xE2, 0x54, 0xA8, 0xBC, 0x8A, 0x5D, 0x9A, 0x4B, 0x1D,
 | 
			
		||||
 0x94, 0x61, 0xB9, 0xBD, 0x2F, 0xA0, 0xFA, 0x7C, 0x0E, 0xE7, 0x01, 0xFF, 0x13, 0x68, 0xF9, 0xE8,
 | 
			
		||||
 0x5F, 0x17, 0x60, 0xC9, 0xA3, 0x34, 0x78, 0x8B, 0xBB, 0x0D, 0xE3, 0xC0, 0xF9, 0x8F, 0x6D, 0x7C,
 | 
			
		||||
 0xF9, 0x1F, 0xFB, 0xA6, 0x66, 0x9A, 0x07, 0xFF, 0x6A, 0x48, 0x0D, 0x1B, 0xC2, 0xFC, 0xD2, 0xBA,
 | 
			
		||||
 0xB1, 0x08, 0x80, 0xED, 0x7F, 0x9B, 0xFF, 0xB1, 0x25, 0xB8, 0x02, 0x6B, 0xDF, 0x45, 0x90, 0x49,
 | 
			
		||||
 0xF0, 0x24, 0x34, 0xB0, 0x68, 0xA4, 0x91, 0xCD, 0x4D, 0x43, 0xB8, 0xA4, 0x72, 0x8D, 0x35, 0x51,
 | 
			
		||||
 0xD3, 0x6D, 0x88, 0x53, 0x50, 0x5B, 0xAC, 0x04, 0xBF, 0x3E, 0x24, 0x7A, 0x15, 0x5B, 0x17, 0x00,
 | 
			
		||||
 0xC9, 0x3D, 0xCA, 0x0C, 0x3D, 0x22, 0x97, 0x52, 0xCB, 0x0C, 0x02, 0x42, 0xA7, 0x89, 0xE7, 0x2A,
 | 
			
		||||
 0xAD, 0x1D, 0x14, 0x30, 0x17, 0xA2, 0xE0, 0xBC, 0x1C, 0x2D, 0x15, 0xEA, 0xAA, 0xFD, 0x17, 0x0A,
 | 
			
		||||
 0xA3, 0xD6, 0x12, 0x8A, 0x04, 0x31, 0xAD, 0xD8, 0x79, 0xC6, 0x72, 0x75, 0x4C, 0x59, 0xBA, 0x35,
 | 
			
		||||
 0x59, 0x5D, 0x96, 0xAD, 0x04, 0xAE, 0x2F, 0x8D, 0xFE, 0xD7, 0x3D, 0x16, 0x8E, 0xB5, 0x12, 0x3F,
 | 
			
		||||
 0xF8, 0x97, 0xFB, 0x2B, 0x46, 0xE4, 0xCD, 0x3F, 0xBC, 0x21, 0x70, 0x05, 0xA6, 0x41, 0x6D, 0x1E,
 | 
			
		||||
 0x4D, 0x0D, 0xB3, 0xF6, 0xAB, 0xAE, 0x49, 0x8A, 0xAE, 0x1E, 0x92, 0xFB, 0xBC, 0xA7, 0xC4, 0x8C,
 | 
			
		||||
 0xD7, 0xD6, 0x70, 0x5E, 0xB4, 0x28, 0xF9, 0x82, 0xEC, 0xE6, 0x48, 0x26, 0xA2, 0xB6, 0x56, 0x64,
 | 
			
		||||
 0x52, 0xD5, 0xCA, 0xE8, 0x5A, 0x63, 0xFF, 0xD7, 0x4A, 0x40, 0xB7, 0x98, 0xBA, 0x4E, 0x15, 0x8C,
 | 
			
		||||
 0xB3, 0x00, 0x1C, 0x93, 0x3E, 0x1D, 0x69, 0x03, 0x26, 0x03, 0x75, 0x35, 0x46, 0x5A, 0x81, 0xC1,
 | 
			
		||||
 0xCC, 0x03, 0xC3, 0x2B, 0xFB, 0xF3, 0x1E, 0x16, 0xBF, 0xFB, 0x97, 0xAA, 0xAA, 0x81, 0xD4, 0x8B,
 | 
			
		||||
 0x33, 0x5D, 0x59, 0x59, 0xD5, 0x4B, 0xE0, 0xD2, 0x08, 0xA0, 0x5B, 0x8B, 0x3C, 0x3A, 0x8C, 0xFC,
 | 
			
		||||
 0x87, 0x52, 0xF6, 0x4D, 0xBB, 0x0F, 0x87, 0x01, 0x49, 0xD3, 0x73, 0xB8, 0x01, 0x43, 0xF7, 0x42,
 | 
			
		||||
 0x50, 0xB8, 0xB2, 0xC2, 0xFD, 0xE6, 0xE6, 0x66, 0x15, 0x29, 0xA1, 0x21, 0x14, 0xDB, 0x8A, 0x2B,
 | 
			
		||||
 0xF0, 0x49, 0xD3, 0xF1, 0x81, 0x30, 0x18, 0xD2, 0x1A, 0xC6, 0xF0, 0x25, 0xE3, 0x47, 0x5C, 0x71,
 | 
			
		||||
 0xF4, 0xF4, 0x22, 0xA6, 0xFC, 0x33, 0xDC, 0x95, 0x32, 0xCB, 0x1A, 0xAD, 0xA6, 0x68, 0xFA, 0x8F,
 | 
			
		||||
 0xD8, 0x3E, 0xCA, 0x0D, 0x76, 0xC1, 0x7A, 0xBA, 0x56, 0xA1, 0xFC, 0x9F, 0x61, 0xB9, 0x94, 0x28,
 | 
			
		||||
 0xD6, 0x70, 0x9C, 0x40, 0x80, 0x5A, 0xC3, 0x31, 0xC4, 0x1A, 0x41, 0x17, 0xFC, 0x26, 0x6B, 0xF9,
 | 
			
		||||
 0xCD, 0xFE, 0x19, 0x7E, 0x97, 0x76, 0x1E, 0x15, 0x25, 0x91, 0xAA, 0xAF, 0x50, 0x02, 0x9F, 0xDD,
 | 
			
		||||
 0xE9, 0xA6, 0x15, 0xB9, 0x55, 0x0A, 0x50, 0x1B, 0x46, 0x41, 0xD0, 0x8F, 0xE2, 0x83, 0x27, 0xD6,
 | 
			
		||||
 0x9D, 0xC5, 0x7A, 0x31, 0xC8, 0xD9, 0x5C, 0x6E, 0xB1, 0xBC, 0xB5, 0x44, 0x4F, 0xA1, 0xEC, 0x5F,
 | 
			
		||||
 0x4B, 0x15, 0x01, 0x3F, 0x23, 0x8B, 0x7B, 0xAC, 0xD4, 0xA5, 0x36, 0x28, 0x0F, 0x56, 0x3F, 0xD5,
 | 
			
		||||
 0x3C, 0xCB, 0x5F, 0xCC, 0xAE, 0x6B, 0x51, 0x9B, 0xC0, 0x38, 0x57, 0x92, 0x8B, 0x4A, 0xB2, 0xC8,
 | 
			
		||||
 0x13, 0x01, 0xA8, 0x58, 0xC7, 0x2E, 0xC4, 0x4D, 0x6B, 0x7A, 0x7C, 0xBF, 0x5C, 0x83, 0xC2, 0xDF,
 | 
			
		||||
 0xF5, 0xD5, 0x12, 0x33, 0x08, 0xC4, 0xD3, 0x95, 0x4B, 0x29, 0x5F, 0x37, 0x29, 0x8A, 0x0E, 0x62,
 | 
			
		||||
 0x47, 0xA3, 0x51, 0x4A, 0xC5, 0x47, 0x0C, 0x49, 0x56, 0xB2, 0x98, 0x9F, 0xC8, 0x90, 0x04, 0x8C,
 | 
			
		||||
 0x45, 0x3C, 0x8C, 0xB2, 0x94, 0x46, 0x99, 0xA8, 0xA4, 0x16, 0x63, 0x21, 0xCC, 0x5E, 0xFA, 0xE7,
 | 
			
		||||
 0x9F, 0x8B, 0xC9, 0x7E, 0x5A, 0x0B, 0x96, 0xD3, 0xEB, 0x3D, 0xBF, 0x34, 0xD9, 0xF7, 0x6B, 0x89,
 | 
			
		||||
 0xB9, 0x7A, 0xE9, 0xFF, 0x67, 0x4B, 0x21, 0x65, 0x4B, 0xF1, 0xB0, 0x54, 0x2E, 0x62, 0x62, 0x29,
 | 
			
		||||
 0xE6, 0xC9, 0x82, 0x91, 0x97, 0x7C, 0x16, 0x0D, 0x1A, 0x2B, 0x25, 0x55, 0x9E, 0x97, 0x7D, 0x95,
 | 
			
		||||
 0x43, 0x40, 0x59, 0x71, 0xE5, 0x35, 0x11, 0x06, 0x34, 0xE0, 0x63, 0x64, 0xF2, 0x41, 0xEB, 0xA7,
 | 
			
		||||
 0xD1, 0x94, 0x26, 0x87, 0x24, 0xA5, 0x06, 0x24, 0xCD, 0x65, 0xDC, 0x41, 0xA8, 0xE9, 0x04, 0xEB,
 | 
			
		||||
 0x76, 0x6D, 0x6E, 0x12, 0x05, 0xCE, 0x33, 0x77, 0xC4, 0xB1, 0x26, 0x03, 0xF9, 0xB2, 0xCA, 0x09,
 | 
			
		||||
 0xD4, 0xC6, 0xBE, 0x12, 0xA4, 0x3E, 0x52, 0x25, 0xA8, 0x61, 0x5A, 0xD0, 0x76, 0xC0, 0x35, 0x5F,
 | 
			
		||||
 0x26, 0x51, 0x4C, 0xC6, 0xB2, 0x07, 0x83, 0x35, 0x74, 0x0F, 0xA4, 0x66, 0x6D, 0x34, 0x91, 0x60,
 | 
			
		||||
 0xA9, 0x73, 0x29, 0xFC, 0x66, 0xD9, 0xC2, 0x70, 0x4B, 0x57, 0xC9, 0xB0, 0xBD, 0xF4, 0xA5, 0x35,
 | 
			
		||||
 0x59, 0x83, 0xE0, 0x0B, 0x6C, 0x62, 0xE0, 0x1E, 0x68, 0x64, 0xF2, 0x7B, 0x00, 0x77, 0x6B, 0xB6,
 | 
			
		||||
 0xA3, 0x3D, 0xD6, 0x8E, 0x6A, 0x35, 0x53, 0x55, 0xE9, 0xAE, 0x0B, 0x6D, 0x4E, 0x74, 0x23, 0x0B,
 | 
			
		||||
 0x4B, 0x10, 0xAA, 0x9A, 0x59, 0x0C, 0x38, 0x1B, 0x81, 0xAA, 0xBA, 0xC0, 0x11, 0xD6, 0x98, 0x66,
 | 
			
		||||
 0xA9, 0x23, 0xF1, 0x97, 0x1D, 0xC9, 0x13, 0xB5, 0x07, 0x95, 0xF5, 0x05, 0xD4, 0x31, 0xAB, 0x25,
 | 
			
		||||
 0x86, 0x30, 0xD3, 0x29, 0x13, 0xDE, 0x04, 0x03, 0x90, 0x07, 0x5A, 0xD5, 0x05, 0x14, 0xB5, 0x8E,
 | 
			
		||||
 0x1C, 0x4D, 0x44, 0xB8, 0x1C, 0x05, 0xF9, 0xF0, 0x6B, 0x9A, 0x0F, 0xBC, 0xB4, 0x18, 0xDD, 0x97,
 | 
			
		||||
 0x80, 0x50, 0xD2, 0xE6, 0xE0, 0x88, 0x8F, 0xF2, 0x21, 0xF4, 0xB2, 0x05, 0x9D, 0x02, 0x58, 0xFC,
 | 
			
		||||
 0xC6, 0x71, 0x3E, 0x8A, 0x27, 0xC5, 0x68, 0x42, 0xEF, 0x17, 0x78, 0x51, 0x01, 0xF5, 0xA9, 0xEE,
 | 
			
		||||
 0x28, 0x1B, 0xDB, 0x68, 0xCE, 0xF3, 0x41, 0x6B, 0x29, 0x7F, 0xF0, 0xFF, 0x28, 0x7F, 0xCC, 0xC7,
 | 
			
		||||
 0x85, 0x34, 0x71, 0x31, 0x1A, 0xB3, 0x42, 0x96, 0x61, 0x18, 0xFF, 0x90, 0x93, 0xA4, 0xD4, 0x13,
 | 
			
		||||
 0x97, 0x7A, 0x5A, 0xF1, 0xB3, 0xB6, 0x53, 0x98, 0x8E, 0x31, 0xAA, 0xF8, 0xE3, 0xC8, 0xF6, 0xF0,
 | 
			
		||||
 0xF7, 0x3C, 0xF2, 0x65, 0x6D, 0x69, 0x5A, 0xA1, 0x31, 0x82, 0x3A, 0x57, 0x37, 0xCB, 0x7E, 0x9A,
 | 
			
		||||
 0xFD, 0xB7, 0xAD, 0xE8, 0xD1, 0xF1, 0xE9, 0x71, 0xFF, 0xB8, 0x5C, 0x38, 0x23, 0xE7, 0x25, 0x93,
 | 
			
		||||
 0x8A, 0x2B, 0x5D, 0xFA, 0xB2, 0x22, 0x80, 0x02, 0x1B, 0x45, 0x01, 0x7B, 0xDD, 0xDC, 0x54, 0x7E,
 | 
			
		||||
 0xF1, 0xB6, 0x77, 0x71, 0x6E, 0xC7, 0x24, 0x01, 0x8F, 0x24, 0x15, 0xE6, 0xC2, 0x82, 0x44, 0xF9,
 | 
			
		||||
 0xE0, 0xD7, 0xC7, 0xA5, 0x72, 0x5D, 0x7E, 0x61, 0x70, 0xC4, 0xDC, 0x52, 0xA7, 0xA9, 0x7E, 0x78,
 | 
			
		||||
 0xE2, 0x62, 0x5D, 0x99, 0xBF, 0x04, 0x41, 0x72, 0x1A, 0x2D, 0x13, 0x55, 0x11, 0x67, 0x46, 0xE5,
 | 
			
		||||
 0x30, 0x2F, 0xEE, 0xB2, 0x75, 0x0D, 0xD3, 0xC8, 0xB4, 0xC4, 0x84, 0xA5, 0xE5, 0x46, 0xA5, 0x12,
 | 
			
		||||
 0x14, 0xFE, 0xA2, 0xB6, 0xE7, 0x8B, 0x91, 0x24, 0xB7, 0x5A, 0x73, 0xAB, 0x6F, 0x41, 0x2A, 0x3E,
 | 
			
		||||
 0x58, 0x04, 0x23, 0x66, 0x39, 0xDB, 0x16, 0x77, 0xA3, 0x43, 0xEE, 0x61, 0x5C, 0x7F, 0xBA, 0x35,
 | 
			
		||||
 0x78, 0xD2, 0x3C, 0x79, 0x61, 0x9E, 0xFC, 0xB1, 0x7B, 0x2E, 0x1C, 0x45, 0xF9, 0xDA, 0xE2, 0x98,
 | 
			
		||||
 0xF6, 0x10, 0x58, 0xBB, 0x6D, 0x2F, 0x7D, 0x18, 0x20, 0xD2, 0x83, 0xCB, 0x00, 0xF4, 0x63, 0x58,
 | 
			
		||||
 0xFF, 0x4A, 0xEE, 0x88, 0x7A, 0x09, 0xAA, 0xA2, 0xAD, 0x73, 0x54, 0xD8, 0xEE, 0xFD, 0x81, 0xA3,
 | 
			
		||||
 0xF2, 0xCE, 0x65, 0x18, 0x48, 0x97, 0xC3, 0x92, 0x37, 0x8B, 0x75, 0xC1, 0x61, 0x19, 0x31, 0x64,
 | 
			
		||||
 0x6C, 0x00, 0xE3, 0xCD, 0x5D, 0x49, 0x13, 0xD5, 0x1C, 0xB4, 0xF0, 0x1B, 0x08, 0x8A, 0x4F, 0x39,
 | 
			
		||||
 0xCE, 0x9A, 0x38, 0xAD, 0x62, 0x72, 0xC5, 0x23, 0xC8, 0x4A, 0x67, 0x89, 0xC0, 0x6E, 0x10, 0x0D,
 | 
			
		||||
 0x0D, 0x7C, 0x64, 0x9A, 0xA1, 0xB6, 0x1D, 0x3E, 0x37, 0xD7, 0xBC, 0xD9, 0x54, 0xFA, 0x4B, 0x62,
 | 
			
		||||
 0x79, 0xD5, 0xB0, 0x8B, 0x1C, 0x56, 0xCC, 0x75, 0x7D, 0x1F, 0xF4, 0xA3, 0x4E, 0x29, 0xAF, 0x48,
 | 
			
		||||
 0xA4, 0x53, 0xD1, 0x83, 0xC4, 0x86, 0xA2, 0x41, 0xBE, 0x91, 0x40, 0x44, 0x72, 0x4A, 0x33, 0x5D,
 | 
			
		||||
 0xC7, 0xCA, 0xD2, 0x0B, 0x28, 0x49, 0x7A, 0xB2, 0x73, 0x95, 0x49, 0x6B, 0x25, 0x06, 0xFE, 0xC8,
 | 
			
		||||
 0xD7, 0xF0, 0xC7, 0xA1, 0xD0, 0xA3, 0x83, 0x9B, 0x49, 0x2B, 0x83, 0xA4, 0x23, 0x64, 0x83, 0xA9,
 | 
			
		||||
 0x37, 0xE4, 0xBB, 0xA8, 0x2D, 0x2F, 0xCB, 0xB4, 0x16, 0x50, 0x70, 0x71, 0x83, 0xBB, 0x11, 0x30,
 | 
			
		||||
 0x52, 0x5A, 0xC4, 0x9E, 0x94, 0xA8, 0xC7, 0x8F, 0x10, 0x1F, 0x53, 0x4A, 0x20, 0x06, 0x20, 0xA6,
 | 
			
		||||
 0x40, 0xD0, 0xA7, 0x42, 0x8A, 0x54, 0xE6, 0x92, 0x53, 0x2A, 0x20, 0xCA, 0x48, 0xCD, 0xE2, 0xC1,
 | 
			
		||||
 0x85, 0x78, 0xD4, 0x46, 0xD6, 0x80, 0xFD, 0xDC, 0xBD, 0x73, 0x33, 0xDE, 0x90, 0x68, 0x09, 0x56,
 | 
			
		||||
 0x36, 0x3D, 0x9A, 0xA6, 0x52, 0x5C, 0x54, 0xC7, 0x19, 0xF8, 0xA8, 0xA1, 0x03, 0x5A, 0x23, 0x84,
 | 
			
		||||
 0x11, 0x1E, 0x84, 0x8A, 0x01, 0x40, 0x7F, 0x42, 0xC3, 0x1C, 0x22, 0x70, 0x08, 0x20, 0x82, 0xA0,
 | 
			
		||||
 0x7F, 0x49, 0x0D, 0xF7, 0x64, 0x05, 0xC9, 0xF8, 0xD8, 0x6D, 0x35, 0xF0, 0x9D, 0x66, 0x95, 0xEC,
 | 
			
		||||
 0x20, 0xA5, 0xBD, 0x68, 0x24, 0xFA, 0x64, 0x98, 0x1A, 0x50, 0x00, 0xAC, 0xD9, 0x01, 0xA0, 0x1E,
 | 
			
		||||
 0x24, 0x5E, 0x63, 0x2B, 0x3F, 0xEF, 0x04, 0x2A, 0xBB, 0x00, 0xAB, 0xBB, 0x8E, 0x87, 0x5F, 0x39,
 | 
			
		||||
 0x4F, 0x19, 0xA7, 0x39, 0x26, 0x00, 0x7B, 0x93, 0x68, 0x7A, 0x99, 0x30, 0x2E, 0xCE, 0x64, 0x1B,
 | 
			
		||||
 0x6A, 0x6C, 0xB4, 0xE4, 0xF5, 0xA9, 0x87, 0x15, 0x79, 0x3F, 0xC5, 0x8B, 0xCB, 0x0C, 0xF3, 0xBA,
 | 
			
		||||
 0x53, 0x79, 0x77, 0xB1, 0x86, 0x70, 0x21, 0x50, 0x66, 0x38, 0xB3, 0x29, 0x74, 0xB0, 0xFA, 0xA1,
 | 
			
		||||
 0x48, 0x82, 0x7A, 0x4F, 0xB7, 0x42, 0xE2, 0xC1, 0x44, 0xED, 0x81, 0xF9, 0xDC, 0xC2, 0xD8, 0xE1,
 | 
			
		||||
 0x94, 0x83, 0x5A, 0x0A, 0xB5, 0x02, 0x45, 0xC6, 0x95, 0xCD, 0x98, 0x35, 0x1D, 0x6A, 0x58, 0x88,
 | 
			
		||||
 0x61, 0xE0, 0xAF, 0xFE, 0x05, 0x0F, 0x1E, 0x1C, 0xC8, 0x55, 0x3F, 0xE1, 0x23, 0xE3, 0x7E, 0xF4,
 | 
			
		||||
 0x23, 0x3E, 0x3E, 0xAF, 0xF0, 0xF1, 0x79, 0x1D, 0x1F, 0xB4, 0xAA, 0x3C, 0x98, 0x0C, 0x80, 0xEC,
 | 
			
		||||
 0x19, 0xE1, 0x64, 0x4C, 0x13, 0x58, 0xC0, 0x43, 0x50, 0x25, 0x7F, 0x8B, 0xB3, 0x84, 0xFE, 0x98,
 | 
			
		||||
 0xB3, 0xDE, 0x84, 0x8D, 0xC4, 0x23, 0xFE, 0x8A, 0xD5, 0xFF, 0x82, 0x4B, 0x3C, 0x70, 0x3D, 0x97,
 | 
			
		||||
 0x79, 0x6D, 0x5A, 0x49, 0x28, 0x3F, 0x7E, 0x2B, 0x91, 0x7E, 0xE4, 0x42, 0x78, 0xA9, 0x38, 0xC8,
 | 
			
		||||
 0xDF, 0xB7, 0xF4, 0x00, 0xBC, 0x11, 0xF8, 0x29, 0x35, 0x75, 0xBC, 0x0B, 0xA5, 0xFC, 0x29, 0x30,
 | 
			
		||||
 0x64, 0xA8, 0xC0, 0x47, 0xDD, 0xD9, 0xDC, 0x12, 0xAE, 0x01, 0x8A, 0xF1, 0xA3, 0x29, 0xB0, 0xEA,
 | 
			
		||||
 0xC9, 0x02, 0xD7, 0x9E, 0x40, 0x26, 0x04, 0x91, 0xE0, 0x48, 0xC8, 0xA7, 0x8D, 0x2F, 0x07, 0x9B,
 | 
			
		||||
 0x37, 0x35, 0xC8, 0x43, 0x2E, 0xFC, 0x98, 0x2E, 0x0C, 0x36, 0x6F, 0xFE, 0x6D, 0x36, 0xC6, 0xCC,
 | 
			
		||||
 0x5A, 0x76, 0xA4, 0x96, 0x4C, 0xF6, 0xF4, 0x0B, 0xBF, 0x71, 0x09, 0x48, 0x5D, 0x49, 0x78, 0x45,
 | 
			
		||||
 0x34, 0x03, 0x6B, 0x43, 0x61, 0xE1, 0x07, 0xFF, 0x47, 0x09, 0xF8, 0x91, 0x9E, 0x07, 0xCE, 0xBD,
 | 
			
		||||
 0xE6, 0x3D, 0x5E, 0x2F, 0x3E, 0x85, 0xE9, 0x56, 0xE9, 0xC1, 0x4A, 0xC7, 0xEF, 0x53, 0x3A, 0x76,
 | 
			
		||||
 0x59, 0xA2, 0x14, 0x4A, 0x14, 0x59, 0x88, 0x1A, 0x6A, 0x50, 0x0E, 0x51, 0x98, 0x89, 0x17, 0xCD,
 | 
			
		||||
 0x81, 0x02, 0x9B, 0x73, 0x34, 0x5B, 0x3A, 0x02, 0x0F, 0xF4, 0xF5, 0x45, 0xEE, 0xFC, 0x74, 0x76,
 | 
			
		||||
 0x7A, 0x22, 0x44, 0x7C, 0xA5, 0x62, 0x22, 0xD0, 0xAA, 0x2E, 0x2C, 0x2F, 0xCF, 0x9C, 0x89, 0xE4,
 | 
			
		||||
 0xA1, 0x28, 0x75, 0x30, 0x31, 0x28, 0x87, 0xFE, 0x74, 0x31, 0xFC, 0x0A, 0x71, 0xD6, 0xD0, 0xCF,
 | 
			
		||||
 0x52, 0x48, 0x58, 0x5B, 0x36, 0xA2, 0xF7, 0xFB, 0x97, 0xF6, 0xAE, 0xDD, 0x84, 0xBA, 0x00, 0xB4,
 | 
			
		||||
 0x0A, 0x69, 0x19, 0xEE, 0x7D, 0xFE, 0xB7, 0x90, 0xB7, 0xFF, 0x1E, 0x32, 0x83, 0xA8, 0x95, 0x42,
 | 
			
		||||
 0x58, 0x2A, 0xF0, 0xAB, 0xB8, 0x93, 0x24, 0x9A, 0x4A, 0xB4, 0xE3, 0x24, 0xC1, 0x4B, 0xE9, 0x43,
 | 
			
		||||
 0x85, 0xA2, 0x0D, 0x61, 0x31, 0xA5, 0x89, 0xE6, 0x47, 0x34, 0xD5, 0x78, 0x24, 0xB4, 0x34, 0x8B,
 | 
			
		||||
 0x63, 0x68, 0x5C, 0x56, 0xF4, 0x61, 0xEB, 0xC5, 0xEB, 0xCB, 0xFB, 0x8C, 0x66, 0xD4, 0xCF, 0x97,
 | 
			
		||||
 0x69, 0x52, 0xD1, 0x0B, 0x56, 0x50, 0xDF, 0x10, 0xEE, 0x7E, 0xB9, 0xC9, 0xEB, 0xA9, 0x8C, 0x73,
 | 
			
		||||
 0x8C, 0xA2, 0x1B, 0x2D, 0x35, 0x07, 0xE9, 0x26, 0x40, 0xD5, 0xE5, 0x59, 0x10, 0xCC, 0xDB, 0x2B,
 | 
			
		||||
 0xB4, 0xA0, 0xF1, 0x8A, 0x44, 0x24, 0x9F, 0xCB, 0x67, 0x7F, 0xE4, 0xC9, 0xA9, 0xE2, 0x82, 0x50,
 | 
			
		||||
 0xF2, 0x54, 0xA9, 0x36, 0xAD, 0x0D, 0x63, 0x83, 0x6A, 0x8C, 0xA7, 0x82, 0x70, 0x0F, 0xAF, 0x51,
 | 
			
		||||
 0xE9, 0xC2, 0x2C, 0x6A, 0x29, 0xDC, 0xDE, 0x46, 0x5F, 0xCB, 0x6D, 0xE9, 0x89, 0x7C, 0x2A, 0x25,
 | 
			
		||||
 0xE3, 0xAE, 0xAE, 0x63, 0x55, 0x45, 0xB1, 0x3E, 0x25, 0x61, 0x5A, 0x26, 0x5B, 0x54, 0x06, 0x26,
 | 
			
		||||
 0x77, 0x0B, 0x70, 0x9B, 0x06, 0x29, 0x1C, 0xBD, 0x7E, 0x7F, 0xCE, 0x46, 0xD1, 0xCE, 0x11, 0x80,
 | 
			
		||||
 0x69, 0xC5, 0x3E, 0x93, 0xD7, 0xE0, 0x24, 0xCC, 0x73, 0x07, 0x32, 0xE9, 0x4A, 0x03, 0x0E, 0xA9,
 | 
			
		||||
 0x98, 0x44, 0xFE, 0x81, 0x7E, 0xA0, 0x3B, 0x3A, 0xFC, 0xBB, 0x09, 0x35, 0x47, 0xCD, 0xA5, 0xD0,
 | 
			
		||||
 0xA4, 0xFA, 0x74, 0x70, 0xF5, 0x06, 0xC2, 0x53, 0x0C, 0xA5, 0x01, 0x17, 0x50, 0x34, 0xD7, 0x74,
 | 
			
		||||
 0x7C, 0x7A, 0x7D, 0x0C, 0x29, 0xC8, 0x7F, 0x21, 0x37, 0x66, 0xBB, 0xAA, 0x6C, 0xB8, 0xF3, 0xEA,
 | 
			
		||||
 0x75, 0x56, 0x2E, 0x03, 0x7A, 0x61, 0x8C, 0x58, 0x0F, 0x29, 0x7E, 0xFB, 0x7B, 0xF4, 0x9E, 0x8D,
 | 
			
		||||
 0x15, 0xD2, 0x6A, 0x5D, 0x6F, 0xCE, 0x76, 0x90, 0x67, 0x89, 0xD5, 0x43, 0x2C, 0x70, 0x97, 0x1F,
 | 
			
		||||
 0x29, 0x59, 0x95, 0x35, 0xDC, 0xF6, 0x48, 0x10, 0xE0, 0xC7, 0x5A, 0x03, 0x1B, 0x6A, 0x22, 0xB2,
 | 
			
		||||
 0xD4, 0x42, 0x22, 0x29, 0x08, 0x90, 0xD2, 0x3E, 0x84, 0x39, 0xD3, 0x92, 0x65, 0x86, 0xB2, 0xA1,
 | 
			
		||||
 0xBC, 0xFF, 0xC5, 0x9A, 0xA3, 0x64, 0x46, 0xE8, 0xCE, 0xF9, 0x6C, 0x73, 0x53, 0xD8, 0x85, 0x99,
 | 
			
		||||
 0x18, 0x05, 0x52, 0x8A, 0x01, 0x1C, 0x9A, 0x7D, 0x68, 0x2D, 0x8C, 0xB2, 0x90, 0x58, 0xAB, 0x3D,
 | 
			
		||||
 0xD2, 0xB6, 0x51, 0x55, 0x03, 0x54, 0x7C, 0x46, 0x01, 0x03, 0xCE, 0xB2, 0x24, 0x80, 0xA8, 0x8B,
 | 
			
		||||
 0x39, 0xBA, 0xB2, 0x2D, 0xC5, 0xBA, 0xD0, 0x84, 0x0E, 0xEC, 0x67, 0xC8, 0x12, 0x95, 0x97, 0xAD,
 | 
			
		||||
 0xA2, 0x27, 0x12, 0xC5, 0x77, 0x95, 0x9E, 0xC8, 0x6F, 0xE5, 0x84, 0xAA, 0xC8, 0x77, 0x88, 0x2F,
 | 
			
		||||
 0x13, 0x5C, 0xD4, 0xD1, 0x13, 0xA0, 0x24, 0x83, 0x52, 0x34, 0x60, 0x2A, 0x2C, 0x37, 0xEE, 0xEB,
 | 
			
		||||
 0xD3, 0xE9, 0xB4, 0x8E, 0xDF, 0x6A, 0xEB, 0x70, 0x82, 0xB2, 0x02, 0x5F, 0x5F, 0xC7, 0x21, 0x47,
 | 
			
		||||
 0x15, 0x58, 0xF8, 0x6E, 0xE1, 0xAC, 0xBA, 0xE8, 0x42, 0x7F, 0x2B, 0xDE, 0xD4, 0xAA, 0xD2, 0x59,
 | 
			
		||||
 0xE1, 0x73, 0x79, 0xDB, 0x7B, 0x3B, 0x2B, 0x20, 0x32, 0xC4, 0xAF, 0xB2, 0x90, 0x69, 0x20, 0x0D,
 | 
			
		||||
 0x3B, 0xE5, 0x46, 0x56, 0x25, 0x85, 0x65, 0x5C, 0xB0, 0xE3, 0x2C, 0x9D, 0x18, 0x33, 0x60, 0xDD,
 | 
			
		||||
 0x11, 0x96, 0xD2, 0x95, 0x43, 0x2D, 0x65, 0xB7, 0x0E, 0xB7, 0x0A, 0xFB, 0x70, 0x30, 0x83, 0x94,
 | 
			
		||||
 0x79, 0xFB, 0xF3, 0x4F, 0x39, 0x5B, 0xDE, 0xF6, 0x92, 0x62, 0x71, 0xE1, 0xF3, 0xFC, 0xA9, 0x35,
 | 
			
		||||
 0xAF, 0x69, 0xA5, 0xD1, 0xAF, 0xC4, 0x97, 0xBD, 0x46, 0xFE, 0x19, 0x3B, 0xFF, 0x9C, 0xAD, 0x81,
 | 
			
		||||
 0xB1, 0x43, 0x23, 0x2A, 0xDC, 0x4C, 0x8C, 0xEA, 0x2F, 0x34, 0xE6, 0x63, 0x79, 0x29, 0xBF, 0x2D,
 | 
			
		||||
 0xA0, 0x54, 0xA9, 0xD3, 0x68, 0x78, 0x3E, 0xFF, 0x9A, 0x42, 0x19, 0x1D, 0x65, 0xFE, 0x28, 0x20,
 | 
			
		||||
 0x09, 0xC5, 0x82, 0xA3, 0x41, 0xBE, 0x92, 0xFB, 0x46, 0xC0, 0x86, 0x69, 0x03, 0x93, 0x6D, 0xCB,
 | 
			
		||||
 0xDE, 0xB2, 0x77, 0x71, 0x64, 0x7F, 0x4D, 0xF7, 0x57, 0x4F, 0xD8, 0x5F, 0x34, 0x69, 0x58, 0x0B,
 | 
			
		||||
 0xE7, 0xB5, 0xAB, 0x8A, 0x4D, 0x6A, 0x83, 0xFB, 0xC4, 0xA7, 0x70, 0x3D, 0x6F, 0xB3, 0xCC, 0xB6,
 | 
			
		||||
 0x1A, 0xE4, 0x5F, 0x60, 0xD4, 0x31, 0xBA, 0x95, 0x2F, 0x92, 0xF4, 0x81, 0x7B, 0x18, 0x5B, 0x17,
 | 
			
		||||
 0x54, 0x26, 0x70, 0x49, 0xD5, 0x87, 0x34, 0xB9, 0xD3, 0x9C, 0x2F, 0x39, 0xC3, 0xB7, 0x3C, 0xA8,
 | 
			
		||||
 0x03, 0xE4, 0x37, 0x9C, 0x72, 0x39, 0xB0, 0xBF, 0x07, 0x5D, 0x33, 0x2A, 0x41, 0x79, 0xB1, 0x26,
 | 
			
		||||
 0x9B, 0xE6, 0x7C, 0x02, 0x82, 0x01, 0x70, 0xB1, 0xA3, 0x48, 0xCD, 0x2B, 0xCB, 0x98, 0x9B, 0x57,
 | 
			
		||||
 0x96, 0x54, 0xE2, 0x5F, 0x59, 0xCC, 0xDB, 0x9F, 0xFC, 0xDB, 0x4C, 0xF9, 0x7F, 0x5B, 0x28, 0x36,
 | 
			
		||||
 0x32, 0xF9, 0xE1, 0x09, 0xF7, 0x56, 0x3F, 0x45, 0xAD, 0x47, 0x51, 0xBB, 0xF7, 0xFF, 0x17, 0x53,
 | 
			
		||||
 0xE8, 0x9D, 0x36, 0x92, 0x29, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SPIFFS_MAXLENGTH_FILEPATH 32
 | 
			
		||||
const char *excludeListFile = "/.exclude.files";
 | 
			
		||||
 | 
			
		||||
typedef struct ExcludeListS {
 | 
			
		||||
    char *item;
 | 
			
		||||
    ExcludeListS *next;
 | 
			
		||||
} ExcludeList;
 | 
			
		||||
 | 
			
		||||
static ExcludeList *excludes = NULL;
 | 
			
		||||
 | 
			
		||||
static bool matchWild(const char *pattern, const char *testee) {
 | 
			
		||||
  const char *nxPat = NULL, *nxTst = NULL;
 | 
			
		||||
 | 
			
		||||
  while (*testee) {
 | 
			
		||||
    if (( *pattern == '?' ) || (*pattern == *testee)){
 | 
			
		||||
      pattern++;testee++;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (*pattern=='*'){
 | 
			
		||||
      nxPat=pattern++; nxTst=testee;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (nxPat){ 
 | 
			
		||||
      pattern = nxPat+1; testee=++nxTst;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  while (*pattern=='*'){pattern++;}  
 | 
			
		||||
  return (*pattern == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool addExclude(const char *item){
 | 
			
		||||
    size_t len = strlen(item);
 | 
			
		||||
    if(!len){
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    ExcludeList *e = (ExcludeList *)malloc(sizeof(ExcludeList));
 | 
			
		||||
    if(!e){
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    e->item = (char *)malloc(len+1);
 | 
			
		||||
    if(!e->item){
 | 
			
		||||
        free(e);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(e->item, item, len+1);
 | 
			
		||||
    e->next = excludes;
 | 
			
		||||
    excludes = e;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void loadExcludeList(fs::FS &_fs, const char *filename){
 | 
			
		||||
    static char linebuf[SPIFFS_MAXLENGTH_FILEPATH];
 | 
			
		||||
    fs::File excludeFile=_fs.open(filename, "r");
 | 
			
		||||
    if(!excludeFile){
 | 
			
		||||
        //addExclude("/*.js.gz");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    if(excludeFile.isDirectory()){
 | 
			
		||||
      excludeFile.close();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (excludeFile.size() > 0){
 | 
			
		||||
      uint8_t idx;
 | 
			
		||||
      bool isOverflowed = false;
 | 
			
		||||
      while (excludeFile.available()){
 | 
			
		||||
        linebuf[0] = '\0';
 | 
			
		||||
        idx = 0;
 | 
			
		||||
        int lastChar;
 | 
			
		||||
        do {
 | 
			
		||||
          lastChar = excludeFile.read();
 | 
			
		||||
          if(lastChar != '\r'){
 | 
			
		||||
            linebuf[idx++] = (char) lastChar;
 | 
			
		||||
          }
 | 
			
		||||
        } while ((lastChar >= 0) && (lastChar != '\n') && (idx < SPIFFS_MAXLENGTH_FILEPATH));
 | 
			
		||||
 | 
			
		||||
        if(isOverflowed){
 | 
			
		||||
          isOverflowed = (lastChar != '\n');
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        isOverflowed = (idx >= SPIFFS_MAXLENGTH_FILEPATH);
 | 
			
		||||
        linebuf[idx-1] = '\0';
 | 
			
		||||
        if(!addExclude(linebuf)){
 | 
			
		||||
            excludeFile.close();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    excludeFile.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isExcluded(fs::FS &_fs, const char *filename) {
 | 
			
		||||
  if(excludes == NULL){
 | 
			
		||||
      loadExcludeList(_fs, excludeListFile);
 | 
			
		||||
  }
 | 
			
		||||
  ExcludeList *e = excludes;
 | 
			
		||||
  while(e){
 | 
			
		||||
    if (matchWild(e->item, filename)){
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    e = e->next;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WEB HANDLER IMPLEMENTATION
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
SPIFFSEditor::SPIFFSEditor(const fs::FS& fs, const String& username, const String& password)
 | 
			
		||||
#else
 | 
			
		||||
SPIFFSEditor::SPIFFSEditor(const String& username, const String& password, const fs::FS& fs)
 | 
			
		||||
#endif
 | 
			
		||||
:_fs(fs)
 | 
			
		||||
,_username(username)
 | 
			
		||||
,_password(password)
 | 
			
		||||
,_authenticated(false)
 | 
			
		||||
,_startTime(0)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->url().equalsIgnoreCase("/edit")){
 | 
			
		||||
    if(request->method() == HTTP_GET){
 | 
			
		||||
      if(request->hasParam("list"))
 | 
			
		||||
        return true;
 | 
			
		||||
      if(request->hasParam("edit")){
 | 
			
		||||
        request->_tempFile = _fs.open(request->arg("edit"), "r");
 | 
			
		||||
        if(!request->_tempFile){
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        if(request->_tempFile.isDirectory()){
 | 
			
		||||
          request->_tempFile.close();
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
      if(request->hasParam("download")){
 | 
			
		||||
        request->_tempFile = _fs.open(request->arg("download"), "r");
 | 
			
		||||
        if(!request->_tempFile){
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        if(request->_tempFile.isDirectory()){
 | 
			
		||||
          request->_tempFile.close();
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
      request->addInterestingHeader("If-Modified-Since");
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    else if(request->method() == HTTP_POST)
 | 
			
		||||
      return true;
 | 
			
		||||
    else if(request->method() == HTTP_DELETE)
 | 
			
		||||
      return true;
 | 
			
		||||
    else if(request->method() == HTTP_PUT)
 | 
			
		||||
      return true;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  if(_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
    return request->requestAuthentication();
 | 
			
		||||
 | 
			
		||||
  if(request->method() == HTTP_GET){
 | 
			
		||||
    if(request->hasParam("list")){
 | 
			
		||||
      String path = request->getParam("list")->value();
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      File dir = _fs.open(path);
 | 
			
		||||
#else
 | 
			
		||||
      Dir dir = _fs.openDir(path);
 | 
			
		||||
#endif
 | 
			
		||||
      path = String();
 | 
			
		||||
      String output = "[";
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      File entry = dir.openNextFile();
 | 
			
		||||
      while(entry){
 | 
			
		||||
#else
 | 
			
		||||
      while(dir.next()){
 | 
			
		||||
        fs::File entry = dir.openFile("r");
 | 
			
		||||
#endif
 | 
			
		||||
        if (isExcluded(_fs, entry.name())) {
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
            entry = dir.openNextFile();
 | 
			
		||||
#endif
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (output != "[") output += ',';
 | 
			
		||||
        output += "{\"type\":\"";
 | 
			
		||||
        output += "file";
 | 
			
		||||
        output += "\",\"name\":\"";
 | 
			
		||||
        output += String(entry.name());
 | 
			
		||||
        output += "\",\"size\":";
 | 
			
		||||
        output += String(entry.size());
 | 
			
		||||
        output += "}";
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
        entry = dir.openNextFile();
 | 
			
		||||
#else
 | 
			
		||||
        entry.close();
 | 
			
		||||
#endif
 | 
			
		||||
      }
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
      dir.close();
 | 
			
		||||
#endif
 | 
			
		||||
      output += "]";
 | 
			
		||||
      request->send(200, "application/json", output);
 | 
			
		||||
      output = String();
 | 
			
		||||
    }
 | 
			
		||||
    else if(request->hasParam("edit") || request->hasParam("download")){
 | 
			
		||||
      request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const char * buildTime = __DATE__ " " __TIME__ " GMT";
 | 
			
		||||
      if (request->header("If-Modified-Since").equals(buildTime)) {
 | 
			
		||||
        request->send(304);
 | 
			
		||||
      } else {
 | 
			
		||||
        AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", edit_htm_gz, edit_htm_gz_len);
 | 
			
		||||
        response->addHeader("Content-Encoding", "gzip");
 | 
			
		||||
        response->addHeader("Last-Modified", buildTime);
 | 
			
		||||
        request->send(response);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if(request->method() == HTTP_DELETE){
 | 
			
		||||
    if(request->hasParam("path", true)){
 | 
			
		||||
        _fs.remove(request->getParam("path", true)->value());
 | 
			
		||||
      request->send(200, "", "DELETE: "+request->getParam("path", true)->value());
 | 
			
		||||
    } else
 | 
			
		||||
      request->send(404);
 | 
			
		||||
  } else if(request->method() == HTTP_POST){
 | 
			
		||||
    if(request->hasParam("data", true, true) && _fs.exists(request->getParam("data", true, true)->value()))
 | 
			
		||||
      request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value());
 | 
			
		||||
    else
 | 
			
		||||
      request->send(500);
 | 
			
		||||
  } else if(request->method() == HTTP_PUT){
 | 
			
		||||
    if(request->hasParam("path", true)){
 | 
			
		||||
      String filename = request->getParam("path", true)->value();
 | 
			
		||||
      if(_fs.exists(filename)){
 | 
			
		||||
        request->send(200);
 | 
			
		||||
      } else {
 | 
			
		||||
        fs::File f = _fs.open(filename, "w");
 | 
			
		||||
        if(f){
 | 
			
		||||
          f.write((uint8_t)0x00);
 | 
			
		||||
          f.close();
 | 
			
		||||
          request->send(200, "", "CREATE: "+filename);
 | 
			
		||||
        } else {
 | 
			
		||||
          request->send(500);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else
 | 
			
		||||
      request->send(400);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SPIFFSEditor::handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
 | 
			
		||||
  if(!index){
 | 
			
		||||
    if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())){
 | 
			
		||||
      _authenticated = true;
 | 
			
		||||
      request->_tempFile = _fs.open(filename, "w");
 | 
			
		||||
      _startTime = millis();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if(_authenticated && request->_tempFile){
 | 
			
		||||
    if(len){
 | 
			
		||||
      request->_tempFile.write(data,len);
 | 
			
		||||
    }
 | 
			
		||||
    if(final){
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								lib/ESP Async WebServer/src/SPIFFSEditor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/ESP Async WebServer/src/SPIFFSEditor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
#ifndef SPIFFSEditor_H_
 | 
			
		||||
#define SPIFFSEditor_H_
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
 | 
			
		||||
class SPIFFSEditor: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
    fs::FS _fs;
 | 
			
		||||
    String _username;
 | 
			
		||||
    String _password; 
 | 
			
		||||
    bool _authenticated;
 | 
			
		||||
    uint32_t _startTime;
 | 
			
		||||
  public:
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    SPIFFSEditor(const fs::FS& fs, const String& username=String(), const String& password=String());
 | 
			
		||||
#else
 | 
			
		||||
    SPIFFSEditor(const String& username=String(), const String& password=String(), const fs::FS& fs=SPIFFS);
 | 
			
		||||
#endif
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
 | 
			
		||||
    virtual bool isRequestHandlerTrivial() override final {return false;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										193
									
								
								lib/ESP Async WebServer/src/StringArray.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								lib/ESP Async WebServer/src/StringArray.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 STRINGARRAY_H_
 | 
			
		||||
#define STRINGARRAY_H_
 | 
			
		||||
 | 
			
		||||
#include "stddef.h"
 | 
			
		||||
#include "WString.h"
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
class LinkedListNode {
 | 
			
		||||
    T _value;
 | 
			
		||||
  public:
 | 
			
		||||
    LinkedListNode<T>* next;
 | 
			
		||||
    LinkedListNode(const T val): _value(val), next(nullptr) {}
 | 
			
		||||
    ~LinkedListNode(){}
 | 
			
		||||
    const T& value() const { return _value; };
 | 
			
		||||
    T& value(){ return _value; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T, template<typename> class Item = LinkedListNode>
 | 
			
		||||
class LinkedList {
 | 
			
		||||
  public:
 | 
			
		||||
    typedef Item<T> ItemType;
 | 
			
		||||
    typedef std::function<void(const T&)> OnRemove;
 | 
			
		||||
    typedef std::function<bool(const T&)> Predicate;
 | 
			
		||||
  private:
 | 
			
		||||
    ItemType* _root;
 | 
			
		||||
    OnRemove _onRemove;
 | 
			
		||||
 | 
			
		||||
    class Iterator {
 | 
			
		||||
      ItemType* _node;
 | 
			
		||||
    public:
 | 
			
		||||
      Iterator(ItemType* current = nullptr) : _node(current) {}
 | 
			
		||||
      Iterator(const Iterator& i) : _node(i._node) {}
 | 
			
		||||
      Iterator& operator ++() { _node = _node->next; return *this; }
 | 
			
		||||
      bool operator != (const Iterator& i) const { return _node != i._node; }
 | 
			
		||||
      const T& operator * () const { return _node->value(); }
 | 
			
		||||
      const T* operator -> () const { return &_node->value(); }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
  public:
 | 
			
		||||
    typedef const Iterator ConstIterator;
 | 
			
		||||
    ConstIterator begin() const { return ConstIterator(_root); }
 | 
			
		||||
    ConstIterator end() const { return ConstIterator(nullptr); }
 | 
			
		||||
 | 
			
		||||
    LinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
 | 
			
		||||
    ~LinkedList(){}
 | 
			
		||||
    void add(const T& t){
 | 
			
		||||
      auto it = new ItemType(t);
 | 
			
		||||
      if(!_root){
 | 
			
		||||
        _root = it;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto i = _root;
 | 
			
		||||
        while(i->next) i = i->next;
 | 
			
		||||
        i->next = it;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    T& front() const {
 | 
			
		||||
      return _root->value();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool isEmpty() const {
 | 
			
		||||
      return _root == nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    size_t length() const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        i++;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
    size_t count_if(Predicate predicate) const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if (!predicate){
 | 
			
		||||
          i++;
 | 
			
		||||
        }
 | 
			
		||||
        else if (predicate(it->value())) {
 | 
			
		||||
          i++;
 | 
			
		||||
        }
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
    const T* nth(size_t N) const {
 | 
			
		||||
      size_t i = 0;
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(i++ == N)
 | 
			
		||||
          return &(it->value());
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    bool remove(const T& t){
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      auto pit = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(it->value() == t){
 | 
			
		||||
          if(it == _root){
 | 
			
		||||
            _root = _root->next;
 | 
			
		||||
          } else {
 | 
			
		||||
            pit->next = it->next;
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          if (_onRemove) {
 | 
			
		||||
            _onRemove(it->value());
 | 
			
		||||
          }
 | 
			
		||||
          
 | 
			
		||||
          delete it;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        pit = it;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    bool remove_first(Predicate predicate){
 | 
			
		||||
      auto it = _root;
 | 
			
		||||
      auto pit = _root;
 | 
			
		||||
      while(it){
 | 
			
		||||
        if(predicate(it->value())){
 | 
			
		||||
          if(it == _root){
 | 
			
		||||
            _root = _root->next;
 | 
			
		||||
          } else {
 | 
			
		||||
            pit->next = it->next;
 | 
			
		||||
          }
 | 
			
		||||
          if (_onRemove) {
 | 
			
		||||
            _onRemove(it->value());
 | 
			
		||||
          }
 | 
			
		||||
          delete it;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        pit = it;
 | 
			
		||||
        it = it->next;
 | 
			
		||||
      }
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void free(){
 | 
			
		||||
      while(_root != nullptr){
 | 
			
		||||
        auto it = _root;
 | 
			
		||||
        _root = _root->next;
 | 
			
		||||
        if (_onRemove) {
 | 
			
		||||
          _onRemove(it->value());
 | 
			
		||||
        }
 | 
			
		||||
        delete it;
 | 
			
		||||
      }
 | 
			
		||||
      _root = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StringArray : public LinkedList<String> {
 | 
			
		||||
public:
 | 
			
		||||
  
 | 
			
		||||
  StringArray() : LinkedList(nullptr) {}
 | 
			
		||||
  
 | 
			
		||||
  bool containsIgnoreCase(const String& str){
 | 
			
		||||
    for (const auto& s : *this) {
 | 
			
		||||
      if (str.equalsIgnoreCase(s)) {
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* STRINGARRAY_H_ */
 | 
			
		||||
							
								
								
									
										238
									
								
								lib/ESP Async WebServer/src/WebAuthentication.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								lib/ESP Async WebServer/src/WebAuthentication.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,238 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 "WebAuthentication.h"
 | 
			
		||||
#include <libb64/cencode.h>
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#include "mbedtls/md5.h"
 | 
			
		||||
#else
 | 
			
		||||
#include "md5.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Basic Auth hash = base64("username:password")
 | 
			
		||||
 | 
			
		||||
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
 | 
			
		||||
  if(username == NULL || password == NULL || hash == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  size_t toencodeLen = strlen(username)+strlen(password)+1;
 | 
			
		||||
  size_t encodedLen = base64_encode_expected_len(toencodeLen);
 | 
			
		||||
  if(strlen(hash) != encodedLen)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  char *toencode = new char[toencodeLen+1];
 | 
			
		||||
  if(toencode == NULL){
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
 | 
			
		||||
  if(encoded == NULL){
 | 
			
		||||
    delete[] toencode;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  sprintf(toencode, "%s:%s", username, password);
 | 
			
		||||
  if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
 | 
			
		||||
    delete[] toencode;
 | 
			
		||||
    delete[] encoded;
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  delete[] toencode;
 | 
			
		||||
  delete[] encoded;
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
    mbedtls_md5_context _ctx;
 | 
			
		||||
#else
 | 
			
		||||
    md5_context_t _ctx;
 | 
			
		||||
#endif
 | 
			
		||||
  uint8_t i;
 | 
			
		||||
  uint8_t * _buf = (uint8_t*)malloc(16);
 | 
			
		||||
  if(_buf == NULL)
 | 
			
		||||
    return false;
 | 
			
		||||
  memset(_buf, 0x00, 16);
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
  mbedtls_md5_init(&_ctx);
 | 
			
		||||
  mbedtls_md5_update_ret (&_ctx,data,len);
 | 
			
		||||
  mbedtls_md5_finish_ret(&_ctx,data);
 | 
			
		||||
  mbedtls_internal_md5_process( &_ctx ,data);
 | 
			
		||||
// mbedtls_md5_starts(&_ctx);
 | 
			
		||||
// mbedtls_md5_update(&_ctx, data, len);
 | 
			
		||||
// mbedtls_md5_finish(&_ctx, _buf);
 | 
			
		||||
#else
 | 
			
		||||
  MD5Init(&_ctx);
 | 
			
		||||
  MD5Update(&_ctx, data, len);
 | 
			
		||||
  MD5Final(_buf, &_ctx);
 | 
			
		||||
#endif
 | 
			
		||||
  for(i = 0; i < 16; i++) {
 | 
			
		||||
    sprintf(output + (i * 2), "%02x", _buf[i]);
 | 
			
		||||
  }
 | 
			
		||||
  free(_buf);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String genRandomMD5(){
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
  uint32_t r = RANDOM_REG32;
 | 
			
		||||
#else
 | 
			
		||||
  uint32_t r = rand();
 | 
			
		||||
#endif
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
 | 
			
		||||
    return "";
 | 
			
		||||
  String res = String(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String stringMD5(const String& in){
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
 | 
			
		||||
    return "";
 | 
			
		||||
  String res = String(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String generateDigestHash(const char * username, const char * password, const char * realm){
 | 
			
		||||
  if(username == NULL || password == NULL || realm == NULL){
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
  char * out = (char*)malloc(33);
 | 
			
		||||
  String res = String(username);
 | 
			
		||||
  res.concat(":");
 | 
			
		||||
  res.concat(realm);
 | 
			
		||||
  res.concat(":");
 | 
			
		||||
  String in = res;
 | 
			
		||||
  in.concat(password);
 | 
			
		||||
  if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
 | 
			
		||||
    return "";
 | 
			
		||||
  res.concat(out);
 | 
			
		||||
  free(out);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String requestDigestAuthentication(const char * realm){
 | 
			
		||||
  String header = "realm=\"";
 | 
			
		||||
  if(realm == NULL)
 | 
			
		||||
    header.concat("asyncesp");
 | 
			
		||||
  else
 | 
			
		||||
    header.concat(realm);
 | 
			
		||||
  header.concat( "\", qop=\"auth\", nonce=\"");
 | 
			
		||||
  header.concat(genRandomMD5());
 | 
			
		||||
  header.concat("\", opaque=\"");
 | 
			
		||||
  header.concat(genRandomMD5());
 | 
			
		||||
  header.concat("\"");
 | 
			
		||||
  return header;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
 | 
			
		||||
  if(username == NULL || password == NULL || header == NULL || method == NULL){
 | 
			
		||||
    //os_printf("AUTH FAIL: missing requred fields\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String myHeader = String(header);
 | 
			
		||||
  int nextBreak = myHeader.indexOf(",");
 | 
			
		||||
  if(nextBreak < 0){
 | 
			
		||||
    //os_printf("AUTH FAIL: no variables\n");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String myUsername = String();
 | 
			
		||||
  String myRealm = String();
 | 
			
		||||
  String myNonce = String();
 | 
			
		||||
  String myUri = String();
 | 
			
		||||
  String myResponse = String();
 | 
			
		||||
  String myQop = String();
 | 
			
		||||
  String myNc = String();
 | 
			
		||||
  String myCnonce = String();
 | 
			
		||||
 | 
			
		||||
  myHeader += ", ";
 | 
			
		||||
  do {
 | 
			
		||||
    String avLine = myHeader.substring(0, nextBreak);
 | 
			
		||||
    avLine.trim();
 | 
			
		||||
    myHeader = myHeader.substring(nextBreak+1);
 | 
			
		||||
    nextBreak = myHeader.indexOf(",");
 | 
			
		||||
 | 
			
		||||
    int eqSign = avLine.indexOf("=");
 | 
			
		||||
    if(eqSign < 0){
 | 
			
		||||
      //os_printf("AUTH FAIL: no = sign\n");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    String varName = avLine.substring(0, eqSign);
 | 
			
		||||
    avLine = avLine.substring(eqSign + 1);
 | 
			
		||||
    if(avLine.startsWith("\"")){
 | 
			
		||||
      avLine = avLine.substring(1, avLine.length() - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(varName.equals("username")){
 | 
			
		||||
      if(!avLine.equals(username)){
 | 
			
		||||
        //os_printf("AUTH FAIL: username\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myUsername = avLine;
 | 
			
		||||
    } else if(varName.equals("realm")){
 | 
			
		||||
      if(realm != NULL && !avLine.equals(realm)){
 | 
			
		||||
        //os_printf("AUTH FAIL: realm\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myRealm = avLine;
 | 
			
		||||
    } else if(varName.equals("nonce")){
 | 
			
		||||
      if(nonce != NULL && !avLine.equals(nonce)){
 | 
			
		||||
        //os_printf("AUTH FAIL: nonce\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myNonce = avLine;
 | 
			
		||||
    } else if(varName.equals("opaque")){
 | 
			
		||||
      if(opaque != NULL && !avLine.equals(opaque)){
 | 
			
		||||
        //os_printf("AUTH FAIL: opaque\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(varName.equals("uri")){
 | 
			
		||||
      if(uri != NULL && !avLine.equals(uri)){
 | 
			
		||||
        //os_printf("AUTH FAIL: uri\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      myUri = avLine;
 | 
			
		||||
    } else if(varName.equals("response")){
 | 
			
		||||
      myResponse = avLine;
 | 
			
		||||
    } else if(varName.equals("qop")){
 | 
			
		||||
      myQop = avLine;
 | 
			
		||||
    } else if(varName.equals("nc")){
 | 
			
		||||
      myNc = avLine;
 | 
			
		||||
    } else if(varName.equals("cnonce")){
 | 
			
		||||
      myCnonce = avLine;
 | 
			
		||||
    }
 | 
			
		||||
  } while(nextBreak > 0);
 | 
			
		||||
 | 
			
		||||
  String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password));
 | 
			
		||||
  String ha2 = String(method) + ":" + myUri;
 | 
			
		||||
  String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);
 | 
			
		||||
 | 
			
		||||
  if(myResponse.equals(stringMD5(response))){
 | 
			
		||||
    //os_printf("AUTH SUCCESS\n");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //os_printf("AUTH FAIL: password\n");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								lib/ESP Async WebServer/src/WebAuthentication.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								lib/ESP Async WebServer/src/WebAuthentication.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 WEB_AUTHENTICATION_H_
 | 
			
		||||
#define WEB_AUTHENTICATION_H_
 | 
			
		||||
 | 
			
		||||
#include "Arduino.h"
 | 
			
		||||
 | 
			
		||||
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
 | 
			
		||||
String requestDigestAuthentication(const char * realm);
 | 
			
		||||
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
 | 
			
		||||
 | 
			
		||||
//for storing hashed versions on the device that can be authenticated against
 | 
			
		||||
String generateDigestHash(const char * username, const char * password, const char * realm);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										133
									
								
								lib/ESP Async WebServer/src/WebHandlerImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								lib/ESP Async WebServer/src/WebHandlerImpl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 ASYNCWEBSERVERHANDLERIMPL_H_
 | 
			
		||||
#define ASYNCWEBSERVERHANDLERIMPL_H_
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <regex>
 | 
			
		||||
 | 
			
		||||
#include "stddef.h"
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
class AsyncStaticWebHandler: public AsyncWebHandler {
 | 
			
		||||
   using File = fs::File;
 | 
			
		||||
   using FS = fs::FS;
 | 
			
		||||
  private:
 | 
			
		||||
    bool _getFile(AsyncWebServerRequest *request);
 | 
			
		||||
    bool _fileExists(AsyncWebServerRequest *request, const String& path);
 | 
			
		||||
    uint8_t _countBits(const uint8_t value) const;
 | 
			
		||||
  protected:
 | 
			
		||||
    FS _fs;
 | 
			
		||||
    String _uri;
 | 
			
		||||
    String _path;
 | 
			
		||||
    String _default_file;
 | 
			
		||||
    String _cache_control;
 | 
			
		||||
    String _last_modified;
 | 
			
		||||
    AwsTemplateProcessor _callback;
 | 
			
		||||
    bool _isDir;
 | 
			
		||||
    bool _gzipFirst;
 | 
			
		||||
    uint8_t _gzipStats;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final;
 | 
			
		||||
    AsyncStaticWebHandler& setIsDir(bool isDir);
 | 
			
		||||
    AsyncStaticWebHandler& setDefaultFile(const char* filename);
 | 
			
		||||
    AsyncStaticWebHandler& setCacheControl(const char* cache_control);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(const char* last_modified);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
 | 
			
		||||
  #ifdef ESP8266
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(time_t last_modified);
 | 
			
		||||
    AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
 | 
			
		||||
  #endif
 | 
			
		||||
    AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackWebHandler: public AsyncWebHandler {
 | 
			
		||||
  private:
 | 
			
		||||
  protected:
 | 
			
		||||
    String _uri;
 | 
			
		||||
    WebRequestMethodComposite _method;
 | 
			
		||||
    ArRequestHandlerFunction _onRequest;
 | 
			
		||||
    ArUploadHandlerFunction _onUpload;
 | 
			
		||||
    ArBodyHandlerFunction _onBody;
 | 
			
		||||
    bool _isRegex;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false){}
 | 
			
		||||
    void setUri(const String& uri){ 
 | 
			
		||||
      _uri = uri; 
 | 
			
		||||
       _isRegex = uri.startsWith("^") && uri.endsWith("$");
 | 
			
		||||
    }
 | 
			
		||||
    void setMethod(WebRequestMethodComposite method){ _method = method; }
 | 
			
		||||
    void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
 | 
			
		||||
    void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
 | 
			
		||||
    void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
 | 
			
		||||
 | 
			
		||||
    virtual bool canHandle(AsyncWebServerRequest *request) override final{
 | 
			
		||||
 | 
			
		||||
      if(!_onRequest)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      if(!(_method & request->method()))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      if (_isRegex) {
 | 
			
		||||
        std::regex rgx(_uri.c_str());
 | 
			
		||||
        std::smatch matches;
 | 
			
		||||
        std::string s(request->url().c_str());
 | 
			
		||||
        if(std::regex_search(s, matches, rgx)) {
 | 
			
		||||
          for (size_t i = 1; i < matches.size(); ++i) { // start from 1
 | 
			
		||||
            request->_addPathParam(matches[i].str().c_str());
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
      } else if (_uri.length() && _uri.endsWith("*")) {
 | 
			
		||||
        String uriTemplate = String(_uri);
 | 
			
		||||
	uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
 | 
			
		||||
        if (!request->url().startsWith(uriTemplate))
 | 
			
		||||
          return false;
 | 
			
		||||
      }
 | 
			
		||||
      else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
      request->addInterestingHeader("ANY");
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  
 | 
			
		||||
    virtual void handleRequest(AsyncWebServerRequest *request) override final {
 | 
			
		||||
      if(_onRequest)
 | 
			
		||||
        _onRequest(request);
 | 
			
		||||
      else
 | 
			
		||||
        request->send(500);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
 | 
			
		||||
      if(_onUpload)
 | 
			
		||||
        _onUpload(request, filename, index, data, len, final);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
 | 
			
		||||
      if(_onBody)
 | 
			
		||||
        _onBody(request, data, len, index, total);
 | 
			
		||||
    }
 | 
			
		||||
    virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
 | 
			
		||||
							
								
								
									
										220
									
								
								lib/ESP Async WebServer/src/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								lib/ESP Async WebServer/src/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,220 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
 | 
			
		||||
  : _fs(fs), _uri(uri), _path(path), _default_file("index.htm"), _cache_control(cache_control), _last_modified(""), _callback(nullptr)
 | 
			
		||||
{
 | 
			
		||||
  // Ensure leading '/'
 | 
			
		||||
  if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
 | 
			
		||||
  if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
 | 
			
		||||
 | 
			
		||||
  // If path ends with '/' we assume a hint that this is a directory to improve performance.
 | 
			
		||||
  // However - if it does not end with '/' we, can't assume a file, path can still be a directory.
 | 
			
		||||
  _isDir = _path[_path.length()-1] == '/';
 | 
			
		||||
 | 
			
		||||
  // Remove the trailing '/' so we can handle default file
 | 
			
		||||
  // Notice that root will be "" not "/"
 | 
			
		||||
  if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
 | 
			
		||||
  if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
 | 
			
		||||
 | 
			
		||||
  // Reset stats
 | 
			
		||||
  _gzipFirst = false;
 | 
			
		||||
  _gzipStats = 0xF8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){
 | 
			
		||||
  _isDir = isDir;
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){
 | 
			
		||||
  _default_file = String(filename);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){
 | 
			
		||||
  _cache_control = String(cache_control);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
 | 
			
		||||
  _last_modified = String(last_modified);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
 | 
			
		||||
  char result[30];
 | 
			
		||||
  strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified);
 | 
			
		||||
  return setLastModified((const char *)result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef ESP8266
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){
 | 
			
		||||
  return setLastModified((struct tm *)gmtime(&last_modified));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
 | 
			
		||||
  time_t last_modified;
 | 
			
		||||
  if(time(&last_modified) == 0) //time is not yet set
 | 
			
		||||
    return *this;
 | 
			
		||||
  return setLastModified(last_modified);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
 | 
			
		||||
  if(request->method() != HTTP_GET 
 | 
			
		||||
    || !request->url().startsWith(_uri) 
 | 
			
		||||
    || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
 | 
			
		||||
  ){
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (_getFile(request)) {
 | 
			
		||||
    // We interested in "If-Modified-Since" header to check if file was modified
 | 
			
		||||
    if (_last_modified.length())
 | 
			
		||||
      request->addInterestingHeader("If-Modified-Since");
 | 
			
		||||
 | 
			
		||||
    if(_cache_control.length())
 | 
			
		||||
      request->addInterestingHeader("If-None-Match");
 | 
			
		||||
 | 
			
		||||
    DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  // Remove the found uri
 | 
			
		||||
  String path = request->url().substring(_uri.length());
 | 
			
		||||
 | 
			
		||||
  // We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
 | 
			
		||||
  bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
 | 
			
		||||
 | 
			
		||||
  path = _path + path;
 | 
			
		||||
 | 
			
		||||
  // Do we have a file or .gz file
 | 
			
		||||
  if (!canSkipFileCheck && _fileExists(request, path))
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  // Can't handle if not default file
 | 
			
		||||
  if (_default_file.length() == 0)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Try to add default file, ensure there is a trailing '/' ot the path.
 | 
			
		||||
  if (path.length() == 0 || path[path.length()-1] != '/')
 | 
			
		||||
    path += "/";
 | 
			
		||||
  path += _default_file;
 | 
			
		||||
 | 
			
		||||
  return _fileExists(request, path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef ESP32
 | 
			
		||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
 | 
			
		||||
#else
 | 
			
		||||
#define FILE_IS_REAL(f) (f == true)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
 | 
			
		||||
{
 | 
			
		||||
  bool fileFound = false;
 | 
			
		||||
  bool gzipFound = false;
 | 
			
		||||
 | 
			
		||||
  String gzip = path + ".gz";
 | 
			
		||||
 | 
			
		||||
  if (_gzipFirst) {
 | 
			
		||||
    request->_tempFile = _fs.open(gzip, "r");
 | 
			
		||||
    gzipFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    if (!gzipFound){
 | 
			
		||||
      request->_tempFile = _fs.open(path, "r");
 | 
			
		||||
      fileFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    request->_tempFile = _fs.open(path, "r");
 | 
			
		||||
    fileFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    if (!fileFound){
 | 
			
		||||
      request->_tempFile = _fs.open(gzip, "r");
 | 
			
		||||
      gzipFound = FILE_IS_REAL(request->_tempFile);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool found = fileFound || gzipFound;
 | 
			
		||||
 | 
			
		||||
  if (found) {
 | 
			
		||||
    // Extract the file name from the path and keep it in _tempObject
 | 
			
		||||
    size_t pathLen = path.length();
 | 
			
		||||
    char * _tempPath = (char*)malloc(pathLen+1);
 | 
			
		||||
    snprintf(_tempPath, pathLen+1, "%s", path.c_str());
 | 
			
		||||
    request->_tempObject = (void*)_tempPath;
 | 
			
		||||
 | 
			
		||||
    // Calculate gzip statistic
 | 
			
		||||
    _gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
 | 
			
		||||
    if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
 | 
			
		||||
    else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
 | 
			
		||||
    else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
 | 
			
		||||
{
 | 
			
		||||
  uint8_t w = value;
 | 
			
		||||
  uint8_t n;
 | 
			
		||||
  for (n=0; w!=0; n++) w&=w-1;
 | 
			
		||||
  return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
 | 
			
		||||
{
 | 
			
		||||
  // Get the filename from request->_tempObject and free it
 | 
			
		||||
  String filename = String((char*)request->_tempObject);
 | 
			
		||||
  free(request->_tempObject);
 | 
			
		||||
  request->_tempObject = NULL;
 | 
			
		||||
  if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
 | 
			
		||||
      return request->requestAuthentication();
 | 
			
		||||
 | 
			
		||||
  if (request->_tempFile == true) {
 | 
			
		||||
    String etag = String(request->_tempFile.size());
 | 
			
		||||
    if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) {
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
      request->send(304); // Not modified
 | 
			
		||||
    } else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) {
 | 
			
		||||
      request->_tempFile.close();
 | 
			
		||||
      AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
 | 
			
		||||
      response->addHeader("Cache-Control", _cache_control);
 | 
			
		||||
      response->addHeader("ETag", etag);
 | 
			
		||||
      request->send(response);
 | 
			
		||||
    } else {
 | 
			
		||||
      AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
 | 
			
		||||
      if (_last_modified.length())
 | 
			
		||||
        response->addHeader("Last-Modified", _last_modified);
 | 
			
		||||
      if (_cache_control.length()){
 | 
			
		||||
        response->addHeader("Cache-Control", _cache_control);
 | 
			
		||||
        response->addHeader("ETag", etag);
 | 
			
		||||
      }
 | 
			
		||||
      request->send(response);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    request->send(404);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1009
									
								
								lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1009
									
								
								lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										136
									
								
								lib/ESP Async WebServer/src/WebResponseImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								lib/ESP Async WebServer/src/WebResponseImpl.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 ASYNCWEBSERVERRESPONSEIMPL_H_
 | 
			
		||||
#define ASYNCWEBSERVERRESPONSEIMPL_H_
 | 
			
		||||
 | 
			
		||||
#ifdef Arduino_h
 | 
			
		||||
// arduino is not compatible with std::vector
 | 
			
		||||
#undef min
 | 
			
		||||
#undef max
 | 
			
		||||
#endif
 | 
			
		||||
#include <vector>
 | 
			
		||||
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
 | 
			
		||||
 | 
			
		||||
class AsyncBasicResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncAbstractResponse: public AsyncWebServerResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    String _head;
 | 
			
		||||
    // Data is inserted into cache at begin(). 
 | 
			
		||||
    // This is inefficient with vector, but if we use some other container, 
 | 
			
		||||
    // we won't be able to access it as contiguous array of bytes when reading from it,
 | 
			
		||||
    // so by gaining performance in one place, we'll lose it in another.
 | 
			
		||||
    std::vector<uint8_t> _cache;
 | 
			
		||||
    size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
 | 
			
		||||
    size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
 | 
			
		||||
  protected:
 | 
			
		||||
    AwsTemplateProcessor _callback;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    void _respond(AsyncWebServerRequest *request);
 | 
			
		||||
    size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
 | 
			
		||||
    bool _sourceValid() const { return false; }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifndef TEMPLATE_PLACEHOLDER
 | 
			
		||||
#define TEMPLATE_PLACEHOLDER '%'
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TEMPLATE_PARAM_NAME_LENGTH 32
 | 
			
		||||
class AsyncFileResponse: public AsyncAbstractResponse {
 | 
			
		||||
  using File = fs::File;
 | 
			
		||||
  using FS = fs::FS;
 | 
			
		||||
  private:
 | 
			
		||||
    File _content;
 | 
			
		||||
    String _path;
 | 
			
		||||
    void _setContentType(const String& path);
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    ~AsyncFileResponse();
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncStreamResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    Stream *_content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncCallbackResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    AwsResponseFiller _content;
 | 
			
		||||
    size_t _filledLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncChunkedResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    AwsResponseFiller _content;
 | 
			
		||||
    size_t _filledLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return !!(_content); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AsyncProgmemResponse: public AsyncAbstractResponse {
 | 
			
		||||
  private:
 | 
			
		||||
    const uint8_t * _content;
 | 
			
		||||
    size_t _readLength;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
 | 
			
		||||
    bool _sourceValid() const { return true; }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class cbuf;
 | 
			
		||||
 | 
			
		||||
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
 | 
			
		||||
  private:
 | 
			
		||||
    cbuf *_content;
 | 
			
		||||
  public:
 | 
			
		||||
    AsyncResponseStream(const String& contentType, size_t bufferSize);
 | 
			
		||||
    ~AsyncResponseStream();
 | 
			
		||||
    bool _sourceValid() const { return (_state < RESPONSE_END); }
 | 
			
		||||
    virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
 | 
			
		||||
    size_t write(const uint8_t *data, size_t len);
 | 
			
		||||
    size_t write(uint8_t data);
 | 
			
		||||
    using Print::write;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */
 | 
			
		||||
							
								
								
									
										697
									
								
								lib/ESP Async WebServer/src/WebResponses.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								lib/ESP Async WebServer/src/WebResponses.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,697 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebResponseImpl.h"
 | 
			
		||||
#include "cbuf.h"
 | 
			
		||||
 | 
			
		||||
// Since ESP8266 does not link memchr by default, here's its implementation.
 | 
			
		||||
void* memchr(void* ptr, int ch, size_t count)
 | 
			
		||||
{
 | 
			
		||||
  unsigned char* p = static_cast<unsigned char*>(ptr);
 | 
			
		||||
  while(count--)
 | 
			
		||||
    if(*p++ == static_cast<unsigned char>(ch))
 | 
			
		||||
      return --p;
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Abstract Response
 | 
			
		||||
 * */
 | 
			
		||||
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
 | 
			
		||||
  switch (code) {
 | 
			
		||||
    case 100: return "Continue";
 | 
			
		||||
    case 101: return "Switching Protocols";
 | 
			
		||||
    case 200: return "OK";
 | 
			
		||||
    case 201: return "Created";
 | 
			
		||||
    case 202: return "Accepted";
 | 
			
		||||
    case 203: return "Non-Authoritative Information";
 | 
			
		||||
    case 204: return "No Content";
 | 
			
		||||
    case 205: return "Reset Content";
 | 
			
		||||
    case 206: return "Partial Content";
 | 
			
		||||
    case 300: return "Multiple Choices";
 | 
			
		||||
    case 301: return "Moved Permanently";
 | 
			
		||||
    case 302: return "Found";
 | 
			
		||||
    case 303: return "See Other";
 | 
			
		||||
    case 304: return "Not Modified";
 | 
			
		||||
    case 305: return "Use Proxy";
 | 
			
		||||
    case 307: return "Temporary Redirect";
 | 
			
		||||
    case 400: return "Bad Request";
 | 
			
		||||
    case 401: return "Unauthorized";
 | 
			
		||||
    case 402: return "Payment Required";
 | 
			
		||||
    case 403: return "Forbidden";
 | 
			
		||||
    case 404: return "Not Found";
 | 
			
		||||
    case 405: return "Method Not Allowed";
 | 
			
		||||
    case 406: return "Not Acceptable";
 | 
			
		||||
    case 407: return "Proxy Authentication Required";
 | 
			
		||||
    case 408: return "Request Time-out";
 | 
			
		||||
    case 409: return "Conflict";
 | 
			
		||||
    case 410: return "Gone";
 | 
			
		||||
    case 411: return "Length Required";
 | 
			
		||||
    case 412: return "Precondition Failed";
 | 
			
		||||
    case 413: return "Request Entity Too Large";
 | 
			
		||||
    case 414: return "Request-URI Too Large";
 | 
			
		||||
    case 415: return "Unsupported Media Type";
 | 
			
		||||
    case 416: return "Requested range not satisfiable";
 | 
			
		||||
    case 417: return "Expectation Failed";
 | 
			
		||||
    case 500: return "Internal Server Error";
 | 
			
		||||
    case 501: return "Not Implemented";
 | 
			
		||||
    case 502: return "Bad Gateway";
 | 
			
		||||
    case 503: return "Service Unavailable";
 | 
			
		||||
    case 504: return "Gateway Time-out";
 | 
			
		||||
    case 505: return "HTTP Version not supported";
 | 
			
		||||
    default:  return "";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServerResponse::AsyncWebServerResponse()
 | 
			
		||||
  : _code(0)
 | 
			
		||||
  , _headers(LinkedList<AsyncWebHeader *>([](AsyncWebHeader *h){ delete h; }))
 | 
			
		||||
  , _contentType()
 | 
			
		||||
  , _contentLength(0)
 | 
			
		||||
  , _sendContentLength(true)
 | 
			
		||||
  , _chunked(false)
 | 
			
		||||
  , _headLength(0)
 | 
			
		||||
  , _sentLength(0)
 | 
			
		||||
  , _ackedLength(0)
 | 
			
		||||
  , _writtenLength(0)
 | 
			
		||||
  , _state(RESPONSE_SETUP)
 | 
			
		||||
{
 | 
			
		||||
  for(auto header: DefaultHeaders::Instance()) {
 | 
			
		||||
    _headers.add(new AsyncWebHeader(header->name(), header->value()));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServerResponse::~AsyncWebServerResponse(){
 | 
			
		||||
  _headers.free();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setCode(int code){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _code = code;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setContentLength(size_t len){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _contentLength = len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::setContentType(const String& type){
 | 
			
		||||
  if(_state == RESPONSE_SETUP)
 | 
			
		||||
    _contentType = type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
 | 
			
		||||
  _headers.add(new AsyncWebHeader(name, value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String AsyncWebServerResponse::_assembleHead(uint8_t version){
 | 
			
		||||
  if(version){
 | 
			
		||||
    addHeader("Accept-Ranges","none");
 | 
			
		||||
    if(_chunked)
 | 
			
		||||
      addHeader("Transfer-Encoding","chunked");
 | 
			
		||||
  }
 | 
			
		||||
  String out = String();
 | 
			
		||||
  int bufSize = 300;
 | 
			
		||||
  char buf[bufSize];
 | 
			
		||||
 | 
			
		||||
  snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
 | 
			
		||||
  out.concat(buf);
 | 
			
		||||
 | 
			
		||||
  if(_sendContentLength) {
 | 
			
		||||
    snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
  if(_contentType.length()) {
 | 
			
		||||
    snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(const auto& header: _headers){
 | 
			
		||||
    snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str());
 | 
			
		||||
    out.concat(buf);
 | 
			
		||||
  }
 | 
			
		||||
  _headers.free();
 | 
			
		||||
 | 
			
		||||
  out.concat("\r\n");
 | 
			
		||||
  _headLength = out.length();
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
 | 
			
		||||
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
 | 
			
		||||
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
 | 
			
		||||
bool AsyncWebServerResponse::_sourceValid() const { return false; }
 | 
			
		||||
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
 | 
			
		||||
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ return 0; }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * String/Code Response
 | 
			
		||||
 * */
 | 
			
		||||
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
 | 
			
		||||
  _code = code;
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  if(_content.length()){
 | 
			
		||||
    _contentLength = _content.length();
 | 
			
		||||
    if(!_contentType.length())
 | 
			
		||||
      _contentType = "text/plain";
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Connection","close");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  _state = RESPONSE_HEADERS;
 | 
			
		||||
  String out = _assembleHead(request->version());
 | 
			
		||||
  size_t outLen = out.length();
 | 
			
		||||
  size_t space = request->client()->space();
 | 
			
		||||
  if(!_contentLength && space >= outLen){
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
  } else if(_contentLength && space >= outLen + _contentLength){
 | 
			
		||||
    out += _content;
 | 
			
		||||
    outLen += _contentLength;
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
  } else if(space && space < outLen){
 | 
			
		||||
    String partial = out.substring(0, space);
 | 
			
		||||
    _content = out.substring(space) + _content;
 | 
			
		||||
    _contentLength += outLen - space;
 | 
			
		||||
    _writtenLength += request->client()->write(partial.c_str(), partial.length());
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  } else if(space > outLen && space < (outLen + _contentLength)){
 | 
			
		||||
    size_t shift = space - outLen;
 | 
			
		||||
    outLen += shift;
 | 
			
		||||
    _sentLength += shift;
 | 
			
		||||
    out += _content.substring(0, shift);
 | 
			
		||||
    _content = _content.substring(shift);
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), outLen);
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  } else {
 | 
			
		||||
    _content = out + _content;
 | 
			
		||||
    _contentLength += outLen;
 | 
			
		||||
    _state = RESPONSE_CONTENT;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
 | 
			
		||||
  _ackedLength += len;
 | 
			
		||||
  if(_state == RESPONSE_CONTENT){
 | 
			
		||||
    size_t available = _contentLength - _sentLength;
 | 
			
		||||
    size_t space = request->client()->space();
 | 
			
		||||
    //we can fit in this packet
 | 
			
		||||
    if(space > available){
 | 
			
		||||
      _writtenLength += request->client()->write(_content.c_str(), available);
 | 
			
		||||
      _content = String();
 | 
			
		||||
      _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
      return available;
 | 
			
		||||
    }
 | 
			
		||||
    //send some data, the rest on ack
 | 
			
		||||
    String out = _content.substring(0, space);
 | 
			
		||||
    _content = _content.substring(space);
 | 
			
		||||
    _sentLength += space;
 | 
			
		||||
    _writtenLength += request->client()->write(out.c_str(), space);
 | 
			
		||||
    return space;
 | 
			
		||||
  } else if(_state == RESPONSE_WAIT_ACK){
 | 
			
		||||
    if(_ackedLength >= _writtenLength){
 | 
			
		||||
      _state = RESPONSE_END;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Abstract Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
 | 
			
		||||
{
 | 
			
		||||
  // In case of template processing, we're unable to determine real response size
 | 
			
		||||
  if(callback) {
 | 
			
		||||
    _contentLength = 0;
 | 
			
		||||
    _sendContentLength = false;
 | 
			
		||||
    _chunked = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
 | 
			
		||||
  addHeader("Connection","close");
 | 
			
		||||
  _head = _assembleHead(request->version());
 | 
			
		||||
  _state = RESPONSE_HEADERS;
 | 
			
		||||
  _ack(request, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
 | 
			
		||||
  if(!_sourceValid()){
 | 
			
		||||
    _state = RESPONSE_FAILED;
 | 
			
		||||
    request->client()->close();
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  _ackedLength += len;
 | 
			
		||||
  size_t space = request->client()->space();
 | 
			
		||||
 | 
			
		||||
  size_t headLen = _head.length();
 | 
			
		||||
  if(_state == RESPONSE_HEADERS){
 | 
			
		||||
    if(space >= headLen){
 | 
			
		||||
      _state = RESPONSE_CONTENT;
 | 
			
		||||
      space -= headLen;
 | 
			
		||||
    } else {
 | 
			
		||||
      String out = _head.substring(0, space);
 | 
			
		||||
      _head = _head.substring(space);
 | 
			
		||||
      _writtenLength += request->client()->write(out.c_str(), out.length());
 | 
			
		||||
      return out.length();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(_state == RESPONSE_CONTENT){
 | 
			
		||||
    size_t outLen;
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
      if(space <= 8){
 | 
			
		||||
        return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = space;
 | 
			
		||||
    } else if(!_sendContentLength){
 | 
			
		||||
      outLen = space;
 | 
			
		||||
    } else {
 | 
			
		||||
      outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
 | 
			
		||||
    if (!buf) {
 | 
			
		||||
      // os_printf("_ack malloc %d failed\n", outLen+headLen);
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(headLen){
 | 
			
		||||
      memcpy(buf, _head.c_str(), _head.length());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t readLen = 0;
 | 
			
		||||
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
      // HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
 | 
			
		||||
      // See RFC2616 sections 2, 3.6.1.
 | 
			
		||||
      readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
 | 
			
		||||
      if(readLen == RESPONSE_TRY_AGAIN){
 | 
			
		||||
          free(buf);
 | 
			
		||||
          return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen;
 | 
			
		||||
      while(outLen < headLen + 4) buf[outLen++] = ' ';
 | 
			
		||||
      buf[outLen++] = '\r';
 | 
			
		||||
      buf[outLen++] = '\n';
 | 
			
		||||
      outLen += readLen;
 | 
			
		||||
      buf[outLen++] = '\r';
 | 
			
		||||
      buf[outLen++] = '\n';
 | 
			
		||||
    } else {
 | 
			
		||||
      readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
 | 
			
		||||
      if(readLen == RESPONSE_TRY_AGAIN){
 | 
			
		||||
          free(buf);
 | 
			
		||||
          return 0;
 | 
			
		||||
      }
 | 
			
		||||
      outLen = readLen + headLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(headLen){
 | 
			
		||||
        _head = String();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(outLen){
 | 
			
		||||
        _writtenLength += request->client()->write((const char*)buf, outLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(_chunked){
 | 
			
		||||
        _sentLength += readLen;
 | 
			
		||||
    } else {
 | 
			
		||||
        _sentLength += outLen - headLen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    free(buf);
 | 
			
		||||
 | 
			
		||||
    if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
 | 
			
		||||
      _state = RESPONSE_WAIT_ACK;
 | 
			
		||||
    }
 | 
			
		||||
    return outLen;
 | 
			
		||||
 | 
			
		||||
  } else if(_state == RESPONSE_WAIT_ACK){
 | 
			
		||||
    if(!_sendContentLength || _ackedLength >= _writtenLength){
 | 
			
		||||
      _state = RESPONSE_END;
 | 
			
		||||
      if(!_chunked && !_sendContentLength)
 | 
			
		||||
        request->client()->close(true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
 | 
			
		||||
{
 | 
			
		||||
    // If we have something in cache, copy it to buffer
 | 
			
		||||
    const size_t readFromCache = std::min(len, _cache.size());
 | 
			
		||||
    if(readFromCache) {
 | 
			
		||||
      memcpy(data, _cache.data(), readFromCache);
 | 
			
		||||
      _cache.erase(_cache.begin(), _cache.begin() + readFromCache);
 | 
			
		||||
    }
 | 
			
		||||
    // If we need to read more...
 | 
			
		||||
    const size_t needFromFile = len - readFromCache;
 | 
			
		||||
    const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
 | 
			
		||||
    return readFromCache + readFromContent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
  if(!_callback)
 | 
			
		||||
    return _fillBuffer(data, len);
 | 
			
		||||
 | 
			
		||||
  const size_t originalLen = len;
 | 
			
		||||
  len = _readDataFromCacheOrContent(data, len);
 | 
			
		||||
  // Now we've read 'len' bytes, either from cache or from file
 | 
			
		||||
  // Search for template placeholders
 | 
			
		||||
  uint8_t* pTemplateStart = data;
 | 
			
		||||
  while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
 | 
			
		||||
    uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
 | 
			
		||||
    // temporary buffer to hold parameter name
 | 
			
		||||
    uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
 | 
			
		||||
    String paramName;
 | 
			
		||||
    // If closing placeholder is found:
 | 
			
		||||
    if(pTemplateEnd) {
 | 
			
		||||
      // prepare argument to callback
 | 
			
		||||
      const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1));
 | 
			
		||||
      if(paramNameLength) {
 | 
			
		||||
        memcpy(buf, pTemplateStart + 1, paramNameLength);
 | 
			
		||||
        buf[paramNameLength] = 0;
 | 
			
		||||
        paramName = String(reinterpret_cast<char*>(buf));
 | 
			
		||||
      } else { // double percent sign encountered, this is single percent sign escaped.
 | 
			
		||||
        // remove the 2nd percent sign
 | 
			
		||||
        memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
 | 
			
		||||
        len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
 | 
			
		||||
        ++pTemplateStart;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
 | 
			
		||||
      memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
 | 
			
		||||
      const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
 | 
			
		||||
      if(readFromCacheOrContent) {
 | 
			
		||||
        pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
 | 
			
		||||
        if(pTemplateEnd) {
 | 
			
		||||
          // prepare argument to callback
 | 
			
		||||
          *pTemplateEnd = 0;
 | 
			
		||||
          paramName = String(reinterpret_cast<char*>(buf));
 | 
			
		||||
          // Copy remaining read-ahead data into cache
 | 
			
		||||
          _cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
 | 
			
		||||
          pTemplateEnd = &data[len - 1];
 | 
			
		||||
        }
 | 
			
		||||
        else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
 | 
			
		||||
        {
 | 
			
		||||
          // but first, store read file data in cache
 | 
			
		||||
          _cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
 | 
			
		||||
          ++pTemplateStart;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
 | 
			
		||||
        ++pTemplateStart;
 | 
			
		||||
    }
 | 
			
		||||
    else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
 | 
			
		||||
      ++pTemplateStart;
 | 
			
		||||
    if(paramName.length()) {
 | 
			
		||||
      // call callback and replace with result.
 | 
			
		||||
      // Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
 | 
			
		||||
      // Data after pTemplateEnd may need to be moved.
 | 
			
		||||
      // The first byte of data after placeholder is located at pTemplateEnd + 1.
 | 
			
		||||
      // It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
 | 
			
		||||
      const String paramValue(_callback(paramName));
 | 
			
		||||
      const char* pvstr = paramValue.c_str();
 | 
			
		||||
      const unsigned int pvlen = paramValue.length();
 | 
			
		||||
      const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
 | 
			
		||||
      // make room for param value
 | 
			
		||||
      // 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
 | 
			
		||||
      if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
 | 
			
		||||
        _cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
 | 
			
		||||
        //2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
 | 
			
		||||
        memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
 | 
			
		||||
        len = originalLen; // fix issue with truncated data, not sure if it has any side effects
 | 
			
		||||
      } else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
 | 
			
		||||
        //2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
 | 
			
		||||
        //   Move the entire data after the placeholder
 | 
			
		||||
        memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
 | 
			
		||||
      // 3. replace placeholder with actual value
 | 
			
		||||
      memcpy(pTemplateStart, pvstr, numBytesCopied);
 | 
			
		||||
      // If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
 | 
			
		||||
      if(numBytesCopied < pvlen) {
 | 
			
		||||
        _cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
 | 
			
		||||
      } else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
 | 
			
		||||
        // there is some free room, fill it from cache
 | 
			
		||||
        const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
 | 
			
		||||
        const size_t totalFreeRoom = originalLen - len + roomFreed;
 | 
			
		||||
        len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
 | 
			
		||||
      } else { // result is copied fully; it is longer than placeholder text
 | 
			
		||||
        const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
 | 
			
		||||
        len = std::min(len + roomTaken, originalLen);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } // while(pTemplateStart)
 | 
			
		||||
  return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * File Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::~AsyncFileResponse(){
 | 
			
		||||
  if(_content)
 | 
			
		||||
    _content.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncFileResponse::_setContentType(const String& path){
 | 
			
		||||
  if (path.endsWith(".html")) _contentType = "text/html";
 | 
			
		||||
  else if (path.endsWith(".htm")) _contentType = "text/html";
 | 
			
		||||
  else if (path.endsWith(".css")) _contentType = "text/css";
 | 
			
		||||
  else if (path.endsWith(".json")) _contentType = "application/json";
 | 
			
		||||
  else if (path.endsWith(".js")) _contentType = "application/javascript";
 | 
			
		||||
  else if (path.endsWith(".png")) _contentType = "image/png";
 | 
			
		||||
  else if (path.endsWith(".gif")) _contentType = "image/gif";
 | 
			
		||||
  else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
 | 
			
		||||
  else if (path.endsWith(".ico")) _contentType = "image/x-icon";
 | 
			
		||||
  else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
 | 
			
		||||
  else if (path.endsWith(".eot")) _contentType = "font/eot";
 | 
			
		||||
  else if (path.endsWith(".woff")) _contentType = "font/woff";
 | 
			
		||||
  else if (path.endsWith(".woff2")) _contentType = "font/woff2";
 | 
			
		||||
  else if (path.endsWith(".ttf")) _contentType = "font/ttf";
 | 
			
		||||
  else if (path.endsWith(".xml")) _contentType = "text/xml";
 | 
			
		||||
  else if (path.endsWith(".pdf")) _contentType = "application/pdf";
 | 
			
		||||
  else if (path.endsWith(".zip")) _contentType = "application/zip";
 | 
			
		||||
  else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
 | 
			
		||||
  else _contentType = "text/plain";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _path = path;
 | 
			
		||||
 | 
			
		||||
  if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
 | 
			
		||||
    _path = _path+".gz";
 | 
			
		||||
    addHeader("Content-Encoding", "gzip");
 | 
			
		||||
    _callback = nullptr; // Unable to process zipped templates
 | 
			
		||||
    _sendContentLength = true;
 | 
			
		||||
    _chunked = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _content = fs.open(_path, "r");
 | 
			
		||||
  _contentLength = _content.size();
 | 
			
		||||
 | 
			
		||||
  if(contentType == "")
 | 
			
		||||
    _setContentType(path);
 | 
			
		||||
  else
 | 
			
		||||
    _contentType = contentType;
 | 
			
		||||
 | 
			
		||||
  int filenameStart = path.lastIndexOf('/') + 1;
 | 
			
		||||
  char buf[26+path.length()-filenameStart];
 | 
			
		||||
  char* filename = (char*)path.c_str() + filenameStart;
 | 
			
		||||
 | 
			
		||||
  if(download) {
 | 
			
		||||
    // set filename and force download
 | 
			
		||||
    snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
 | 
			
		||||
  } else {
 | 
			
		||||
    // set filename and force rendering
 | 
			
		||||
    snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Content-Disposition", buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _path = path;
 | 
			
		||||
 | 
			
		||||
  if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){
 | 
			
		||||
    addHeader("Content-Encoding", "gzip");
 | 
			
		||||
    _callback = nullptr; // Unable to process gzipped templates
 | 
			
		||||
    _sendContentLength = true;
 | 
			
		||||
    _chunked = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentLength = _content.size();
 | 
			
		||||
 | 
			
		||||
  if(contentType == "")
 | 
			
		||||
    _setContentType(path);
 | 
			
		||||
  else
 | 
			
		||||
    _contentType = contentType;
 | 
			
		||||
 | 
			
		||||
  int filenameStart = path.lastIndexOf('/') + 1;
 | 
			
		||||
  char buf[26+path.length()-filenameStart];
 | 
			
		||||
  char* filename = (char*)path.c_str() + filenameStart;
 | 
			
		||||
 | 
			
		||||
  if(download) {
 | 
			
		||||
    snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
 | 
			
		||||
  } else {
 | 
			
		||||
    snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
 | 
			
		||||
  }
 | 
			
		||||
  addHeader("Content-Disposition", buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  return _content.read(data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Stream Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = &stream;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t available = _content->available();
 | 
			
		||||
  size_t outLen = (available > len)?len:available;
 | 
			
		||||
  size_t i;
 | 
			
		||||
  for(i=0;i<outLen;i++)
 | 
			
		||||
    data[i] = _content->read();
 | 
			
		||||
  return outLen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Callback Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = callback;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  if(!len)
 | 
			
		||||
    _sendContentLength = false;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _filledLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t ret = _content(data, len, _filledLength);
 | 
			
		||||
  if(ret != RESPONSE_TRY_AGAIN){
 | 
			
		||||
      _filledLength += ret;
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chunked Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _content = callback;
 | 
			
		||||
  _contentLength = 0;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _sendContentLength = false;
 | 
			
		||||
  _chunked = true;
 | 
			
		||||
  _filledLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t ret = _content(data, len, _filledLength);
 | 
			
		||||
  if(ret != RESPONSE_TRY_AGAIN){
 | 
			
		||||
      _filledLength += ret;
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Progmem Response
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
 | 
			
		||||
  _code = code;
 | 
			
		||||
  _content = content;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _contentLength = len;
 | 
			
		||||
  _readLength = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
 | 
			
		||||
  size_t left = _contentLength - _readLength;
 | 
			
		||||
  if (left > len) {
 | 
			
		||||
    memcpy_P(data, _content + _readLength, len);
 | 
			
		||||
    _readLength += len;
 | 
			
		||||
    return len;
 | 
			
		||||
  }
 | 
			
		||||
  memcpy_P(data, _content + _readLength, left);
 | 
			
		||||
  _readLength += left;
 | 
			
		||||
  return left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Response Stream (You can print/write/printf to it, up to the contentLen bytes)
 | 
			
		||||
 * */
 | 
			
		||||
 | 
			
		||||
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize){
 | 
			
		||||
  _code = 200;
 | 
			
		||||
  _contentLength = 0;
 | 
			
		||||
  _contentType = contentType;
 | 
			
		||||
  _content = new cbuf(bufferSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncResponseStream::~AsyncResponseStream(){
 | 
			
		||||
  delete _content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
 | 
			
		||||
  return _content->read((char*)buf, maxLen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
 | 
			
		||||
  if(_started())
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if(len > _content->room()){
 | 
			
		||||
    size_t needed = len - _content->room();
 | 
			
		||||
    _content->resizeAdd(needed);
 | 
			
		||||
  }
 | 
			
		||||
  size_t written = _content->write((const char*)data, len);
 | 
			
		||||
  _contentLength += written;
 | 
			
		||||
  return written;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t AsyncResponseStream::write(uint8_t data){
 | 
			
		||||
  return write(&data, 1);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										193
									
								
								lib/ESP Async WebServer/src/WebServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								lib/ESP Async WebServer/src/WebServer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
/*
 | 
			
		||||
  Asynchronous WebServer library for Espressif MCUs
 | 
			
		||||
 | 
			
		||||
  Copyright (c) 2016 Hristo Gochkov. All rights reserved.
 | 
			
		||||
  This file is part of the esp8266 core for Arduino environment.
 | 
			
		||||
 | 
			
		||||
  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 "ESPAsyncWebServer.h"
 | 
			
		||||
#include "WebHandlerImpl.h"
 | 
			
		||||
 | 
			
		||||
bool ON_STA_FILTER(AsyncWebServerRequest *request) {
 | 
			
		||||
  return WiFi.localIP() == request->client()->localIP();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ON_AP_FILTER(AsyncWebServerRequest *request) {
 | 
			
		||||
  return WiFi.localIP() != request->client()->localIP();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AsyncWebServer::AsyncWebServer(uint16_t port)
 | 
			
		||||
  : _server(port)
 | 
			
		||||
  , _rewrites(LinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
 | 
			
		||||
  , _handlers(LinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
 | 
			
		||||
{
 | 
			
		||||
  _catchAllHandler = new AsyncCallbackWebHandler();
 | 
			
		||||
  if(_catchAllHandler == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
  _server.onClient([](void *s, AsyncClient* c){
 | 
			
		||||
    if(c == NULL)
 | 
			
		||||
      return;
 | 
			
		||||
    c->setRxTimeout(3);
 | 
			
		||||
    AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
 | 
			
		||||
    if(r == NULL){
 | 
			
		||||
      c->close(true);
 | 
			
		||||
      c->free();
 | 
			
		||||
      delete c;
 | 
			
		||||
    }
 | 
			
		||||
  }, this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebServer::~AsyncWebServer(){
 | 
			
		||||
  reset();  
 | 
			
		||||
  end();
 | 
			
		||||
  if(_catchAllHandler) delete _catchAllHandler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
 | 
			
		||||
  _rewrites.add(rewrite);
 | 
			
		||||
  return *rewrite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
 | 
			
		||||
  return _rewrites.remove(rewrite);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
 | 
			
		||||
  return addRewrite(new AsyncWebRewrite(from, to));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
 | 
			
		||||
  _handlers.add(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
 | 
			
		||||
  return _handlers.remove(handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::begin(){
 | 
			
		||||
  _server.setNoDelay(true);
 | 
			
		||||
  _server.begin();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::end(){
 | 
			
		||||
  _server.end();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if ASYNC_TCP_SSL_ENABLED
 | 
			
		||||
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
 | 
			
		||||
  _server.onSslFileRequest(cb, arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
 | 
			
		||||
  _server.beginSecure(cert, key, password);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
 | 
			
		||||
  delete request;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
 | 
			
		||||
  for(const auto& r: _rewrites){
 | 
			
		||||
    if (r->match(request)){
 | 
			
		||||
      request->_url = r->toUrl();
 | 
			
		||||
      request->_addGetParams(r->params());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
 | 
			
		||||
  for(const auto& h: _handlers){
 | 
			
		||||
    if (h->filter(request) && h->canHandle(request)){
 | 
			
		||||
      request->setHandler(h);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  request->addInterestingHeader("ANY");
 | 
			
		||||
  request->setHandler(_catchAllHandler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  handler->onUpload(onUpload);
 | 
			
		||||
  handler->onBody(onBody);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  handler->onUpload(onUpload);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->setMethod(method);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
 | 
			
		||||
  AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
 | 
			
		||||
  handler->setUri(uri);
 | 
			
		||||
  handler->onRequest(onRequest);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
 | 
			
		||||
  AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
 | 
			
		||||
  addHandler(handler);
 | 
			
		||||
  return *handler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onRequest(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onUpload(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
 | 
			
		||||
  _catchAllHandler->onBody(fn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AsyncWebServer::reset(){
 | 
			
		||||
  _rewrites.free();
 | 
			
		||||
  _handlers.free();
 | 
			
		||||
  
 | 
			
		||||
  if (_catchAllHandler != NULL){
 | 
			
		||||
    _catchAllHandler->onRequest(NULL);
 | 
			
		||||
    _catchAllHandler->onUpload(NULL);
 | 
			
		||||
    _catchAllHandler->onBody(NULL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										627
									
								
								lib/ESP Async WebServer/src/edit.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										627
									
								
								lib/ESP Async WebServer/src/edit.htm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,627 @@
 | 
			
		||||
<!--This is the plain html source of the hex encoded Editor-Page embedded in SPIFFSEditor.cpp -->
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
<title>ESP Editor</title>
 | 
			
		||||
<style type="text/css" media="screen">
 | 
			
		||||
.cm {
 | 
			
		||||
  z-index: 300;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 5px;
 | 
			
		||||
  border: 1px solid #444;
 | 
			
		||||
  background-color: #F5F5F5;
 | 
			
		||||
  display: none;
 | 
			
		||||
  box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
  font-family: sans-serif;
 | 
			
		||||
  font-weight:bold;
 | 
			
		||||
}
 | 
			
		||||
.cm ul {
 | 
			
		||||
  list-style: none;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
}
 | 
			
		||||
.cm li {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  min-width: 60px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.cm span {
 | 
			
		||||
  color: #444;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 6px;
 | 
			
		||||
}
 | 
			
		||||
.cm li:hover { background: #444; }
 | 
			
		||||
.cm li:hover span { color: #EEE; }
 | 
			
		||||
.tvu ul, .tvu li {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  list-style: none;
 | 
			
		||||
}
 | 
			
		||||
.tvu input {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
}
 | 
			
		||||
.tvu {
 | 
			
		||||
  font: normal 12px Verdana, Arial, Sans-serif;
 | 
			
		||||
  -moz-user-select: none;
 | 
			
		||||
  -webkit-user-select: none;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  color: #444;
 | 
			
		||||
  line-height: 16px;
 | 
			
		||||
}
 | 
			
		||||
.tvu span {
 | 
			
		||||
  margin-bottom:5px;
 | 
			
		||||
  padding: 0 0 0 18px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  height: 16px;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADoSURBVBgZBcExblNBGAbA2ceegTRBuIKOgiihSZNTcC5LUHAihNJR0kGKCDcYJY6D3/77MdOinTvzAgCw8ysThIvn/VojIyMjIyPP+bS1sUQIV2s95pBDDvmbP/mdkft83tpYguZq5Jh/OeaYh+yzy8hTHvNlaxNNczm+la9OTlar1UdA/+C2A4trRCnD3jS8BB1obq2Gk6GU6QbQAS4BUaYSQAf4bhhKKTFdAzrAOwAxEUAH+KEM01SY3gM6wBsEAQB0gJ+maZoC3gI6iPYaAIBJsiRmHU0AALOeFC3aK2cWAACUXe7+AwO0lc9eTHYTAAAAAElFTkSuQmCC') no-repeat;
 | 
			
		||||
  background-position: 0px 0px;
 | 
			
		||||
}
 | 
			
		||||
.tvu span:hover {
 | 
			
		||||
  text-decoration: underline;
 | 
			
		||||
}
 | 
			
		||||
@media screen and (-webkit-min-device-pixel-ratio:0){
 | 
			
		||||
  .tvu{
 | 
			
		||||
    -webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @-webkit-keyframes webkit-adjacent-element-selector-bugfix {
 | 
			
		||||
    from { 
 | 
			
		||||
      padding: 0;
 | 
			
		||||
    } 
 | 
			
		||||
    to { 
 | 
			
		||||
      padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#uploader { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  height:28px;
 | 
			
		||||
  line-height: 24px;
 | 
			
		||||
  padding-left: 10px;
 | 
			
		||||
  background-color: #444;
 | 
			
		||||
  color:#EEE;
 | 
			
		||||
}
 | 
			
		||||
#tree { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 28px;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width:160px;
 | 
			
		||||
  padding: 8px;
 | 
			
		||||
}
 | 
			
		||||
#editor, #preview { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 28px;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 160px;
 | 
			
		||||
  border-left:1px solid #EEE;
 | 
			
		||||
}
 | 
			
		||||
#preview {
 | 
			
		||||
  background-color: #EEE;
 | 
			
		||||
  padding:5px;
 | 
			
		||||
}
 | 
			
		||||
#loader { 
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 36%;
 | 
			
		||||
  right: 40%;
 | 
			
		||||
}
 | 
			
		||||
.loader {
 | 
			
		||||
    z-index: 10000;
 | 
			
		||||
    border: 8px solid #b5b5b5; /* Grey */
 | 
			
		||||
    border-top: 8px solid #3498db; /* Blue */
 | 
			
		||||
    border-bottom: 8px solid #3498db; /* Blue */
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
    width: 240px;
 | 
			
		||||
    height: 240px;
 | 
			
		||||
    animation: spin 2s linear infinite;
 | 
			
		||||
    display:none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes spin {
 | 
			
		||||
    0% { transform: rotate(0deg); }
 | 
			
		||||
    100% { transform: rotate(360deg); }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
if (typeof XMLHttpRequest === "undefined") {
 | 
			
		||||
  XMLHttpRequest = function () {
 | 
			
		||||
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
 | 
			
		||||
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
 | 
			
		||||
    try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
 | 
			
		||||
    throw new Error("This browser does not support XMLHttpRequest.");
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ge(a){
 | 
			
		||||
  return document.getElementById(a);
 | 
			
		||||
}
 | 
			
		||||
function ce(a){
 | 
			
		||||
  return document.createElement(a);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sortByKey(array, key) {
 | 
			
		||||
  return array.sort(function(a, b) {
 | 
			
		||||
    var x = a[key]; var y = b[key];
 | 
			
		||||
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var QueuedRequester = function () {
 | 
			
		||||
  this.queue = [];
 | 
			
		||||
  this.running = false;
 | 
			
		||||
  this.xmlhttp = null;
 | 
			
		||||
}
 | 
			
		||||
QueuedRequester.prototype = {
 | 
			
		||||
  _request: function(req){
 | 
			
		||||
    this.running = true;
 | 
			
		||||
    if(!req instanceof Object) return;
 | 
			
		||||
    var that = this;
 | 
			
		||||
    
 | 
			
		||||
    function ajaxCb(x,d){ return function(){
 | 
			
		||||
      if (x.readyState == 4){
 | 
			
		||||
        ge("loader").style.display = "none";
 | 
			
		||||
        d.callback(x.status, x.responseText);
 | 
			
		||||
        if(that.queue.length === 0) that.running = false;
 | 
			
		||||
        if(that.running) that._request(that.queue.shift());
 | 
			
		||||
      }
 | 
			
		||||
    }}
 | 
			
		||||
    
 | 
			
		||||
    ge("loader").style.display = "block";
 | 
			
		||||
    
 | 
			
		||||
    var p = "";
 | 
			
		||||
    if(req.params instanceof FormData){
 | 
			
		||||
      p = req.params;
 | 
			
		||||
    } else if(req.params instanceof Object){
 | 
			
		||||
      for (var key in req.params) {
 | 
			
		||||
        if(p === "")
 | 
			
		||||
          p += (req.method === "GET")?"?":"";
 | 
			
		||||
        else
 | 
			
		||||
          p += "&";
 | 
			
		||||
        p += encodeURIComponent(key)+"="+encodeURIComponent(req.params[key]);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    this.xmlhttp = new XMLHttpRequest();
 | 
			
		||||
    this.xmlhttp.onreadystatechange = ajaxCb(this.xmlhttp, req);
 | 
			
		||||
    if(req.method === "GET"){
 | 
			
		||||
      this.xmlhttp.open(req.method, req.url+p, true);
 | 
			
		||||
      this.xmlhttp.send();
 | 
			
		||||
    } else {
 | 
			
		||||
      this.xmlhttp.open(req.method, req.url, true);
 | 
			
		||||
      if(p instanceof String)
 | 
			
		||||
        this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
 | 
			
		||||
      this.xmlhttp.send(p);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  stop: function(){
 | 
			
		||||
    if(this.running) this.running = false;
 | 
			
		||||
    if(this.xmlhttp && this.xmlhttp.readyState < 4){
 | 
			
		||||
      this.xmlhttp.abort();
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  add: function(method, url, params, callback){
 | 
			
		||||
    this.queue.push({url:url,method:method,params:params,callback:callback});
 | 
			
		||||
    if(!this.running){
 | 
			
		||||
      this._request(this.queue.shift());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var requests = new QueuedRequester();
 | 
			
		||||
 | 
			
		||||
function createFileUploader(element, tree, editor){
 | 
			
		||||
  var xmlHttp;
 | 
			
		||||
  
 | 
			
		||||
  var refresh = ce("button");
 | 
			
		||||
  refresh.innerHTML = 'Refresh List';
 | 
			
		||||
  ge(element).appendChild(refresh);
 | 
			
		||||
 | 
			
		||||
  var input = ce("input");
 | 
			
		||||
  input.type = "file";
 | 
			
		||||
  input.multiple = false;
 | 
			
		||||
  input.name = "data";
 | 
			
		||||
  input.id="upload-select";
 | 
			
		||||
  ge(element).appendChild(input);
 | 
			
		||||
  
 | 
			
		||||
  var path = ce("input");
 | 
			
		||||
  path.id = "upload-path";
 | 
			
		||||
  path.type = "text";
 | 
			
		||||
  path.name = "path";
 | 
			
		||||
  path.defaultValue = "/";
 | 
			
		||||
  ge(element).appendChild(path);
 | 
			
		||||
  
 | 
			
		||||
  var button = ce("button");
 | 
			
		||||
  button.innerHTML = 'Upload';
 | 
			
		||||
  ge(element).appendChild(button);
 | 
			
		||||
  
 | 
			
		||||
  var mkfile = ce("button");
 | 
			
		||||
  mkfile.innerHTML = 'Create';
 | 
			
		||||
  ge(element).appendChild(mkfile);
 | 
			
		||||
 | 
			
		||||
  var filename     = ce("input");
 | 
			
		||||
  filename.id      = "editor-filename";
 | 
			
		||||
  filename.type    = "text";
 | 
			
		||||
  filename.disabled= true;
 | 
			
		||||
  filename.size    = 20;
 | 
			
		||||
  ge(element).appendChild(filename);
 | 
			
		||||
 | 
			
		||||
  var savefile = ce("button");
 | 
			
		||||
  savefile.innerHTML = ' Save ' ;
 | 
			
		||||
  ge(element).appendChild(savefile);
 | 
			
		||||
 | 
			
		||||
  function httpPostProcessRequest(status, responseText){
 | 
			
		||||
    if(status != 200)
 | 
			
		||||
      alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
    else
 | 
			
		||||
      tree.refreshPath(path.value);
 | 
			
		||||
  }
 | 
			
		||||
  function createPath(p){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("path", p);
 | 
			
		||||
    requests.add("PUT", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  mkfile.onclick = function(e){
 | 
			
		||||
    createPath(path.value);
 | 
			
		||||
    editor.loadUrl(path.value);
 | 
			
		||||
    path.value="/";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  savefile.onclick = function(e){
 | 
			
		||||
    editor.execCommand('saveCommand');
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  refresh.onclick = function(e){
 | 
			
		||||
    tree.refreshPath(path.value);
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  button.onclick = function(e){
 | 
			
		||||
    if(input.files.length === 0){
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("data", input.files[0], path.value);
 | 
			
		||||
    requests.add("POST", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
    var uploadPath= ge("upload-path");
 | 
			
		||||
    uploadPath.value="/";
 | 
			
		||||
    var uploadSelect= ge("upload-select");
 | 
			
		||||
    uploadSelect.value="";
 | 
			
		||||
  };
 | 
			
		||||
  input.onchange = function(e){
 | 
			
		||||
    if(input.files.length === 0) return;
 | 
			
		||||
    var filename = input.files[0].name;
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
    var name = /(.*)\.[^.]+$/.exec(filename)[1];
 | 
			
		||||
    if(typeof name !== undefined){
 | 
			
		||||
      filename = name;
 | 
			
		||||
    }
 | 
			
		||||
    path.value = "/"+filename+"."+ext;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createTree(element, editor){
 | 
			
		||||
  var preview = ge("preview");
 | 
			
		||||
  var treeRoot = ce("div");
 | 
			
		||||
  treeRoot.className = "tvu";
 | 
			
		||||
  ge(element).appendChild(treeRoot);
 | 
			
		||||
 | 
			
		||||
  function loadDownload(path){
 | 
			
		||||
    ge('download-frame').src = "/edit?download="+path;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function loadPreview(path){
 | 
			
		||||
    var edfname = ge("editor-filename");
 | 
			
		||||
    edfname.value=path;
 | 
			
		||||
    ge("editor").style.display = "none";
 | 
			
		||||
    preview.style.display = "block";
 | 
			
		||||
    preview.innerHTML = '<img src="/edit?edit='+path+'&_cb='+Date.now()+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function fillFileMenu(el, path){
 | 
			
		||||
    var list = ce("ul");
 | 
			
		||||
    el.appendChild(list);
 | 
			
		||||
    var action = ce("li");
 | 
			
		||||
    list.appendChild(action);
 | 
			
		||||
    if(isImageFile(path)){
 | 
			
		||||
      action.innerHTML = "<span>Preview</span>";
 | 
			
		||||
      action.onclick = function(e){
 | 
			
		||||
        loadPreview(path);
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
      };
 | 
			
		||||
    } else if(isTextFile(path)){
 | 
			
		||||
      action.innerHTML = "<span>Edit</span>";
 | 
			
		||||
      action.onclick = function(e){
 | 
			
		||||
        editor.loadUrl(path);
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    var download = ce("li");
 | 
			
		||||
    list.appendChild(download);
 | 
			
		||||
    download.innerHTML = "<span>Download</span>";
 | 
			
		||||
    download.onclick = function(e){
 | 
			
		||||
      loadDownload(path);
 | 
			
		||||
      if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
    };
 | 
			
		||||
    var delFile = ce("li");
 | 
			
		||||
    list.appendChild(delFile);
 | 
			
		||||
    delFile.innerHTML = "<span>Delete</span>";
 | 
			
		||||
    delFile.onclick = function(e){
 | 
			
		||||
      httpDelete(path);
 | 
			
		||||
      if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(el);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function showContextMenu(event, path, isfile){
 | 
			
		||||
    var divContext = ce("div");
 | 
			
		||||
    var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
 | 
			
		||||
    var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
 | 
			
		||||
    var left = event.clientX + scrollLeft;
 | 
			
		||||
    var top = event.clientY + scrollTop;
 | 
			
		||||
    divContext.className = 'cm';
 | 
			
		||||
    divContext.style.display = 'block';
 | 
			
		||||
    divContext.style.left = left + 'px';
 | 
			
		||||
    divContext.style.top = top + 'px';
 | 
			
		||||
    fillFileMenu(divContext, path);
 | 
			
		||||
    document.body.appendChild(divContext);
 | 
			
		||||
    var width = divContext.offsetWidth;
 | 
			
		||||
    var height = divContext.offsetHeight;
 | 
			
		||||
    divContext.onmouseout = function(e){
 | 
			
		||||
      if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
 | 
			
		||||
        if(document.body.getElementsByClassName('cm').length > 0) document.body.removeChild(divContext);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function createTreeLeaf(path, name, size){
 | 
			
		||||
    var leaf = ce("li");
 | 
			
		||||
    leaf.id = name;
 | 
			
		||||
    var label = ce("span");
 | 
			
		||||
    label.innerHTML = name;
 | 
			
		||||
    leaf.appendChild(label);
 | 
			
		||||
    leaf.onclick = function(e){
 | 
			
		||||
      if(isTextFile(leaf.id.toLowerCase())){
 | 
			
		||||
        editor.loadUrl(leaf.id);
 | 
			
		||||
      } else if(isImageFile(leaf.id.toLowerCase())){
 | 
			
		||||
        loadPreview(leaf.id);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    leaf.oncontextmenu = function(e){
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      e.stopPropagation();
 | 
			
		||||
      showContextMenu(e, leaf.id, true);
 | 
			
		||||
    };
 | 
			
		||||
    return leaf;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function addList(parent, path, items){
 | 
			
		||||
    sortByKey(items, 'name');
 | 
			
		||||
    var list = ce("ul");
 | 
			
		||||
    parent.appendChild(list);
 | 
			
		||||
    var ll = items.length;
 | 
			
		||||
    for(var i = 0; i < ll; i++){
 | 
			
		||||
      if(items[i].type === "file")
 | 
			
		||||
        list.appendChild(createTreeLeaf(path, items[i].name, items[i].size));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isTextFile(path){
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "txt":
 | 
			
		||||
        case "htm":
 | 
			
		||||
        case "html":
 | 
			
		||||
        case "js":
 | 
			
		||||
        case "css":
 | 
			
		||||
        case "xml":
 | 
			
		||||
        case "json":
 | 
			
		||||
        case "conf":
 | 
			
		||||
        case "ini":
 | 
			
		||||
        case "h":
 | 
			
		||||
        case "c":
 | 
			
		||||
        case "cpp":
 | 
			
		||||
        case "php":
 | 
			
		||||
        case "hex":
 | 
			
		||||
        case "ino":
 | 
			
		||||
        case "pde":
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function isImageFile(path){
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "png":
 | 
			
		||||
        case "jpg":
 | 
			
		||||
        case "gif":
 | 
			
		||||
        case "bmp":
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this.refreshPath = function(path){
 | 
			
		||||
    treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
    httpGet(treeRoot, "/");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  function delCb(path){
 | 
			
		||||
    return function(status, responseText){
 | 
			
		||||
      if(status != 200){
 | 
			
		||||
        alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
      } else {
 | 
			
		||||
        treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
        httpGet(treeRoot, "/");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function httpDelete(filename){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("path", filename);
 | 
			
		||||
    requests.add("DELETE", "/edit", formData, delCb(filename));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getCb(parent, path){
 | 
			
		||||
    return function(status, responseText){
 | 
			
		||||
      if(status == 200)
 | 
			
		||||
        addList(parent, path, JSON.parse(responseText));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function httpGet(parent, path){
 | 
			
		||||
    requests.add("GET", "/edit", { list: path }, getCb(parent, path));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httpGet(treeRoot, "/");
 | 
			
		||||
  return this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createEditor(element, file, lang, theme, type){
 | 
			
		||||
  function getLangFromFilename(filename){
 | 
			
		||||
    var lang = "plain";
 | 
			
		||||
    var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
    if(typeof ext !== undefined){
 | 
			
		||||
      switch(ext){
 | 
			
		||||
        case "txt": lang = "plain"; break;
 | 
			
		||||
        case "hex": lang = "plain"; break;
 | 
			
		||||
        case "conf": lang = "plain"; break;
 | 
			
		||||
        case "htm": lang = "html"; break;
 | 
			
		||||
        case "js": lang = "javascript"; break;
 | 
			
		||||
        case "h": lang = "c_cpp"; break;
 | 
			
		||||
        case "c": lang = "c_cpp"; break;
 | 
			
		||||
        case "cpp": lang = "c_cpp"; break;
 | 
			
		||||
        case "css":
 | 
			
		||||
        case "scss":
 | 
			
		||||
        case "php":
 | 
			
		||||
        case "html":
 | 
			
		||||
        case "json":
 | 
			
		||||
        case "xml":
 | 
			
		||||
        case "ini":  lang = ext;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return lang;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(typeof file === "undefined") file = "/index.html";
 | 
			
		||||
 | 
			
		||||
  if(typeof lang === "undefined"){
 | 
			
		||||
    lang = getLangFromFilename(file);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(typeof theme === "undefined") theme = "textmate";
 | 
			
		||||
 | 
			
		||||
  if(typeof type === "undefined"){
 | 
			
		||||
    type = "text/"+lang;
 | 
			
		||||
    if(lang === "c_cpp") type = "text/plain";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var editor = ace.edit(element);
 | 
			
		||||
  function httpPostProcessRequest(status, responseText){
 | 
			
		||||
    if(status != 200) alert("ERROR["+status+"]: "+responseText);
 | 
			
		||||
  }
 | 
			
		||||
  function httpPost(filename, data, type){
 | 
			
		||||
    var formData = new FormData();
 | 
			
		||||
    formData.append("data", new Blob([data], { type: type }), filename);
 | 
			
		||||
    requests.add("POST", "/edit", formData, httpPostProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
  function httpGetProcessRequest(status, responseText){
 | 
			
		||||
      ge("preview").style.display = "none";
 | 
			
		||||
      ge("editor").style.display = "block";
 | 
			
		||||
      if(status == 200)
 | 
			
		||||
        editor.setValue(responseText);
 | 
			
		||||
      else
 | 
			
		||||
        editor.setValue("");
 | 
			
		||||
      editor.clearSelection();
 | 
			
		||||
  }
 | 
			
		||||
  function httpGet(theUrl){
 | 
			
		||||
      requests.add("GET", "/edit", { edit: theUrl }, httpGetProcessRequest);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
  editor.setTheme("ace/theme/"+theme);
 | 
			
		||||
  editor.$blockScrolling = Infinity;
 | 
			
		||||
  editor.getSession().setUseSoftTabs(true);
 | 
			
		||||
  editor.getSession().setTabSize(2);
 | 
			
		||||
  editor.setHighlightActiveLine(true);
 | 
			
		||||
  editor.setShowPrintMargin(false);
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'saveCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        httpPost(file, editor.getValue()+"", type);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'undoCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-Z',  mac: 'Command-Z'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        editor.getSession().getUndoManager().undo(false);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.commands.addCommand({
 | 
			
		||||
      name: 'redoCommand',
 | 
			
		||||
      bindKey: {win: 'Ctrl-Shift-Z',  mac: 'Command-Shift-Z'},
 | 
			
		||||
      exec: function(editor) {
 | 
			
		||||
        editor.getSession().getUndoManager().redo(false);
 | 
			
		||||
      },
 | 
			
		||||
      readOnly: false
 | 
			
		||||
  });
 | 
			
		||||
  editor.loadUrl = function(filename){
 | 
			
		||||
    var edfname = ge("editor-filename");
 | 
			
		||||
    edfname.value=filename;
 | 
			
		||||
    file = filename;
 | 
			
		||||
    lang = getLangFromFilename(file);
 | 
			
		||||
    type = "text/"+lang;
 | 
			
		||||
    if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
    httpGet(file);
 | 
			
		||||
  };
 | 
			
		||||
  return editor;
 | 
			
		||||
}
 | 
			
		||||
function onBodyLoad(){
 | 
			
		||||
  var vars = {};
 | 
			
		||||
  var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
 | 
			
		||||
  var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
 | 
			
		||||
  var tree = createTree("tree", editor);
 | 
			
		||||
  createFileUploader("uploader", tree, editor);
 | 
			
		||||
  if(typeof vars.file === "undefined") vars.file = "/index.htm";
 | 
			
		||||
  editor.loadUrl(vars.file);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<script id='ace' src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript" charset="utf-8"></script>
 | 
			
		||||
<script>
 | 
			
		||||
  if  (typeof ace.edit == "undefined") {
 | 
			
		||||
    var script = document.createElement('script');
 | 
			
		||||
    script.src = "/ace.js";
 | 
			
		||||
    script.async = false;
 | 
			
		||||
    document.head.appendChild(script);
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="onBodyLoad();">
 | 
			
		||||
  <div id="loader" class="loader"></div>
 | 
			
		||||
  <div id="uploader"></div>
 | 
			
		||||
  <div id="tree"></div>
 | 
			
		||||
  <div id="editor"></div>
 | 
			
		||||
  <div id="preview" style="display:none;"></div>
 | 
			
		||||
  <iframe id=download-frame style='display:none;'></iframe>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -12,14 +12,14 @@
 | 
			
		||||
extra_configs =
 | 
			
		||||
  wifi_credentials.ini
 | 
			
		||||
 | 
			
		||||
[env:d1_mini]
 | 
			
		||||
platform = espressif8266
 | 
			
		||||
board = d1_mini
 | 
			
		||||
[env:de-timer]
 | 
			
		||||
platform = espressif32
 | 
			
		||||
board = ttgo-lora32-v1
 | 
			
		||||
board_build.filesystem = littlefs
 | 
			
		||||
board_build.f_flash = 80000000L
 | 
			
		||||
board_build.ldscript = eagle.flash.4m1m.ld
 | 
			
		||||
;board_build.f_flash = 80000000L
 | 
			
		||||
;board_build.ldscript = eagle.flash.4m1m.ld
 | 
			
		||||
 | 
			
		||||
monitor_filters = esp8266_exception_decoder
 | 
			
		||||
monitor_filters = esp32_exception_decoder
 | 
			
		||||
monitor_speed = 115200
 | 
			
		||||
 | 
			
		||||
upload_protocol = esptool
 | 
			
		||||
@@ -34,7 +34,6 @@ build_type = debug
 | 
			
		||||
build_flags=
 | 
			
		||||
  !python git_rev_macro.py
 | 
			
		||||
  -DSERIAL_DEBUG
 | 
			
		||||
  -DREMOTE_DEBUG
 | 
			
		||||
  ;-DWIFI_CLIENT
 | 
			
		||||
  ;-DCAPTIVE
 | 
			
		||||
  -DWIFI_AP_IP_GW=10,0,1,1
 | 
			
		||||
@@ -50,9 +49,13 @@ build_flags=
 | 
			
		||||
framework = arduino
 | 
			
		||||
lib_deps =
 | 
			
		||||
    smougenot/TM1637@0.0.0-alpha+sha.9486982048
 | 
			
		||||
    joaolopesf/RemoteDebug @ ^2.1.2
 | 
			
		||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
			
		||||
    ;me-no-dev/ESP Async WebServer @ ^1.2.3  ; local version included due to bug
 | 
			
		||||
    me-no-dev/AsyncTCP @ ^1.1.1
 | 
			
		||||
    sstaub/Ticker @ ^4.2.0
 | 
			
		||||
    adafruit/Adafruit INA219 @ ^1.1.1
 | 
			
		||||
    robtillaart/I2C_EEPROM @ ^1.5.2
 | 
			
		||||
    me-no-dev/ESP Async WebServer @ ^1.2.3
 | 
			
		||||
    sandeepmistry/LoRa @ ^0.8.0
 | 
			
		||||
    adafruit/Adafruit SSD1306 @ ^2.4.0
 | 
			
		||||
    adafruit/Adafruit GFX Library @ ^1.10.1
 | 
			
		||||
    adafruit/Adafruit BusIO @ ^1.5.0
 | 
			
		||||
    bblanchon/ArduinoJson @ ^6.19.4
 | 
			
		||||
							
								
								
									
										16
									
								
								src/common.h
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/common.h
									
									
									
									
									
								
							@@ -5,17 +5,17 @@
 | 
			
		||||
#define QUOTE(x) Q(x)
 | 
			
		||||
 | 
			
		||||
// Module connection pins (ESP GPIO-Nums)
 | 
			
		||||
#define CLK 16
 | 
			
		||||
#define DIO_FAC_1_7SEG 14
 | 
			
		||||
#define DIO_FAC_2_7SEG 12
 | 
			
		||||
#define DIO_FAC_3_7SEG 13
 | 
			
		||||
#define CLK 12
 | 
			
		||||
#define DIO_FAC_1_7SEG 13
 | 
			
		||||
#define DIO_FAC_2_7SEG 17
 | 
			
		||||
#define DIO_FAC_3_7SEG 21
 | 
			
		||||
 | 
			
		||||
#define DIO_FAC_1_TRG 0
 | 
			
		||||
#define DIO_FAC_1_TRG 36
 | 
			
		||||
#define FAC_1_TRG_PRESSED LOW
 | 
			
		||||
#define DIO_FAC_2_TRG 2
 | 
			
		||||
#define DIO_FAC_2_TRG 37
 | 
			
		||||
#define FAC_2_TRG_PRESSED LOW
 | 
			
		||||
#define DIO_FAC_3_TRG 15
 | 
			
		||||
#define FAC_3_TRG_PRESSED HIGH
 | 
			
		||||
#define DIO_FAC_3_TRG 38
 | 
			
		||||
#define FAC_3_TRG_PRESSED LOW
 | 
			
		||||
 | 
			
		||||
#ifndef HOST_NAME
 | 
			
		||||
#define HOST_NAME "DE_Timer_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								src/lora_net.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/lora_net.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#include "lora_net.h"
 | 
			
		||||
 | 
			
		||||
uint8_t LoRa_Init()
 | 
			
		||||
{
 | 
			
		||||
  uint8_t success = false;
 | 
			
		||||
  // SPI LoRa pins
 | 
			
		||||
  SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
 | 
			
		||||
  // setup LoRa transceiver module
 | 
			
		||||
  LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
 | 
			
		||||
 | 
			
		||||
  if (!LoRa.begin(LORA_BAND))
 | 
			
		||||
  {
 | 
			
		||||
    Serial.println("Starting LoRa failed!");
 | 
			
		||||
    success = false;
 | 
			
		||||
  }
 | 
			
		||||
  Serial.println("LoRa Initializing OK!");
 | 
			
		||||
  success = true;
 | 
			
		||||
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								src/lora_net.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/lora_net.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#ifndef _LORA_NET_H_
 | 
			
		||||
#define _LORA_NET_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <SPI.h>
 | 
			
		||||
#include <LoRa.h>
 | 
			
		||||
 | 
			
		||||
// define the pins used by the LoRa transceiver module
 | 
			
		||||
#define LORA_SCK 5
 | 
			
		||||
#define LORA_MISO 19
 | 
			
		||||
#define LORA_MOSI 27
 | 
			
		||||
#define LORA_SS 18
 | 
			
		||||
#define LORA_RST 14
 | 
			
		||||
#define LORA_DIO0 26
 | 
			
		||||
 | 
			
		||||
// 433E6 for Asia
 | 
			
		||||
// 866E6 for Europe
 | 
			
		||||
// 915E6 for North America
 | 
			
		||||
#define LORA_BAND 8681E5
 | 
			
		||||
 | 
			
		||||
uint8_t LoRa_Init();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										270
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										270
									
								
								src/main.cpp
									
									
									
									
									
								
							@@ -2,14 +2,15 @@
 | 
			
		||||
#include <TM1637Display.h>
 | 
			
		||||
#include <Ticker.h>
 | 
			
		||||
#include <DNSServer.h>
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#include <ESP8266mDNS.h>
 | 
			
		||||
#include <WiFi.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPmDNS.h>
 | 
			
		||||
#include <ArduinoOTA.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <Adafruit_INA219.h>
 | 
			
		||||
#include <ArduinoJson.h>
 | 
			
		||||
 | 
			
		||||
// local includes
 | 
			
		||||
#include "defaults.h"
 | 
			
		||||
@@ -18,23 +19,17 @@
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "dtc.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
#ifdef REMOTE_DEBUG
 | 
			
		||||
#include <RemoteDebug.h>
 | 
			
		||||
#include "rmtdbghelp.h"
 | 
			
		||||
#else
 | 
			
		||||
#define debugV Serial.println
 | 
			
		||||
#define debugE Serial.println
 | 
			
		||||
#endif
 | 
			
		||||
#include "lora_net.h"
 | 
			
		||||
#include "oled_display.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
#include <ESP8266WiFiMulti.h>
 | 
			
		||||
#include <WiFiMulti.h>
 | 
			
		||||
 | 
			
		||||
const char *ssid = QUOTE(WIFI_SSID);
 | 
			
		||||
const char *password = QUOTE(WIFI_PASSWORD);
 | 
			
		||||
const uint32_t connectTimeoutMs = 5000;
 | 
			
		||||
 | 
			
		||||
ESP8266WiFiMulti wifiMulti;
 | 
			
		||||
WiFiMulti wifiMulti;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void SevenSeg_Output();
 | 
			
		||||
@@ -47,26 +42,15 @@ void SystemShutdown();
 | 
			
		||||
void SetBatteryType(batteryType_t type);
 | 
			
		||||
void ProcessKeyCombos();
 | 
			
		||||
void OverrideDisplay(const uint8_t *message, uint32_t time);
 | 
			
		||||
 | 
			
		||||
#ifdef REMOTE_DEBUG
 | 
			
		||||
RemoteDebug Debug;
 | 
			
		||||
String IpAddress2String(const IPAddress &ipAddress);
 | 
			
		||||
void processCmdRemoteDebug();
 | 
			
		||||
void RemoteDebug_formatCFG();
 | 
			
		||||
void RemoteDebug_formatPersistence();
 | 
			
		||||
void RemotDebug_printSystemInfo();
 | 
			
		||||
void RemoteDebug_printWifiInfo();
 | 
			
		||||
void RemoteDebug_CheckEEPOM();
 | 
			
		||||
void RemoteDebug_dumpConfig();
 | 
			
		||||
void RemoteDebug_dumpPersistance();
 | 
			
		||||
void RemoteDebug_ShowDTCs();
 | 
			
		||||
#endif
 | 
			
		||||
void LoRaPublish_callback();
 | 
			
		||||
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
void wifiMaintainConnectionTicker_callback();
 | 
			
		||||
Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint32_t getESPChipID();
 | 
			
		||||
 | 
			
		||||
TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG);
 | 
			
		||||
TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG);
 | 
			
		||||
@@ -82,6 +66,7 @@ Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS);
 | 
			
		||||
Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS);
 | 
			
		||||
Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS);
 | 
			
		||||
Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 0, MILLIS);
 | 
			
		||||
Ticker LoRaPublishTicker(LoRaPublish_callback, 60000, 0, MILLIS);
 | 
			
		||||
 | 
			
		||||
uint8_t Faction_1_dot = 0;
 | 
			
		||||
uint8_t Faction_2_dot = 0;
 | 
			
		||||
@@ -101,34 +86,24 @@ const uint8_t sevenSeg_file[] = {0x71, 0x30, 0x38, 0x79};
 | 
			
		||||
 | 
			
		||||
void setup()
 | 
			
		||||
{
 | 
			
		||||
  system_update_cpu_freq(SYS_CPU_80MHZ);
 | 
			
		||||
  setCpuFrequencyMhz(80);
 | 
			
		||||
  WiFi.setAutoReconnect(false);
 | 
			
		||||
  WiFi.persistent(false);
 | 
			
		||||
  WiFi.disconnect();
 | 
			
		||||
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
  Serial.print("\n\n\n");
 | 
			
		||||
 | 
			
		||||
  strcpy(globals.DeviceName, DEVICE_NAME);
 | 
			
		||||
  snprintf(globals.DeviceName_ID, 42, "%s_%08X", globals.DeviceName, ESP.getChipId());
 | 
			
		||||
  snprintf(globals.DeviceName_ID, 42, "%s_%08X", globals.DeviceName, getESPChipID());
 | 
			
		||||
 | 
			
		||||
  if (LoRa_Init())
 | 
			
		||||
    LoRaPublishTicker.start();
 | 
			
		||||
  OLED_Init();
 | 
			
		||||
 | 
			
		||||
  pinMode(DIO_FAC_1_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_2_TRG, INPUT_PULLUP);
 | 
			
		||||
  pinMode(DIO_FAC_3_TRG, INPUT);
 | 
			
		||||
 | 
			
		||||
#ifdef REMOTE_DEBUG
 | 
			
		||||
  if (MDNS.begin(globals.DeviceName_ID))
 | 
			
		||||
    MDNS.addService("telnet", "tcp", 23);
 | 
			
		||||
 | 
			
		||||
  Debug.begin(globals.DeviceName_ID);
 | 
			
		||||
  Debug.setResetCmdEnabled(true);
 | 
			
		||||
  Debug.showProfiler(false);
 | 
			
		||||
  Debug.showColors(true);
 | 
			
		||||
  Debug.setPassword(QUOTE(ADMIN_PASSWORD));
 | 
			
		||||
  Debug.setSerialEnabled(true);
 | 
			
		||||
  Debug.showDebugLevel(true);
 | 
			
		||||
 | 
			
		||||
  Debug.setHelpProjectsCmds(helpCmd);
 | 
			
		||||
  Debug.setCallBackProjectCmds(&processCmdRemoteDebug);
 | 
			
		||||
#endif
 | 
			
		||||
  pinMode(DIO_FAC_3_TRG, INPUT_PULLUP);
 | 
			
		||||
 | 
			
		||||
#ifdef SERIAL_DEBUG
 | 
			
		||||
  Serial.setDebugOutput(true);
 | 
			
		||||
@@ -147,6 +122,9 @@ void setup()
 | 
			
		||||
  wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
 | 
			
		||||
  WiFiMaintainConnectionTicker.start();
 | 
			
		||||
#else
 | 
			
		||||
  WiFi.mode(WIFI_AP);
 | 
			
		||||
  WiFi.begin(QUOTE(DEVICE_NAME), QUOTE(WIFI_AP_PASSWORD));
 | 
			
		||||
  WiFi.setSleep(true);
 | 
			
		||||
  WiFi.mode(WIFI_OFF);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -217,16 +195,15 @@ void loop()
 | 
			
		||||
  FactionTicker.update();
 | 
			
		||||
  InputGetterTicker.update();
 | 
			
		||||
  PowerMonitorTicker.update();
 | 
			
		||||
  LoRaPublishTicker.update();
 | 
			
		||||
  ArduinoOTA.handle();
 | 
			
		||||
  SevenSeg_Output();
 | 
			
		||||
  EEPROM_Process();
 | 
			
		||||
  OLED_Process();
 | 
			
		||||
 | 
			
		||||
#ifdef CAPTIVE
 | 
			
		||||
  dnsServer.processNextRequest();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef REMOTE_DEBUG
 | 
			
		||||
  Debug.handle();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef WIFI_CLIENT
 | 
			
		||||
  WiFiMaintainConnectionTicker.update();
 | 
			
		||||
#endif
 | 
			
		||||
@@ -374,12 +351,12 @@ void powerMonitorTicker_callback()
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  debugV("Battery Level: %d %%", globals.battery_level);
 | 
			
		||||
  debugV("Bus Voltage: %f V", busvoltage);
 | 
			
		||||
  debugV("Shunt Voltage: %f mV", shuntvoltage);
 | 
			
		||||
  debugV("Load Voltage: %f V", globals.loadvoltage);
 | 
			
		||||
  debugV("Current: %f mA", current_mA);
 | 
			
		||||
  debugV("Power: %f mW", power_mW);
 | 
			
		||||
  // Serial.printf("Battery Level: %d %%\n", globals.battery_level);
 | 
			
		||||
  // Serial.printf("Bus Voltage: %f V\n", busvoltage);
 | 
			
		||||
  // Serial.printf("Shunt Voltage: %f mV\n", shuntvoltage);
 | 
			
		||||
  // Serial.printf("Load Voltage: %f V\n", globals.loadvoltage);
 | 
			
		||||
  // Serial.printf("Current: %f mA\n", current_mA);
 | 
			
		||||
  // Serial.printf("Power: %f mW\n", power_mW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EEPROMCyclicPDS_callback()
 | 
			
		||||
@@ -433,164 +410,13 @@ void SystemShutdown()
 | 
			
		||||
  ESP.restart();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef REMOTE_DEBUG
 | 
			
		||||
void processCmdRemoteDebug()
 | 
			
		||||
{
 | 
			
		||||
  String lastCmd = Debug.getLastCommand();
 | 
			
		||||
 | 
			
		||||
  if (lastCmd == "sysinfo")
 | 
			
		||||
    RemotDebug_printSystemInfo();
 | 
			
		||||
  else if (lastCmd == "netinfo")
 | 
			
		||||
    RemoteDebug_printWifiInfo();
 | 
			
		||||
  else if (lastCmd == "formatCFG")
 | 
			
		||||
    RemoteDebug_formatCFG();
 | 
			
		||||
  else if (lastCmd == "formatPDS")
 | 
			
		||||
    RemoteDebug_formatPersistence();
 | 
			
		||||
  else if (lastCmd == "checkEE")
 | 
			
		||||
    RemoteDebug_CheckEEPOM();
 | 
			
		||||
  else if (lastCmd == "dumpEE1k")
 | 
			
		||||
    dumpEEPROM(0, 1024);
 | 
			
		||||
  else if (lastCmd == "dumpEE")
 | 
			
		||||
    dumpEEPROM(0, EEPROM_SIZE_BYTES);
 | 
			
		||||
  else if (lastCmd == "resetPageEE")
 | 
			
		||||
    MovePersistencePage_EEPROM(true);
 | 
			
		||||
  else if (lastCmd == "dumpCFG")
 | 
			
		||||
    RemoteDebug_dumpConfig();
 | 
			
		||||
  else if (lastCmd == "dumpPDS")
 | 
			
		||||
    RemoteDebug_dumpPersistance();
 | 
			
		||||
  else if (lastCmd == "saveEE")
 | 
			
		||||
    StoreConfig_EEPROM();
 | 
			
		||||
  else if (lastCmd == "showdtc")
 | 
			
		||||
    RemoteDebug_ShowDTCs();
 | 
			
		||||
  else if (lastCmd == "bat_3s")
 | 
			
		||||
    SetBatteryType(BATTERY_LIPO_3S);
 | 
			
		||||
  else if (lastCmd == "bat_2s")
 | 
			
		||||
    SetBatteryType(BATTERY_LIPO_2S);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_formatCFG()
 | 
			
		||||
{
 | 
			
		||||
  debugA("Formatting Config-EEPROM and reseting to default");
 | 
			
		||||
  FormatConfig_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_formatPersistence()
 | 
			
		||||
{
 | 
			
		||||
  debugA("Formatting Persistence-EEPROM and reseting to default");
 | 
			
		||||
  FormatPersistence_EEPROM();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemotDebug_printSystemInfo()
 | 
			
		||||
{
 | 
			
		||||
  debugA("DE Timer Mk1");
 | 
			
		||||
  debugA("Hostname: %s", globals.DeviceName_ID);
 | 
			
		||||
 | 
			
		||||
  FlashMode_t ideMode = ESP.getFlashChipMode();
 | 
			
		||||
  debugA("Sdk version: %s", ESP.getSdkVersion());
 | 
			
		||||
  debugA("Core Version: %s", ESP.getCoreVersion().c_str());
 | 
			
		||||
  debugA("Boot Version: %u", ESP.getBootVersion());
 | 
			
		||||
  debugA("Boot Mode: %u", ESP.getBootMode());
 | 
			
		||||
  debugA("CPU Frequency: %u MHz", ESP.getCpuFreqMHz());
 | 
			
		||||
  debugA("Reset reason: %s", ESP.getResetReason().c_str());
 | 
			
		||||
  debugA("Flash Size: %d", ESP.getFlashChipRealSize());
 | 
			
		||||
  debugA("Flash Size IDE: %d", ESP.getFlashChipSize());
 | 
			
		||||
  debugA("Flash ide mode:  %s", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT"
 | 
			
		||||
                                                         : ideMode == FM_DIO    ? "DIO"
 | 
			
		||||
                                                         : ideMode == FM_DOUT   ? "DOUT"
 | 
			
		||||
                                                                                : "UNKNOWN"));
 | 
			
		||||
  debugA("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD));
 | 
			
		||||
  debugA("Git-Revison: %s", GIT_REV);
 | 
			
		||||
  debugA("I2C EEPROM Size: %d", GetEESize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_dumpConfig()
 | 
			
		||||
{
 | 
			
		||||
  debugA("EEPROM_Version: %d", ConfigData.EEPROM_Version);
 | 
			
		||||
  debugA("BatteryType: %d", ConfigData.batteryType);
 | 
			
		||||
  debugA("checksum: 0x%08X", ConfigData.checksum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_dumpPersistance()
 | 
			
		||||
{
 | 
			
		||||
  debugA("writeCycleCounter: %d", PersistenceData.writeCycleCounter);
 | 
			
		||||
  debugA("faction_1_timer: %d", PersistenceData.faction_1_timer);
 | 
			
		||||
  debugA("faction_2_timer: %d", PersistenceData.faction_2_timer);
 | 
			
		||||
  debugA("faction_2_timer: %d", PersistenceData.faction_3_timer);
 | 
			
		||||
  debugA("activeFaction: %d", PersistenceData.activeFaction);
 | 
			
		||||
  debugA("checksum: %d", PersistenceData.checksum);
 | 
			
		||||
  debugA("PSD Adress: 0x%04X", getPersistanceAddress());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_printWifiInfo()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_CheckEEPOM()
 | 
			
		||||
{
 | 
			
		||||
  uint32_t checksum = PersistenceData.checksum;
 | 
			
		||||
  PersistenceData.checksum = 0;
 | 
			
		||||
 | 
			
		||||
  if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum)
 | 
			
		||||
  {
 | 
			
		||||
    debugA("PersistenceData EEPROM Checksum OK\n");
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    debugA("PersistenceData EEPROM Checksum BAD\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  PersistenceData.checksum = checksum;
 | 
			
		||||
 | 
			
		||||
  checksum = ConfigData.checksum;
 | 
			
		||||
  ConfigData.checksum = 0;
 | 
			
		||||
 | 
			
		||||
  if (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) == checksum)
 | 
			
		||||
  {
 | 
			
		||||
    debugA("ConfigData EEPROM Checksum OK\n");
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    debugA("ConfigData EEPROM Checksum BAD\n");
 | 
			
		||||
  }
 | 
			
		||||
  ConfigData.checksum = checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoteDebug_ShowDTCs()
 | 
			
		||||
{
 | 
			
		||||
  char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
 | 
			
		||||
  char buff_active[9];
 | 
			
		||||
 | 
			
		||||
  for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
 | 
			
		||||
  {
 | 
			
		||||
    if (DTCStorage[i].Number > 0)
 | 
			
		||||
    {
 | 
			
		||||
      sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
 | 
			
		||||
              DTCStorage[i].timestamp / 86400000,    // Days
 | 
			
		||||
              DTCStorage[i].timestamp / 360000 % 24, // Hours
 | 
			
		||||
              DTCStorage[i].timestamp / 60000 % 60,  // Minutes
 | 
			
		||||
              DTCStorage[i].timestamp / 1000 % 60,   // Seconds
 | 
			
		||||
              DTCStorage[i].timestamp % 1000);       // milliseconds
 | 
			
		||||
 | 
			
		||||
      if (DTCStorage[i].active == DTC_ACTIVE)
 | 
			
		||||
        strcpy(buff_active, "active");
 | 
			
		||||
      else if (DTCStorage[i].active == DTC_PREVIOUS)
 | 
			
		||||
        strcpy(buff_active, "previous");
 | 
			
		||||
      else
 | 
			
		||||
        strcpy(buff_active, "none");
 | 
			
		||||
 | 
			
		||||
      debugA("%s \t %6d \t %s", buff_timestamp, DTCStorage[i].Number, buff_active);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void SetBatteryType(batteryType_t type)
 | 
			
		||||
{
 | 
			
		||||
  if (ConfigData.batteryType != type)
 | 
			
		||||
  {
 | 
			
		||||
    ConfigData.batteryType = type;
 | 
			
		||||
    globals.requestEEAction = EE_CFG_SAVE;
 | 
			
		||||
    debugV("Set Batterytype to %s", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
			
		||||
    Serial.printf("Set Batterytype to %s\n", type == BATTERY_LIPO_2S ? "2s Lipo" : "3s LiPo");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -635,7 +461,7 @@ void ProcessKeyCombos()
 | 
			
		||||
 | 
			
		||||
  if (digitalRead(DIO_FAC_3_TRG) != FAC_3_TRG_PRESSED && keyStatus_Fac3 == KEY_PRESSED)
 | 
			
		||||
  {
 | 
			
		||||
    debugV("KeyCombo 1: %d | 2: %d", keyCount_Fac1, keyCount_Fac2);
 | 
			
		||||
    Serial.printf("KeyCombo 1: %d | 2: %d\n", keyCount_Fac1, keyCount_Fac2);
 | 
			
		||||
 | 
			
		||||
    if (keyCount_Fac1 == 2 && keyCount_Fac2 == 2)
 | 
			
		||||
    {
 | 
			
		||||
@@ -656,4 +482,32 @@ void OverrideDisplay(const uint8_t *message, uint32_t time)
 | 
			
		||||
{
 | 
			
		||||
  DisplayOverrideFlag = millis() + time;
 | 
			
		||||
  DisplayOverrideValue = (uint8_t *)message;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t getESPChipID()
 | 
			
		||||
{
 | 
			
		||||
  uint32_t chipId;
 | 
			
		||||
  for (int i = 0; i < 17; i = i + 8)
 | 
			
		||||
  {
 | 
			
		||||
    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
 | 
			
		||||
  }
 | 
			
		||||
  return chipId;
 | 
			
		||||
}
 | 
			
		||||
#
 | 
			
		||||
void LoRaPublish_callback()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  StaticJsonDocument<200> doc;
 | 
			
		||||
 | 
			
		||||
  doc["lipo"] = globals.battery_level;
 | 
			
		||||
  doc["afact"] = PersistenceData.activeFaction;
 | 
			
		||||
  doc["fac1"] = PersistenceData.faction_1_timer;
 | 
			
		||||
  doc["fac2"] = PersistenceData.faction_2_timer;
 | 
			
		||||
  doc["fac3"] = PersistenceData.faction_3_timer;
 | 
			
		||||
 | 
			
		||||
  LoRa.beginPacket();
 | 
			
		||||
  serializeJson(doc, LoRa);
 | 
			
		||||
  LoRa.endPacket();
 | 
			
		||||
 | 
			
		||||
  Serial.printf("Sendet LoRa-Status Package\n");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								src/oled_display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/oled_display.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#include "oled_display.h"
 | 
			
		||||
 | 
			
		||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);
 | 
			
		||||
 | 
			
		||||
void OLED_Init()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    // reset OLED display via software
 | 
			
		||||
    pinMode(OLED_RST, OUTPUT);
 | 
			
		||||
    digitalWrite(OLED_RST, LOW);
 | 
			
		||||
    delay(20);
 | 
			
		||||
    digitalWrite(OLED_RST, HIGH);
 | 
			
		||||
 | 
			
		||||
    // initialize OLED
 | 
			
		||||
    Wire.begin(OLED_SDA, OLED_SCL);
 | 
			
		||||
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false))
 | 
			
		||||
    { // Address 0x3C for 128x32
 | 
			
		||||
        Serial.println(F("SSD1306 allocation failed"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    display.clearDisplay();
 | 
			
		||||
    display.setTextColor(WHITE);
 | 
			
		||||
    display.setTextSize(1);
 | 
			
		||||
    display.setCursor(0, 0);
 | 
			
		||||
    display.print("DISPLAY INIT");
 | 
			
		||||
    display.display();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OLED_Process()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    display.clearDisplay();
 | 
			
		||||
    display.setCursor(0, 0);
 | 
			
		||||
    display.printf("LiPo: %d%%\n", globals.battery_level);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_1 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_1_NAME, PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_2 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_2_NAME, PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
 | 
			
		||||
    display.print(PersistenceData.activeFaction == FACTION_3 ? "> " : "  ");
 | 
			
		||||
    display.printf("%-5s: %02d:%02d:%02d\n", FACTION_3_NAME, PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
 | 
			
		||||
    display.display();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/oled_display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/oled_display.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
#ifndef _OLED_DISPLAY_H_
 | 
			
		||||
#define _OLED_DISPLAY_H_
 | 
			
		||||
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <Wire.h>
 | 
			
		||||
#include <Adafruit_GFX.h>
 | 
			
		||||
#include <Adafruit_SSD1306.h>
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#define OLED_SDA 4
 | 
			
		||||
#define OLED_SCL 15
 | 
			
		||||
#define OLED_RST 16
 | 
			
		||||
#define SCREEN_WIDTH 128 // OLED display width, in pixels
 | 
			
		||||
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
 | 
			
		||||
 | 
			
		||||
void OLED_Init();
 | 
			
		||||
void OLED_Process();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
const char helpCmd[] = "sysinfo     - System Info\r\n"
 | 
			
		||||
                       "netinfo     - WiFi Info\r\n"
 | 
			
		||||
                       "showdtc     - Show all DTCs\r\n"
 | 
			
		||||
                       "cleardtc    - Clear all DTCs\r\n"
 | 
			
		||||
                       "formatPDS   - Format Persistence EEPROM Data\r\n"
 | 
			
		||||
                       "formatCFG   - Format Configuration EEPROM Data\r\n"
 | 
			
		||||
                       "checkEE     - Check EEPROM with checksum\r\n"
 | 
			
		||||
                       "dumpEE1k    - dump the first 1kb of EEPROM to Serial\r\n"
 | 
			
		||||
                       "dumpEE      - dump the whole EPPROM to Serial\r\n"
 | 
			
		||||
                       "resetPageEE - Reset the PersistenceData Page\r\n"
 | 
			
		||||
                       "dumpCFG     - print Config struct\r\n"
 | 
			
		||||
                       "dumpPDS     - print PersistanceStruct\r\n"
 | 
			
		||||
                       "saveEE      - save EE-Data\r\n"
 | 
			
		||||
                       "bat_3s      - set BatteryType to 3S LiPo\r\n"
 | 
			
		||||
                       "bat_2s      - set BatteryType to 2S LiPo\r\n"
 | 
			
		||||
                       ;
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#include <FS.h>
 | 
			
		||||
#include <LittleFS.h>
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#include <ESPAsyncWebServer.h>
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
[wifi_cred]
 | 
			
		||||
wifi_ap_password = ilovekggmorethangof
 | 
			
		||||
admin_password = i_am_dr.hiabuto
 | 
			
		||||
wifi_ssid = BND_Scanner_#42
 | 
			
		||||
wifi_password = 5xMYkerbLMdrsSdF3hpy5DM9
 | 
			
		||||
		Reference in New Issue
	
	Block a user