Restructured Repo before adding Hardware-Files
This commit is contained in:
		
							
								
								
									
										363
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/AsyncEventSource.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/AsyncJson.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1299
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										350
									
								
								Software/lib/ESP Async WebServer/src/AsyncWebSocket.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								Software/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_ */ | ||||
| @@ -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
									
								
								Software/lib/ESP Async WebServer/src/ESPAsyncWebServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										544
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/SPIFFSEditor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/StringArray.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebAuthentication.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebHandlerImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebHandlers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1009
									
								
								Software/lib/ESP Async WebServer/src/WebRequest.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										136
									
								
								Software/lib/ESP Async WebServer/src/WebResponseImpl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebResponses.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/WebServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								Software/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
									
								
								Software/lib/ESP Async WebServer/src/edit.htm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										627
									
								
								Software/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> | ||||
		Reference in New Issue
	
	Block a user