Applied memory optimizations from sascha432

Updated my additions
This commit is contained in:
lorol
2020-04-28 16:36:40 -04:00
parent da8e271b86
commit 902077553f
28 changed files with 1853 additions and 1783 deletions

View File

@ -1509,7 +1509,7 @@ For Arduino IDE create/update `platform.local.txt`:
Add/Update the following line: Add/Update the following line:
``` ```
compiler.cpp.extra_flags=-DDASYNCWEBSERVER_REGEX compiler.cpp.extra_flags=-DASYNCWEBSERVER_REGEX
``` ```
For platformio modify `platformio.ini`: For platformio modify `platformio.ini`:

View File

@ -1,13 +1,56 @@
ESP12 This application:
D2 = 4; // DHT DATA I/O
D3 = 0; // BUTTON - most modules have it populated on PCB
D4 = 2; // LED (RELAY) - most modules have it populated on PCB
static const uint8_t D0 = 16;
static const uint8_t D1 = 5;
static const uint8_t D2 = 4; // DHT DATA I/O Pinout ESP12 (8266)
static const uint8_t D3 = 0; // BUTTON - most modules have it populated on PCB D GPIO In Out Notes
static const uint8_t D4 = 2; // LED (RELAY) - most modules have it populated on PCB
static const uint8_t D5 = 14; D0 16 no interrupt no PWM or I2C support HIGH at boot used to wake up from deep sleep
static const uint8_t D6 = 12; D1 5 OK OK often used as SCL (I2C)
static const uint8_t D7 = 13; D2 4 OK OK often used as SDA (I2C)
static const uint8_t D8 = 15; D3 0 PU OK pulled up connected to FLASH button, boot fails if pulled LOW
static const uint8_t D9 = 3; D4 2 PU OK pulled up HIGH at boot connected to on-board LED, boot fails if pulled LOW
static const uint8_t D10 = 1; D5 14 OK OK SPI (SCLK)
D6 12 OK OK SPI (MISO)
D7 13 OK OK SPI (MOSI)
D8 15 pulled to GND OK SPI (CS) Boot fails if pulled HIGH
RX 3 OK RX pin HIGH at boot
TX 1 TX pin OK HIGH at boot debug output at boot, boot fails if pulled LOW
A0 ADC0 Analog Input
Pinout ESP32
IO In Out Notes
0 PU OK pulled-up input, outputs PWM signal at boot
1 TX OK debug output at boot
2 OK OK connected to on-board LED
3 OK RX pin HIGH at boot
4 OK OK
5 OK OK outputs PWM signal at boot
6-11 x x connected to the integrated SPI flash
12 OK OK boot fail if pulled high
13 OK OK
14 OK OK outputs PWM signal at boot
15 OK OK outputs PWM signal at boot
16 OK OK
17 OK OK
18 OK OK
19 OK OK
21 OK OK
22 OK OK
23 OK OK
25 OK OK
26 OK OK
27 OK OK
32 OK OK
33 OK OK
34 OK input only
35 OK input only
36 OK input only
39 OK input only

View File

@ -1,16 +1,17 @@
![](1.PNG) ![](2.PNG) ![](1.PNG) ![](2.PNG)
## ##
![](3.PNG) ![](4.PNG) ![](3.PNG) ![](4.PNG)
## SmartSwitch ## SmartSwitch
* Remote Temperature Control application with schedule (example car block heater or battery charger) * Remote Temperature Control application with schedule (example car block heater or battery charger)
* Based on ESP_AsyncFSBrowser example * Based on ESP_AsyncFSBrowser example
* Wide browser compatibility, no extra server-side needed. * Wide browser compatibility, no extra server-side needed
* HTTP server and WebSocket, single port * HTTP server and WebSocket, single port
* Standalone, no JS dependencies for the browser from Internet (I hope), ace editor included * Standalone, no JS dependencies for the browser from Internet (I hope), ace editor included
* Added ESPAsyncWiFiManager * Added ESPAsyncWiFiManager
* Real Time (NTP) w/ Time Zones * Fallback to an own WIFI_AP, no Internet to sync but by a browser the clock can be set once
* Memorized settings to EEPROM * Real Time (NTP) w/ Time Zones
* Multiple clients can be connected at same time, they see each other' requests * Memorized settings to EEPROM
* Default credentials <b>smart:switch</b> * Multiple clients can be connected at same time, they see each other' requests
* Use latest ESP8266 core lib (from Github) * Default credentials <b>smart:switch</b>
* Use latest ESP8266 or ESP32 core(from GitHub)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,6 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> <meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Smart Switch</title> <title>Smart Switch</title>
@ -54,6 +54,7 @@
appearance: none; appearance: none;
} }
.switch { .switch {
position: relative; position: relative;
display: inline-block; display: inline-block;
@ -116,6 +117,12 @@
.clk { .clk {
font-size: 54px; font-size: 54px;
color: #444; color: #444;
cursor: pointer
}
.clk2 {
font-size: 32px;
color: #444
} }
.clear:after, .clear:after,
@ -136,6 +143,7 @@
left: 0; left: 0;
margin: auto; margin: auto;
max-width: 500px; max-width: 500px;
text-align: center;
border: 0px solid #ccc border: 0px solid #ccc
} }
@ -185,7 +193,7 @@
.container { .container {
display: inline-block; display: inline-block;
position: relative; position: relative;
padding-left: 28px; padding-left: 19px;
padding-top: 4px; padding-top: 4px;
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
@ -194,6 +202,9 @@
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
width: 12.5%;
user-select: none user-select: none
} }
@ -239,6 +250,7 @@
border-radius: 50%; border-radius: 50%;
background: white background: white
} }
</style> </style>
</head> </head>
@ -249,7 +261,7 @@
<tr align="center"> <tr align="center">
<td colspan=3> <td colspan=3>
<form name="sched"> <form name="sched">
<label class="container" id="LZ0">Auto&nbsp;&nbsp;&nbsp; <label class="container" id="LZ0">Def
<input type="radio" name="radio" onclick="handleClick(this);" value="Z0"><span class="checkmark"></span></label> <input type="radio" name="radio" onclick="handleClick(this);" value="Z0"><span class="checkmark"></span></label>
<label class="container" id="LZ1">M-F <label class="container" id="LZ1">M-F
<input type="radio" name="radio" onclick="handleClick(this);" value="Z1"><span class="checkmark"></span></label> <input type="radio" name="radio" onclick="handleClick(this);" value="Z1"><span class="checkmark"></span></label>
@ -292,26 +304,22 @@
<input type="button" id="N" value="WSoff" onclick="statusWs();" /> <input type="button" id="N" value="WSoff" onclick="statusWs();" />
</div> </div>
</td> </td>
<td><span id="sid"></span></td> <td><span id="sid"></span><div id="free-ram"></div><div id="erase-wifi"></div><div id="hw-reset"></div><div id="get-time"></div></td>
<td> <td>
<div id="hw-reset"> <div id="ebut">
<input type="button" id="R" value="HWrst" onclick="loadDoc('hw-reset', 1);" /> <input type="button" id="E" value="WEdit" onclick="buttonEClick();" />
</div> </div>
</td> </td>
</tr> </tr>
<tr align="center"> <tr align="center">
<td> <td>
<div id="hbut"> <input type="button" id="X" value="xWiFi" onclick="loadDoc('erase-wifi', 1);"/>
<input type="button" id="H" value="Heap" onclick="loadDoc('free-ram', 0);" /> </td>
</div> <td>
<input type="button" id="H" value="Heap" onclick="loadDoc('free-ram', 0);" />
</td> </td>
<td> <td>
<div id="free-ram"></div> <input type="button" id="R" value="HWrst" onclick="loadDoc('hw-reset', 1);"/>
</td>
<td>
<div id="ebut">
<input type="button" id="E" value="WEdit" onclick="buttonEClick();" />
</div>
</td> </td>
</tr> </tr>
</table> </table>
@ -319,15 +327,14 @@
<div class="wrapper clear"> <div class="wrapper clear">
<div id="g1" class="gauge"></div> <div id="g1" class="gauge"></div>
<div id="g2" class="gauge"></div> <div id="g2" class="gauge"></div>
<center> <div id="C" class="clk" onclick="getBtime();"></div>
<label id="C" class="clk"></label> <div id="C2" class="clk2"></div>
</center>
</div> </div>
<script src="app.min.js"></script> <script src="app.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
const MYCORS = '192.168.0.12'; const MYCORS = '192.168.1.12';
var g1, g2; var g1, g2;
var Analog0 = new Array(); var Analog0 = new Array();
var auto = true; var auto = true;
@ -346,12 +353,18 @@
document.addEventListener("DOMContentLoaded", function(event) { document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed"); console.log("DOM fully loaded and parsed");
document.getElementById("R").style.backgroundColor = "red";
document.getElementById("X").style.backgroundColor = "red";
g1 = new JustGage({ g1 = new JustGage({
id: "g1", id: "g1",
value: -5.5, value: -5.5,
min: -40, min: -40,
max: 50, max: 50,
decimals: 1, decimals: 1,
labelFontColor: "#444",
valueFontColor: "#444",
title: "Temperature", title: "Temperature",
titlePosition: "below", titlePosition: "below",
label: "°C", label: "°C",
@ -379,6 +392,8 @@
min: 0, min: 0,
max: 100, max: 100,
decimals: 1, decimals: 1,
labelFontColor: "#444",
valueFontColor: "#444",
title: "Humidity", title: "Humidity",
titlePosition: "below", titlePosition: "below",
label: "%", label: "%",
@ -454,13 +469,14 @@
break; break;
case "Clock": case "Clock":
var dn = parseInt(msgArray[3]); var dn = parseInt(msgArray[3]);
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var dayName = days[dn] || '*'; // 0...6 var dayName = days[dn] || '*'; // 0...6
var shedtype = 0; var shedtype = 0;
if (dn == 0) shedtype = 3; if (dn == 0) shedtype = 3;
else if (dn == 6) shedtype = 2; else if (dn == 6) shedtype = 2;
else if ((dn > 0) && (dn < 6)) shedtype = 1; else if ((dn > 0) && (dn < 6)) shedtype = 1;
document.getElementById('C').innerHTML = msgArray[2] + ' ' + dayName; document.getElementById('C').innerHTML = msgArray[2];
document.getElementById('C2').innerHTML = dayName;
if (auto) document.getElementById('LZ' + shedtype).classList.add('son'); if (auto) document.getElementById('LZ' + shedtype).classList.add('son');
else document.getElementById('LZ' + shedtype).classList.remove('son'); else document.getElementById('LZ' + shedtype).classList.remove('son');
break; break;
@ -528,10 +544,10 @@
function buttonEClick() { function buttonEClick() {
var murl = '/edit'; var murl = '/edit';
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/edit'; //CORS if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/edit'; //CORS
window.open(murl, '_blank'); successNotification({
warningNotification({
message: 'Editor' message: 'Editor'
}); });
window.open(murl, '_blank');
}; };
function checkboxClick(e) { function checkboxClick(e) {
@ -558,9 +574,10 @@
} }
// XMLHttpRequest /non WebSocket/ command. same as command' div' id to get response to // XMLHttpRequest /non WebSocket/ command. same as command' div' id to get response to
function loadDoc(cmd, r) { function loadDoc(cmd, r, param) {
var murl = '/' + cmd; var par = param || '';
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/' + cmd; //CORS var murl = '/' + cmd + par;
if (document.location.host.length < 5) murl = 'http://' + MYCORS + '/' + cmd + par; //CORS
var xhttp = new XMLHttpRequest(); var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() { xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) { if (this.readyState == 4 && this.status == 200) {
@ -569,7 +586,7 @@
}; };
xhttp.open("GET", murl, true); xhttp.open("GET", murl, true);
xhttp.send(); xhttp.send();
warningNotification({ successNotification({
message: 'Cmd: ' + cmd message: 'Cmd: ' + cmd
}); });
if (r) { //restart? if (r) { //restart?
@ -577,14 +594,19 @@
document.getElementById("N").value = "WSon"; document.getElementById("N").value = "WSon";
} }
}; };
function getBtime() {
loadDoc('get-time', 0, '?btime=' + Math.round(new Date().getTime()/1000));
document.getElementById('C').innerHTML ='';
document.getElementById('C2').innerHTML ='';
window.location.reload();
}
function statusWs() { function statusWs() {
if (connection.readyState === WebSocket.OPEN) { if (connection.readyState === WebSocket.OPEN) {
connection.close(); connection.close();
document.getElementById("N").value = "WSon"; document.getElementById("N").value = "WSon";
warningNotification({ warningNotification({ message: 'WS Closed'});
message: 'WS Closed'
});
} else { } else {
window.location.reload(); window.location.reload();
} }

View File

@ -1,24 +1,27 @@
ehg.c (ehg.exe): Tool to generate C-code array from file' bytes ehg.c (ehg.exe): Tool to generate C-code array from file' bytes
Based on https://github.com/TheLivingOne/bin2array/ and compiled with TynyCC Based on https://github.com/TheLivingOne/bin2array/ and compiled with TynyCC
PROGMEM word can optionally be added.
rehg.c (rehg.exe): Tool to reverse C-code array generated by ehg.exe back to a file
Based on https://github.com/birkett/cbintools/tree/master/c2bin and compiled with TynyCC rehg.c (rehg.exe): Tool to reverse C-code array generated by ehg.exe back to a file
Based on https://github.com/birkett/cbintools/tree/master/c2bin and compiled with TynyCC
Tools used (on Win 10): First 4x lines of source are ignored, then parses the 0x00, formated bytes
until a } is found on separate new line.
https://www.7-zip.org (install 7z and use gzip - command line)
https://nodejs.org Tools used (on Win 10):
(install node and npm install html-minifier-terser -g, npm install -g github-files-fetcher)
https://www.npmjs.com/package/html-minifier-terser https://www.7-zip.org (install 7z and use gzip - command line)
https://www.npmjs.com/package/github-files-fetcher https://nodejs.org
(install node and npm install html-minifier-terser -g, npm install -g github-files-fetcher)
Optional: https://www.npmjs.com/package/html-minifier-terser
https://bellard.org/tcc/ (to compile ehg.c and rehg.c) https://www.npmjs.com/package/github-files-fetcher
Provided batch files (MS Win) Optional:
https://bellard.org/tcc/ (TynyCC, ehg.c and rehg.c to executable)
do.bat: Generates edit.htm.gz.h
Provided batch files (MS Win)
undo.bat: Reverts edit.htm from C array header to file (still minified!)
do.bat: Generates edit.htm.gz.h
update_ace.bat: Updates acefull.js.gz from latest Github sources
undo.bat: Reverts edit.htm from C array header to file (still minified!)
update_ace.bat: Updates acefull.js.gz from latest Github sources

View File

@ -1,7 +1,7 @@
copy ..\src\edit.htm edit_src.htm copy ..\src\edit.htm edit_src.htm
call html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true -o edit.htm edit_src.htm call html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --minify-css true --minify-js true -o edit.htm edit_src.htm
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 edit.htm.gz edit.htm "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 edit.htm.gz edit.htm
ehg edit.htm.gz ehg edit.htm.gz PROGMEM
copy edit.htm.gz.h ..\src\edit.htm.gz.h copy edit.htm.gz.h ..\src\edit.htm.gz.h
pause pause
del edit.htm edit.htm.gz edit.htm.gz.h edit_src.htm del edit.htm edit.htm.gz edit.htm.gz.h edit_src.htm

View File

@ -19,12 +19,16 @@ char* replace_char(char* str, char find, char replace){
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
if (argc != 2) { if ((argc > 3)||(argc < 2)) {
printf("USAGE: %s <input file>\n", argv[0]); printf("USAGE: %s <input file> [PROGMEM]\n", argv[0]);
return 1; return 1;
} }
const char * in = argv[1]; const char * in = argv[1];
const char * pr = argv[2];
char pr_o[8] = " ";
if (argv[2]) sprintf(pr_o, "%s", pr);
// Hello stack overflow :) // Hello stack overflow :)
char out_cpp[4096]; char out_cpp[4096];
@ -97,11 +101,15 @@ int main(int argc, char * argv[])
} }
fprintf( fprintf(
fout, fout,
"\n//File: %s, Size: %ld\n"
"#define %s_len %ld\n" "#define %s_len %ld\n"
"const uint8_t %s[] PROGMEM = {\n", "const uint8_t %s[] %s = {\n",
in,
(long)size,
usname, usname,
(long)size, (long)size,
usname); usname,
pr_o);
if (fwrite(text, out_ptr - text, 1, fout) != 1) { if (fwrite(text, out_ptr - text, 1, fout) != 1) {
printf("Error writing output file!"); printf("Error writing output file!");
free(text); free(text);

Binary file not shown.

View File

@ -65,8 +65,8 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
// Skip the first 2 lines. // Skip the first 4 lines.
for (i = 0; i < 2; i++) for (i = 0; i < 4; i++)
{ {
fgets(sBuffer, BUFFER_SIZE, inputFile); fgets(sBuffer, BUFFER_SIZE, inputFile);
} }

Binary file not shown.

View File

@ -10,5 +10,7 @@ cd tmp1
type ace.js mode-html.js theme-monokai.js > acefull.js type ace.js mode-html.js theme-monokai.js > acefull.js
"C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 acefull.js.gz acefull.js "C:\Program Files\7-Zip\7z.exe" a -tgzip -mx9 acefull.js.gz acefull.js
REM update: REM update:
copy acefull.js.gz ..\..\examples\SmartSwitch\data\acefull.js.gz
pause pause
copy acefull.js.gz ..\..\examples\SmartSwitch\data\acefull.js.gz
del *.js *.gz

View File

@ -10,7 +10,7 @@
"repository": "repository":
{ {
"type": "git", "type": "git",
"url": "https://github.com/me-no-dev/ESPAsyncWebServer.git" "url": "https://github.com/lorol/ESPAsyncWebServer.git"
}, },
"version": "1.2.3", "version": "1.2.3",
"license": "LGPL-3.0", "license": "LGPL-3.0",

View File

@ -1,9 +1,9 @@
name=ESP Async WebServer name=ESP Async WebServer
version=1.2.3 version=1.2.3
author=Me-No-Dev author=Me-No-Dev
maintainer=Me-No-Dev maintainer=lorol
sentence=Async Web Server for ESP8266 and ESP31B sentence=Async Web Server for ESP8266 and ESP31B
paragraph=Async Web Server for ESP8266 and ESP31B paragraph=Async Web Server for ESP8266 and ESP31B
category=Other category=Other
url=https://github.com/me-no-dev/ESPAsyncWebServer url=https://github.com/lorol/ESPAsyncWebServer
architectures=* architectures=*

View File

@ -21,24 +21,24 @@
#include "AsyncEventSource.h" #include "AsyncEventSource.h"
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){ static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
String ev = ""; String ev;
if(reconnect){ if(reconnect){
ev += "retry: "; ev += F("retry: ");
ev += String(reconnect); ev += reconnect;
ev += "\r\n"; ev += F("\r\n");
} }
if(id){ if(id){
ev += "id: "; ev += F("id: ");
ev += String(id); ev += String(id);
ev += "\r\n"; ev += F("\r\n");
} }
if(event != NULL){ if(event != NULL){
ev += "event: "; ev += F("event: ");
ev += String(event); ev += String(event);
ev += "\r\n"; ev += F("\r\n");
} }
if(message != NULL){ if(message != NULL){
@ -54,9 +54,9 @@ static String generateEventMessage(const char *message, const char *event, uint3
if(ldata != NULL){ if(ldata != NULL){
memcpy(ldata, lineStart, llen); memcpy(ldata, lineStart, llen);
ldata[llen] = 0; ldata[llen] = 0;
ev += "data: "; ev += F("data: ");
ev += ldata; ev += ldata;
ev += "\r\n\r\n"; ev += F("\r\n\r\n");
free(ldata); free(ldata);
} }
lineStart = (char *)message + messageLen; lineStart = (char *)message + messageLen;
@ -89,14 +89,14 @@ static String generateEventMessage(const char *message, const char *event, uint3
if(ldata != NULL){ if(ldata != NULL){
memcpy(ldata, lineStart, llen); memcpy(ldata, lineStart, llen);
ldata[llen] = 0; ldata[llen] = 0;
ev += "data: "; ev += F("data: ");
ev += ldata; ev += ldata;
ev += "\r\n"; ev += F("\r\n");
free(ldata); free(ldata);
} }
lineStart = nextLine; lineStart = nextLine;
if(lineStart == ((char *)message + messageLen)) if(lineStart == ((char *)message + messageLen))
ev += "\r\n"; ev += F("\r\n");
} }
} while(lineStart < ((char *)message + messageLen)); } while(lineStart < ((char *)message + messageLen));
} }
@ -146,7 +146,7 @@ size_t AsyncEventSourceMessage::send(AsyncClient *client) {
if(client->canSend()) if(client->canSend())
client->send(); client->send();
_sent += sent; _sent += sent;
return sent; return sent;
} }
// Client // Client
@ -157,9 +157,9 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, A
_client = request->client(); _client = request->client();
_server = server; _server = server;
_lastId = 0; _lastId = 0;
if(request->hasHeader("Last-Event-ID")) if(request->hasHeader(F("Last-Event-ID")))
_lastId = atoi(request->getHeader("Last-Event-ID")->value().c_str()); _lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str());
_client->setRxTimeout(0); _client->setRxTimeout(0);
_client->onError(NULL, NULL); _client->onError(NULL, NULL);
_client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this); _client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
@ -185,7 +185,7 @@ void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage)
return; return;
} }
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){ if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
ets_printf("ERROR: Too many messages queued\n"); ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
delete dataMessage; delete dataMessage;
} else { } else {
_messageQueue.add(dataMessage); _messageQueue.add(dataMessage);
@ -276,7 +276,7 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
client->write((const char *)temp, 2053); client->write((const char *)temp, 2053);
free(temp); free(temp);
}*/ }*/
_clients.add(client); _clients.add(client);
if(_connectcb) if(_connectcb)
_connectcb(client); _connectcb(client);
@ -297,10 +297,10 @@ void AsyncEventSource::close(){
size_t AsyncEventSource::avgPacketsWaiting() const { size_t AsyncEventSource::avgPacketsWaiting() const {
if(_clients.isEmpty()) if(_clients.isEmpty())
return 0; return 0;
size_t aql=0; size_t aql=0;
uint32_t nConnectedClients=0; uint32_t nConnectedClients=0;
for(const auto &c: _clients){ for(const auto &c: _clients){
if(c->connected()) { if(c->connected()) {
aql+=c->packetsWaiting(); aql+=c->packetsWaiting();
@ -332,13 +332,14 @@ bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
if(request->method() != HTTP_GET || !request->url().equals(_url)) { if(request->method() != HTTP_GET || !request->url().equals(_url)) {
return false; return false;
} }
request->addInterestingHeader("Last-Event-ID"); request->addInterestingHeader(F("Last-Event-ID"));
return true; return true;
} }
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
return request->requestAuthentication(); return request->requestAuthentication();
}
request->send(new AsyncEventSourceResponse(this)); request->send(new AsyncEventSourceResponse(this));
} }
@ -347,10 +348,10 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
_server = server; _server = server;
_code = 200; _code = 200;
_contentType = "text/event-stream"; _contentType = F("text/event-stream");
_sendContentLength = false; _sendContentLength = false;
addHeader("Cache-Control", "no-cache"); addHeader(F("Cache-Control"), F("no-cache"));
addHeader("Connection","keep-alive"); addHeader(F("Connection"), F("keep-alive"));
} }
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){ void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){

View File

@ -135,7 +135,7 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer()
} }
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size) AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t size)
:_data(nullptr) :_data(nullptr)
,_len(size) ,_len(size)
,_lock(false) ,_lock(false)
@ -143,14 +143,14 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(uint8_t * data, size_t
{ {
if (!data) { if (!data) {
return; return;
} }
_data = new uint8_t[_len + 1]; _data = new uint8_t[_len + 1];
if (_data) { if (_data) {
memcpy(_data, data, _len); memcpy(_data, data, _len);
_data[_len] = 0; _data[_len] = 0;
} }
} }
@ -161,12 +161,12 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(size_t size)
,_lock(false) ,_lock(false)
,_count(0) ,_count(0)
{ {
_data = new uint8_t[_len + 1]; _data = new uint8_t[_len + 1];
if (_data) { if (_data) {
_data[_len] = 0; _data[_len] = 0;
} }
} }
AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy) AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMessageBuffer & copy)
@ -180,13 +180,13 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(const AsyncWebSocketMes
_count = 0; _count = 0;
if (_len) { if (_len) {
_data = new uint8_t[_len + 1]; _data = new uint8_t[_len + 1];
_data[_len] = 0; _data[_len] = 0;
} }
if (_data) { if (_data) {
memcpy(_data, copy._data, _len); memcpy(_data, copy._data, _len);
_data[_len] = 0; _data[_len] = 0;
} }
} }
@ -202,35 +202,35 @@ AsyncWebSocketMessageBuffer::AsyncWebSocketMessageBuffer(AsyncWebSocketMessageBu
_count = 0; _count = 0;
if (copy._data) { if (copy._data) {
_data = copy._data; _data = copy._data;
copy._data = nullptr; copy._data = nullptr;
} }
} }
AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer() AsyncWebSocketMessageBuffer::~AsyncWebSocketMessageBuffer()
{ {
if (_data) { if (_data) {
delete[] _data; delete[] _data;
} }
} }
bool AsyncWebSocketMessageBuffer::reserve(size_t size) bool AsyncWebSocketMessageBuffer::reserve(size_t size)
{ {
_len = size; _len = size;
if (_data) { if (_data) {
delete[] _data; delete[] _data;
_data = nullptr; _data = nullptr;
} }
_data = new uint8_t[_len + 1]; _data = new uint8_t[_len + 1];
if (_data) { if (_data) {
_data[_len] = 0; _data[_len] = 0;
return true; return true;
} else { } else {
return false; return false;
} }
} }
@ -311,7 +311,7 @@ AsyncWebSocketBasicMessage::AsyncWebSocketBasicMessage(uint8_t opcode, bool mask
{ {
_opcode = opcode & 0x07; _opcode = opcode & 0x07;
_mask = mask; _mask = mask;
} }
@ -366,17 +366,17 @@ AsyncWebSocketBasicMessage::~AsyncWebSocketBasicMessage() {
return sent; return sent;
} }
// bool AsyncWebSocketBasicMessage::reserve(size_t size) { // bool AsyncWebSocketBasicMessage::reserve(size_t size) {
// if (size) { // if (size) {
// _data = (uint8_t*)malloc(size +1); // _data = (uint8_t*)malloc(size +1);
// if (_data) { // if (_data) {
// memset(_data, 0, size); // memset(_data, 0, size);
// _len = size; // _len = size;
// _status = WS_MSG_SENDING; // _status = WS_MSG_SENDING;
// return true; // return true;
// } // }
// } // }
// return false; // return false;
// } // }
@ -397,22 +397,22 @@ AsyncWebSocketMultiMessage::AsyncWebSocketMultiMessage(AsyncWebSocketMessageBuff
_mask = mask; _mask = mask;
if (buffer) { if (buffer) {
_WSbuffer = buffer; _WSbuffer = buffer;
(*_WSbuffer)++; (*_WSbuffer)++;
_data = buffer->get(); _data = buffer->get();
_len = buffer->length(); _len = buffer->length();
_status = WS_MSG_SENDING; _status = WS_MSG_SENDING;
//ets_printf("M: %u\n", _len); //ets_printf("M: %u\n", _len);
} else { } else {
_status = WS_MSG_ERROR; _status = WS_MSG_ERROR;
} }
} }
AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() { AsyncWebSocketMultiMessage::~AsyncWebSocketMultiMessage() {
if (_WSbuffer) { if (_WSbuffer) {
(*_WSbuffer)--; // decreases the counter. (*_WSbuffer)--; // decreases the counter.
} }
} }
@ -523,7 +523,7 @@ void AsyncWebSocketClient::_onAck(size_t len, uint32_t time){
if(len && !_messageQueue.isEmpty()){ if(len && !_messageQueue.isEmpty()){
_messageQueue.front()->ack(len, time); _messageQueue.front()->ack(len, time);
} }
_server->_cleanBuffers(); _server->_cleanBuffers();
_runQueue(); _runQueue();
} }
@ -560,7 +560,7 @@ void AsyncWebSocketClient::_queueMessage(AsyncWebSocketMessage *dataMessage){
return; return;
} }
if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){ if(_messageQueue.length() >= WS_MAX_QUEUED_MESSAGES){
ets_printf("ERROR: Too many messages queued\n"); ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
delete dataMessage; delete dataMessage;
} else { } else {
_messageQueue.add(dataMessage); _messageQueue.add(dataMessage);
@ -788,20 +788,7 @@ void AsyncWebSocketClient::text(const String &message){
text(message.c_str(), message.length()); text(message.c_str(), message.length());
} }
void AsyncWebSocketClient::text(const __FlashStringHelper *data){ void AsyncWebSocketClient::text(const __FlashStringHelper *data){
PGM_P p = reinterpret_cast<PGM_P>(data); text(String(data));
size_t n = 0;
while (1) {
if (pgm_read_byte(p+n) == 0) break;
n += 1;
}
char * message = (char*) malloc(n+1);
if(message){
for(size_t b=0; b<n; b++)
message[b] = pgm_read_byte(p++);
message[n] = 0;
text(message, n);
free(message);
}
} }
void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer) void AsyncWebSocketClient::text(AsyncWebSocketMessageBuffer * buffer)
{ {
@ -827,12 +814,11 @@ void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len){
PGM_P p = reinterpret_cast<PGM_P>(data); PGM_P p = reinterpret_cast<PGM_P>(data);
char * message = (char*) malloc(len); char * message = (char*) malloc(len);
if(message){ if(message){
for(size_t b=0; b<len; b++) memcpy_P(message, p, len);
message[b] = pgm_read_byte(p++);
binary(message, len); binary(message, len);
free(message); free(message);
} }
} }
void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer) void AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer * buffer)
{ {
@ -882,7 +868,7 @@ void AsyncWebSocket::_addClient(AsyncWebSocketClient * client){
} }
void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){ void AsyncWebSocket::_handleDisconnect(AsyncWebSocketClient * client){
_clients.remove_first([=](AsyncWebSocketClient * c){ _clients.remove_first([=](AsyncWebSocketClient * c){
return c->id() == client->id(); return c->id() == client->id();
}); });
@ -959,20 +945,20 @@ void AsyncWebSocket::text(uint32_t id, const char * message, size_t len){
void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){ void AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer * buffer){
if (!buffer) return; if (!buffer) return;
buffer->lock(); buffer->lock();
for(const auto& c: _clients){ for(const auto& c: _clients){
if(c->status() == WS_CONNECTED){ if(c->status() == WS_CONNECTED){
c->text(buffer); c->text(buffer);
} }
} }
buffer->unlock(); buffer->unlock();
_cleanBuffers(); _cleanBuffers();
} }
void AsyncWebSocket::textAll(const char * message, size_t len){ void AsyncWebSocket::textAll(const char * message, size_t len){
AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len); AsyncWebSocketMessageBuffer * WSBuffer = makeBuffer((uint8_t *)message, len);
textAll(WSBuffer); textAll(WSBuffer);
} }
void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){ void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){
@ -982,20 +968,20 @@ void AsyncWebSocket::binary(uint32_t id, const char * message, size_t len){
} }
void AsyncWebSocket::binaryAll(const char * message, size_t len){ void AsyncWebSocket::binaryAll(const char * message, size_t len){
AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len); AsyncWebSocketMessageBuffer * buffer = makeBuffer((uint8_t *)message, len);
binaryAll(buffer); binaryAll(buffer);
} }
void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer) void AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer * buffer)
{ {
if (!buffer) return; if (!buffer) return;
buffer->lock(); buffer->lock();
for(const auto& c: _clients){ for(const auto& c: _clients){
if(c->status() == WS_CONNECTED) if(c->status() == WS_CONNECTED)
c->binary(buffer); c->binary(buffer);
} }
buffer->unlock(); buffer->unlock();
_cleanBuffers(); _cleanBuffers();
} }
void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){ void AsyncWebSocket::message(uint32_t id, AsyncWebSocketMessage *message){
@ -1009,7 +995,7 @@ void AsyncWebSocket::messageAll(AsyncWebSocketMultiMessage *message){
if(c->status() == WS_CONNECTED) if(c->status() == WS_CONNECTED)
c->message(message); c->message(message);
} }
_cleanBuffers(); _cleanBuffers();
} }
size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){ size_t AsyncWebSocket::printf(uint32_t id, const char *format, ...){
@ -1034,8 +1020,8 @@ size_t AsyncWebSocket::printfAll(const char *format, ...) {
size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg); size_t len = vsnprintf(temp, MAX_PRINTF_LEN, format, arg);
va_end(arg); va_end(arg);
delete[] temp; delete[] temp;
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len); AsyncWebSocketMessageBuffer * buffer = makeBuffer(len);
if (!buffer) { if (!buffer) {
return 0; return 0;
} }
@ -1072,8 +1058,8 @@ size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg); size_t len = vsnprintf_P(temp, MAX_PRINTF_LEN, formatP, arg);
va_end(arg); va_end(arg);
delete[] temp; delete[] temp;
AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1); AsyncWebSocketMessageBuffer * buffer = makeBuffer(len + 1);
if (!buffer) { if (!buffer) {
return 0; return 0;
} }
@ -1157,19 +1143,28 @@ void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){
} }
} }
const char * WS_STR_CONNECTION = "Connection"; const char __WS_STR_CONNECTION[] PROGMEM = { "Connection" };
const char * WS_STR_UPGRADE = "Upgrade"; const char __WS_STR_UPGRADE[] PROGMEM = { "Upgrade" };
const char * WS_STR_ORIGIN = "Origin"; const char __WS_STR_ORIGIN[] PROGMEM = { "Origin" };
const char * WS_STR_VERSION = "Sec-WebSocket-Version"; const char __WS_STR_VERSION[] PROGMEM = { "Sec-WebSocket-Version" };
const char * WS_STR_KEY = "Sec-WebSocket-Key"; const char __WS_STR_KEY[] PROGMEM = { "Sec-WebSocket-Key" };
const char * WS_STR_PROTOCOL = "Sec-WebSocket-Protocol"; const char __WS_STR_PROTOCOL[] PROGMEM = { "Sec-WebSocket-Protocol" };
const char * WS_STR_ACCEPT = "Sec-WebSocket-Accept"; const char __WS_STR_ACCEPT[] PROGMEM = { "Sec-WebSocket-Accept" };
const char * WS_STR_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; const char __WS_STR_UUID[] PROGMEM = { "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" };
#define WS_STR_CONNECTION FPSTR(__WS_STR_CONNECTION)
#define WS_STR_UPGRADE FPSTR(__WS_STR_UPGRADE)
#define WS_STR_ORIGIN FPSTR(__WS_STR_ORIGIN)
#define WS_STR_VERSION FPSTR(__WS_STR_VERSION)
#define WS_STR_KEY FPSTR(__WS_STR_KEY)
#define WS_STR_PROTOCOL FPSTR(__WS_STR_PROTOCOL)
#define WS_STR_ACCEPT FPSTR(__WS_STR_ACCEPT)
#define WS_STR_UUID FPSTR(__WS_STR_UUID)
bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){
if(!_enabled) if(!_enabled)
return false; return false;
if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS)) if(request->method() != HTTP_GET || !request->url().equals(_url) || !request->isExpectedRequestedConnType(RCT_WS))
return false; return false;
@ -1187,13 +1182,13 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){
request->send(400); request->send(400);
return; return;
} }
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())){ if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication(); return request->requestAuthentication();
} }
AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); AsyncWebHeader* version = request->getHeader(WS_STR_VERSION);
if(version->value().toInt() != 13){ if(version->value().toInt() != 13){
AsyncWebServerResponse *response = request->beginResponse(400); AsyncWebServerResponse *response = request->beginResponse(400);
response->addHeader(WS_STR_VERSION,"13"); response->addHeader(WS_STR_VERSION, F("13"));
request->send(response); request->send(response);
return; return;
} }
@ -1209,24 +1204,24 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){
AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size) AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(size_t size)
{ {
AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size); AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(size);
if (buffer) { if (buffer) {
AsyncWebLockGuard l(_lock); AsyncWebLockGuard l(_lock);
_buffers.add(buffer); _buffers.add(buffer);
} }
return buffer; return buffer;
} }
AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size) AsyncWebSocketMessageBuffer * AsyncWebSocket::makeBuffer(uint8_t * data, size_t size)
{ {
AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size); AsyncWebSocketMessageBuffer * buffer = new AsyncWebSocketMessageBuffer(data, size);
if (buffer) { if (buffer) {
AsyncWebLockGuard l(_lock); AsyncWebLockGuard l(_lock);
_buffers.add(buffer); _buffers.add(buffer);
} }
return buffer; return buffer;
} }
void AsyncWebSocket::_cleanBuffers() void AsyncWebSocket::_cleanBuffers()
@ -1279,7 +1274,7 @@ AsyncWebSocketResponse::AsyncWebSocketResponse(const String& key, AsyncWebSocket
int len = base64_encode_block((const char *) hash, 20, buffer, &_state); int len = base64_encode_block((const char *) hash, 20, buffer, &_state);
len = base64_encode_blockend((buffer + len), &_state); len = base64_encode_blockend((buffer + len), &_state);
addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE); addHeader(WS_STR_CONNECTION, WS_STR_UPGRADE);
addHeader(WS_STR_UPGRADE, "websocket"); addHeader(WS_STR_UPGRADE, F("websocket"));
addHeader(WS_STR_ACCEPT,buffer); addHeader(WS_STR_ACCEPT,buffer);
free(buffer); free(buffer);
free(hash); free(hash);

View File

@ -70,6 +70,19 @@ typedef enum {
} WebRequestMethod; } WebRequestMethod;
#endif #endif
#ifndef HAVE_FS_FILE_OPEN_MODE
namespace fs {
class FileOpenMode {
public:
static const char *read;
static const char *write;
static const char *append;
};
};
#else
#include "FileOpenMode.h"
#endif
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again //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 #define RESPONSE_TRY_AGAIN 0xFFFFFFFF
@ -119,7 +132,7 @@ class AsyncWebHeader {
~AsyncWebHeader(){} ~AsyncWebHeader(){}
const String& name() const { return _name; } const String& name() const { return _name; }
const String& value() const { return _value; } const String& value() const { return _value; }
String toString() const { return String(_name+": "+_value+"\r\n"); } String toString() const { return String(_name + F(": ") + _value + F("\r\n")); }
}; };
/* /*
@ -215,8 +228,8 @@ class AsyncWebServerRequest {
const String& contentType() const { return _contentType; } const String& contentType() const { return _contentType; }
size_t contentLength() const { return _contentLength; } size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; } bool multipart() const { return _isMultipart; }
const char * methodToString() const; const __FlashStringHelper *methodToString() const;
const char * requestedConnTypeToString() const; const __FlashStringHelper *requestedConnTypeToString() const;
RequestedConnectionType requestedConnType() const { return _reqconntype; } RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect (ArDisconnectHandler fn); void onDisconnect (ArDisconnectHandler fn);
@ -266,12 +279,12 @@ class AsyncWebServerRequest {
bool hasParam(const __FlashStringHelper * data, 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 String& name, bool post=false, bool file=false) const;
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const; AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
AsyncWebParameter* getParam(size_t num) const; AsyncWebParameter* getParam(size_t num) const;
size_t args() const { return params(); } // get arguments count 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 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(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& arg(size_t i) const; // get request argument value by number
const String& argName(size_t i) const; // get request argument name 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 char* name) const; // check if argument exists
@ -280,7 +293,7 @@ class AsyncWebServerRequest {
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const; const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
const String& header(const char* name) const;// get request header value by name 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(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& header(size_t i) const; // get request header value by number
const String& headerName(size_t i) const; // get request header name by number const String& headerName(size_t i) const; // get request header name by number
String urlDecode(const String& text) const; String urlDecode(const String& text) const;
@ -369,6 +382,8 @@ class AsyncWebServerResponse {
size_t _writtenLength; size_t _writtenLength;
WebResponseState _state; WebResponseState _state;
const char* _responseCodeToString(int code); const char* _responseCodeToString(int code);
public:
static const __FlashStringHelper *responseCodeToString(int code);
public: public:
AsyncWebServerResponse(); AsyncWebServerResponse();
@ -419,7 +434,7 @@ class AsyncWebServer {
AsyncWebHandler& addHandler(AsyncWebHandler* handler); AsyncWebHandler& addHandler(AsyncWebHandler* handler);
bool removeHandler(AsyncWebHandler* handler); bool removeHandler(AsyncWebHandler* handler);
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest); 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);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
@ -431,8 +446,8 @@ class AsyncWebServer {
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads 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 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 reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
void _handleDisconnect(AsyncWebServerRequest *request); void _handleDisconnect(AsyncWebServerRequest *request);
void _attachHandler(AsyncWebServerRequest *request); void _attachHandler(AsyncWebServerRequest *request);
void _rewriteRequest(AsyncWebServerRequest *request); void _rewriteRequest(AsyncWebServerRequest *request);
@ -441,7 +456,7 @@ class AsyncWebServer {
class DefaultHeaders { class DefaultHeaders {
using headers_t = LinkedList<AsyncWebHeader *>; using headers_t = LinkedList<AsyncWebHeader *>;
headers_t _headers; headers_t _headers;
DefaultHeaders() DefaultHeaders()
:_headers(headers_t([](AsyncWebHeader *h){ delete h; })) :_headers(headers_t([](AsyncWebHeader *h){ delete h; }))
{} {}
@ -450,8 +465,8 @@ public:
void addHeader(const String& name, const String& value){ void addHeader(const String& name, const String& value){
_headers.add(new AsyncWebHeader(name, value)); _headers.add(new AsyncWebHeader(name, value));
} }
ConstIterator begin() const { return _headers.begin(); } ConstIterator begin() const { return _headers.begin(); }
ConstIterator end() const { return _headers.end(); } ConstIterator end() const { return _headers.end(); }

View File

@ -47,7 +47,7 @@ bool checkBasicAuthentication(const char * hash, const char * username, const ch
delete[] toencode; delete[] toencode;
return false; return false;
} }
sprintf(toencode, "%s:%s", username, password); sprintf_P(toencode, PSTR("%s:%s"), username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){ if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
delete[] toencode; delete[] toencode;
delete[] encoded; delete[] encoded;
@ -80,7 +80,7 @@ static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or mo
MD5Final(_buf, &_ctx); MD5Final(_buf, &_ctx);
#endif #endif
for(i = 0; i < 16; i++) { for(i = 0; i < 16; i++) {
sprintf(output + (i * 2), "%02x", _buf[i]); sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
} }
free(_buf); free(_buf);
return true; return true;
@ -94,7 +94,7 @@ static String genRandomMD5(){
#endif #endif
char * out = (char*)malloc(33); char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out)) if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
return ""; return emptyString;
String res = String(out); String res = String(out);
free(out); free(out);
return res; return res;
@ -103,7 +103,7 @@ static String genRandomMD5(){
static String stringMD5(const String& in){ static String stringMD5(const String& in){
char * out = (char*)malloc(33); char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return ""; return emptyString;
String res = String(out); String res = String(out);
free(out); free(out);
return res; return res;
@ -111,44 +111,44 @@ static String stringMD5(const String& in){
String generateDigestHash(const char * username, const char * password, const char * realm){ String generateDigestHash(const char * username, const char * password, const char * realm){
if(username == NULL || password == NULL || realm == NULL){ if(username == NULL || password == NULL || realm == NULL){
return ""; return emptyString;
} }
char * out = (char*)malloc(33); char * out = (char*)malloc(33);
String res = String(username); String res = String(username);
res.concat(":"); res += ':';
res.concat(realm); res.concat(realm);
res.concat(":"); res += ':';
String in = res; String in = res;
in.concat(password); in.concat(password);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out)) if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return ""; return emptyString;
res.concat(out); res.concat(out);
free(out); free(out);
return res; return res;
} }
String requestDigestAuthentication(const char * realm){ String requestDigestAuthentication(const char * realm){
String header = "realm=\""; String header = F("realm=\"");
if(realm == NULL) if(realm == NULL)
header.concat("asyncesp"); header.concat(F("asyncesp"));
else else
header.concat(realm); header.concat(realm);
header.concat( "\", qop=\"auth\", nonce=\""); header.concat(F("\", qop=\"auth\", nonce=\""));
header.concat(genRandomMD5()); header.concat(genRandomMD5());
header.concat("\", opaque=\""); header.concat(F("\", opaque=\""));
header.concat(genRandomMD5()); header.concat(genRandomMD5());
header.concat("\""); header += '"';
return header; 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){ bool checkDigestAuthentication(const char * header, const __FlashStringHelper *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){ if(username == NULL || password == NULL || header == NULL || method == NULL){
//os_printf("AUTH FAIL: missing requred fields\n"); //os_printf("AUTH FAIL: missing requred fields\n");
return false; return false;
} }
String myHeader = String(header); String myHeader = String(header);
int nextBreak = myHeader.indexOf(","); int nextBreak = myHeader.indexOf(',');
if(nextBreak < 0){ if(nextBreak < 0){
//os_printf("AUTH FAIL: no variables\n"); //os_printf("AUTH FAIL: no variables\n");
return false; return false;
@ -163,67 +163,67 @@ bool checkDigestAuthentication(const char * header, const char * method, const c
String myNc = String(); String myNc = String();
String myCnonce = String(); String myCnonce = String();
myHeader += ", "; myHeader += F(", ");
do { do {
String avLine = myHeader.substring(0, nextBreak); String avLine = myHeader.substring(0, nextBreak);
avLine.trim(); avLine.trim();
myHeader = myHeader.substring(nextBreak+1); myHeader = myHeader.substring(nextBreak+1);
nextBreak = myHeader.indexOf(","); nextBreak = myHeader.indexOf(',');
int eqSign = avLine.indexOf("="); int eqSign = avLine.indexOf('=');
if(eqSign < 0){ if(eqSign < 0){
//os_printf("AUTH FAIL: no = sign\n"); //os_printf("AUTH FAIL: no = sign\n");
return false; return false;
} }
String varName = avLine.substring(0, eqSign); String varName = avLine.substring(0, eqSign);
avLine = avLine.substring(eqSign + 1); avLine = avLine.substring(eqSign + 1);
if(avLine.startsWith("\"")){ if(avLine.startsWith(String('"'))){
avLine = avLine.substring(1, avLine.length() - 1); avLine = avLine.substring(1, avLine.length() - 1);
} }
if(varName.equals("username")){ if(varName.equals(F("username"))){
if(!avLine.equals(username)){ if(!avLine.equals(username)){
//os_printf("AUTH FAIL: username\n"); //os_printf("AUTH FAIL: username\n");
return false; return false;
} }
myUsername = avLine; myUsername = avLine;
} else if(varName.equals("realm")){ } else if(varName.equals(F("realm"))){
if(realm != NULL && !avLine.equals(realm)){ if(realm != NULL && !avLine.equals(realm)){
//os_printf("AUTH FAIL: realm\n"); //os_printf("AUTH FAIL: realm\n");
return false; return false;
} }
myRealm = avLine; myRealm = avLine;
} else if(varName.equals("nonce")){ } else if(varName.equals(F("nonce"))){
if(nonce != NULL && !avLine.equals(nonce)){ if(nonce != NULL && !avLine.equals(nonce)){
//os_printf("AUTH FAIL: nonce\n"); //os_printf("AUTH FAIL: nonce\n");
return false; return false;
} }
myNonce = avLine; myNonce = avLine;
} else if(varName.equals("opaque")){ } else if(varName.equals(F("opaque"))){
if(opaque != NULL && !avLine.equals(opaque)){ if(opaque != NULL && !avLine.equals(opaque)){
//os_printf("AUTH FAIL: opaque\n"); //os_printf("AUTH FAIL: opaque\n");
return false; return false;
} }
} else if(varName.equals("uri")){ } else if(varName.equals(F("uri"))){
if(uri != NULL && !avLine.equals(uri)){ if(uri != NULL && !avLine.equals(uri)){
//os_printf("AUTH FAIL: uri\n"); //os_printf("AUTH FAIL: uri\n");
return false; return false;
} }
myUri = avLine; myUri = avLine;
} else if(varName.equals("response")){ } else if(varName.equals(F("response"))){
myResponse = avLine; myResponse = avLine;
} else if(varName.equals("qop")){ } else if(varName.equals(F("qop"))){
myQop = avLine; myQop = avLine;
} else if(varName.equals("nc")){ } else if(varName.equals(F("nc"))){
myNc = avLine; myNc = avLine;
} else if(varName.equals("cnonce")){ } else if(varName.equals(F("cnonce"))){
myCnonce = avLine; myCnonce = avLine;
} }
} while(nextBreak > 0); } while(nextBreak > 0);
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ":" + myRealm + ":" + String(password)); String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
String ha2 = String(method) + ":" + myUri; String ha2 = String(method) + ':' + myUri;
String response = ha1 + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2); String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
if(myResponse.equals(stringMD5(response))){ if(myResponse.equals(stringMD5(response))){
//os_printf("AUTH SUCCESS\n"); //os_printf("AUTH SUCCESS\n");

View File

@ -26,7 +26,7 @@
bool checkBasicAuthentication(const char * header, const char * username, const char * password); bool checkBasicAuthentication(const char * header, const char * username, const char * password);
String requestDigestAuthentication(const char * realm); 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); bool checkDigestAuthentication(const char * header, const __FlashStringHelper *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 //for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char * username, const char * password, const char * realm); String generateDigestHash(const char * username, const char * password, const char * realm);

View File

@ -22,11 +22,11 @@
#include "WebHandlerImpl.h" #include "WebHandlerImpl.h"
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control) 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) : _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr)
{ {
// Ensure leading '/' // Ensure leading '/'
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri; if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri;
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path; if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path;
// If path ends with '/' we assume a hint that this is a directory to improve performance. // 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. // However - if it does not end with '/' we, can't assume a file, path can still be a directory.
@ -63,8 +63,12 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
} }
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
char format[strlen_P(formatP) + 1];
strcpy_P(format, formatP);
char result[30]; char result[30];
strftime (result,30,"%a, %d %b %Y %H:%M:%S %Z", last_modified); strftime(result, sizeof(result), format, last_modified);
return setLastModified((const char *)result); return setLastModified((const char *)result);
} }
@ -81,8 +85,8 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
} }
#endif #endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
if(request->method() != HTTP_GET if(request->method() != HTTP_GET
|| !request->url().startsWith(_uri) || !request->url().startsWith(_uri)
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP) || !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
){ ){
return false; return false;
@ -90,10 +94,10 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
if (_getFile(request)) { if (_getFile(request)) {
// We interested in "If-Modified-Since" header to check if file was modified // We interested in "If-Modified-Since" header to check if file was modified
if (_last_modified.length()) if (_last_modified.length())
request->addInterestingHeader("If-Modified-Since"); request->addInterestingHeader(F("If-Modified-Since"));
if(_cache_control.length()) if(_cache_control.length())
request->addInterestingHeader("If-None-Match"); request->addInterestingHeader(F("If-None-Match"));
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n"); DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
return true; return true;
@ -122,7 +126,7 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
// Try to add default file, ensure there is a trailing '/' ot the path. // Try to add default file, ensure there is a trailing '/' ot the path.
if (path.length() == 0 || path[path.length()-1] != '/') if (path.length() == 0 || path[path.length()-1] != '/')
path += "/"; path += String('/');
path += _default_file; path += _default_file;
return _fileExists(request, path); return _fileExists(request, path);
@ -139,20 +143,20 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
bool fileFound = false; bool fileFound = false;
bool gzipFound = false; bool gzipFound = false;
String gzip = path + ".gz"; String gzip = path + F(".gz");
if (_gzipFirst) { if (_gzipFirst) {
request->_tempFile = _fs.open(gzip, "r"); request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
gzipFound = FILE_IS_REAL(request->_tempFile); gzipFound = FILE_IS_REAL(request->_tempFile);
if (!gzipFound){ if (!gzipFound){
request->_tempFile = _fs.open(path, "r"); request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
fileFound = FILE_IS_REAL(request->_tempFile); fileFound = FILE_IS_REAL(request->_tempFile);
} }
} else { } else {
request->_tempFile = _fs.open(path, "r"); request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
fileFound = FILE_IS_REAL(request->_tempFile); fileFound = FILE_IS_REAL(request->_tempFile);
if (!fileFound){ if (!fileFound){
request->_tempFile = _fs.open(gzip, "r"); request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
gzipFound = FILE_IS_REAL(request->_tempFile); gzipFound = FILE_IS_REAL(request->_tempFile);
} }
} }
@ -163,7 +167,7 @@ bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const St
// Extract the file name from the path and keep it in _tempObject // Extract the file name from the path and keep it in _tempObject
size_t pathLen = path.length(); size_t pathLen = path.length();
char * _tempPath = (char*)malloc(pathLen+1); char * _tempPath = (char*)malloc(pathLen+1);
snprintf(_tempPath, pathLen+1, "%s", path.c_str()); snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str());
request->_tempObject = (void*)_tempPath; request->_tempObject = (void*)_tempPath;
// Calculate gzip statistic // Calculate gzip statistic
@ -190,27 +194,27 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
String filename = String((char*)request->_tempObject); String filename = String((char*)request->_tempObject);
free(request->_tempObject); free(request->_tempObject);
request->_tempObject = NULL; request->_tempObject = NULL;
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str())) if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication(); return request->requestAuthentication();
if (request->_tempFile == true) { if (request->_tempFile == true) {
String etag = String(request->_tempFile.size()); String etag = String(request->_tempFile.size());
if (_last_modified.length() && _last_modified == request->header("If-Modified-Since")) { if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
request->_tempFile.close(); request->_tempFile.close();
request->send(304); // Not modified request->send(304); // Not modified
} else if (_cache_control.length() && request->hasHeader("If-None-Match") && request->header("If-None-Match").equals(etag)) { } else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {
request->_tempFile.close(); request->_tempFile.close();
AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
response->addHeader("Cache-Control", _cache_control); response->addHeader(F("Cache-Control"), _cache_control);
response->addHeader("ETag", etag); response->addHeader(F("ETag"), etag);
request->send(response); request->send(response);
} else { } else {
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback); AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
if (_last_modified.length()) if (_last_modified.length())
response->addHeader("Last-Modified", _last_modified); response->addHeader(F("Last-Modified"), _last_modified);
if (_cache_control.length()){ if (_cache_control.length()){
response->addHeader("Cache-Control", _cache_control); response->addHeader(F("Cache-Control"), _cache_control);
response->addHeader("ETag", etag); response->addHeader(F("ETag"), etag);
} }
request->send(response); request->send(response);
} }

View File

@ -26,8 +26,6 @@
#define os_strlen strlen #define os_strlen strlen
#endif #endif
static const String SharedEmptyString = String();
#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL };
@ -100,6 +98,7 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
if(_itemBuffer){ if(_itemBuffer){
free(_itemBuffer); free(_itemBuffer);
} }
} }
void AsyncWebServerRequest::_onData(void *buf, size_t len){ void AsyncWebServerRequest::_onData(void *buf, size_t len){
@ -147,9 +146,9 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
_parsedLength += len; _parsedLength += len;
} else { } else {
if(_parsedLength == 0){ if(_parsedLength == 0){
if(_contentType.startsWith("application/x-www-form-urlencoded")){ if(_contentType.startsWith(F("application/x-www-form-urlencoded"))){
_isPlainPost = true; _isPlainPost = true;
} else if(_contentType == "text/plain" && __is_param_char(((char*)buf)[0])){ } else if(_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])){
size_t i = 0; size_t i = 0;
while (i<len && __is_param_char(((char*)buf)[i++])); while (i<len && __is_param_char(((char*)buf)[i++]));
if(i < len && ((char*)buf)[i-1] == '='){ if(i < len && ((char*)buf)[i-1] == '='){
@ -183,7 +182,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
} }
void AsyncWebServerRequest::_removeNotInterestingHeaders(){ void AsyncWebServerRequest::_removeNotInterestingHeaders(){
if (_interestingHeaders.containsIgnoreCase("ANY")) return; // nothing to do if (_interestingHeaders.containsIgnoreCase(F("ANY"))) return; // nothing to do
for(const auto& header: _headers){ for(const auto& header: _headers){
if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){ if(!_interestingHeaders.containsIgnoreCase(header->name().c_str())){
_headers.remove(header); _headers.remove(header);
@ -263,23 +262,23 @@ bool AsyncWebServerRequest::_parseReqHead(){
String u = _temp.substring(m.length()+1, index); String u = _temp.substring(m.length()+1, index);
_temp = _temp.substring(index+1); _temp = _temp.substring(index+1);
if(m == "GET"){ if(m == F("GET")){
_method = HTTP_GET; _method = HTTP_GET;
} else if(m == "POST"){ } else if(m == F("POST")){
_method = HTTP_POST; _method = HTTP_POST;
} else if(m == "DELETE"){ } else if(m == F("DELETE")){
_method = HTTP_DELETE; _method = HTTP_DELETE;
} else if(m == "PUT"){ } else if(m == F("PUT")){
_method = HTTP_PUT; _method = HTTP_PUT;
} else if(m == "PATCH"){ } else if(m == F("PATCH")){
_method = HTTP_PATCH; _method = HTTP_PATCH;
} else if(m == "HEAD"){ } else if(m == F("HEAD")){
_method = HTTP_HEAD; _method = HTTP_HEAD;
} else if(m == "OPTIONS"){ } else if(m == F("OPTIONS")){
_method = HTTP_OPTIONS; _method = HTTP_OPTIONS;
} }
String g = String(); String g;
index = u.indexOf('?'); index = u.indexOf('?');
if(index > 0){ if(index > 0){
g = u.substring(index +1); g = u.substring(index +1);
@ -288,14 +287,14 @@ bool AsyncWebServerRequest::_parseReqHead(){
_url = urlDecode(u); _url = urlDecode(u);
_addGetParams(g); _addGetParams(g);
if(!_temp.startsWith("HTTP/1.0")) if(!_temp.startsWith(F("HTTP/1.0")))
_version = 1; _version = 1;
_temp = String(); _temp = String();
return true; return true;
} }
bool strContains(String src, String find, bool mindcase = true) { bool strContains(const String &src, const String &find, bool mindcase = true) {
int pos=0, i=0; int pos=0, i=0;
const int slen = src.length(); const int slen = src.length();
const int flen = find.length(); const int flen = find.length();
@ -305,7 +304,10 @@ bool strContains(String src, String find, bool mindcase = true) {
for (i=0; i < flen; i++) { for (i=0; i < flen; i++) {
if (mindcase) { if (mindcase) {
if (src[pos+i] != find[i]) i = flen + 1; // no match if (src[pos+i] != find[i]) i = flen + 1; // no match
} else if (tolower(src[pos+i]) != tolower(find[i])) i = flen + 1; // no match }
else if (tolower(src[pos+i]) != tolower(find[i])) {
i = flen + 1; // no match
}
} }
if (i == flen) return true; if (i == flen) return true;
pos++; pos++;
@ -320,30 +322,30 @@ bool AsyncWebServerRequest::_parseReqHeader(){
String value = _temp.substring(index + 2); String value = _temp.substring(index + 2);
if(name.equalsIgnoreCase("Host")){ if(name.equalsIgnoreCase("Host")){
_host = value; _host = value;
} else if(name.equalsIgnoreCase("Content-Type")){ } else if(name.equalsIgnoreCase(F("Content-Type"))){
_contentType = value.substring(0, value.indexOf(';')); _contentType = value.substring(0, value.indexOf(';'));
if (value.startsWith("multipart/")){ if (value.startsWith(F("multipart/"))){
_boundary = value.substring(value.indexOf('=')+1); _boundary = value.substring(value.indexOf('=')+1);
_boundary.replace("\"",""); _boundary.replace(String('"'), String());
_isMultipart = true; _isMultipart = true;
} }
} else if(name.equalsIgnoreCase("Content-Length")){ } else if(name.equalsIgnoreCase(F("Content-Length"))){
_contentLength = atoi(value.c_str()); _contentLength = atoi(value.c_str());
} else if(name.equalsIgnoreCase("Expect") && value == "100-continue"){ } else if(name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")){
_expectingContinue = true; _expectingContinue = true;
} else if(name.equalsIgnoreCase("Authorization")){ } else if(name.equalsIgnoreCase(F("Authorization"))){
if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase("Basic")){ if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase(F("Basic"))){
_authorization = value.substring(6); _authorization = value.substring(6);
} else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase("Digest")){ } else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase(F("Digest"))){
_isDigest = true; _isDigest = true;
_authorization = value.substring(7); _authorization = value.substring(7);
} }
} else { } else {
if(name.equalsIgnoreCase("Upgrade") && value.equalsIgnoreCase("websocket")){ if(name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))){
// WebSocket request can be uniquely identified by header: [Upgrade: websocket] // WebSocket request can be uniquely identified by header: [Upgrade: websocket]
_reqconntype = RCT_WS; _reqconntype = RCT_WS;
} else { } else {
if(name.equalsIgnoreCase("Accept") && strContains(value, "text/event-stream", false)){ if(name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)){
// WebEvent request can be uniquely identified by header: [Accept: text/event-stream] // WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
_reqconntype = RCT_EVENT; _reqconntype = RCT_EVENT;
} }
@ -359,9 +361,9 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
if(data && (char)data != '&') if(data && (char)data != '&')
_temp += (char)data; _temp += (char)data;
if(!data || (char)data == '&' || _parsedLength == _contentLength){ if(!data || (char)data == '&' || _parsedLength == _contentLength){
String name = "body"; String name = F("body");
String value = _temp; String value = _temp;
if(!_temp.startsWith("{") && !_temp.startsWith("[") && _temp.indexOf('=') > 0){ if(!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0){
name = _temp.substring(0, _temp.indexOf('=')); name = _temp.substring(0, _temp.indexOf('='));
value = _temp.substring(_temp.indexOf('=') + 1); value = _temp.substring(_temp.indexOf('=') + 1);
} }
@ -435,17 +437,17 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
_temp += (char)data; _temp += (char)data;
if((char)data == '\n'){ if((char)data == '\n'){
if(_temp.length()){ if(_temp.length()){
if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase("Content-Type")){ if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))){
_itemType = _temp.substring(14); _itemType = _temp.substring(14);
_itemIsFile = true; _itemIsFile = true;
} else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase("Content-Disposition")){ } else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
_temp = _temp.substring(_temp.indexOf(';') + 2); _temp = _temp.substring(_temp.indexOf(';') + 2);
while(_temp.indexOf(';') > 0){ while(_temp.indexOf(';') > 0){
String name = _temp.substring(0, _temp.indexOf('=')); String name = _temp.substring(0, _temp.indexOf('='));
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1); String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
if(name == "name"){ if(name == F("name")){
_itemName = nameVal; _itemName = nameVal;
} else if(name == "filename"){ } else if(name == F("filename")){
_itemFilename = nameVal; _itemFilename = nameVal;
_itemIsFile = true; _itemIsFile = true;
} }
@ -453,9 +455,9 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
} }
String name = _temp.substring(0, _temp.indexOf('=')); String name = _temp.substring(0, _temp.indexOf('='));
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1); String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
if(name == "name"){ if(name == F("name")){
_itemName = nameVal; _itemName = nameVal;
} else if(name == "filename"){ } else if(name == F("filename")){
_itemFilename = nameVal; _itemFilename = nameVal;
_itemIsFile = true; _itemIsFile = true;
} }
@ -574,8 +576,8 @@ void AsyncWebServerRequest::_parseLine(){
_server->_attachHandler(this); _server->_attachHandler(this);
_removeNotInterestingHeaders(); _removeNotInterestingHeaders();
if(_expectingContinue){ if(_expectingContinue){
const char * response = "HTTP/1.1 100 Continue\r\n\r\n"; String response = F("HTTP/1.1 100 Continue\r\n\r\n");
_client->write(response, os_strlen(response)); _client->write(response.c_str(), response.length());
} }
//check handler for authentication //check handler for authentication
if(_contentLength){ if(_contentLength){
@ -603,23 +605,7 @@ bool AsyncWebServerRequest::hasHeader(const String& name) const {
} }
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const { bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return hasHeader(String(data));
size_t n = 0;
while (1) {
if (pgm_read_byte(p+n) == 0) break;
n += 1;
}
char * name = (char*) malloc(n+1);
name[n] = 0;
if (name) {
for(size_t b=0; b<n; b++)
name[b] = pgm_read_byte(p++);
bool result = hasHeader( String(name) );
free(name);
return result;
} else {
return false;
}
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const { AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
@ -632,17 +618,7 @@ AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const { AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return getHeader(String(data));
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
}
} }
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const { AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
@ -664,19 +640,7 @@ bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) c
} }
bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const { bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return hasParam(String(data).c_str(), post, file);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
name[n] = 0;
if (name) {
strcpy_P(name,p);
bool result = hasParam( name, post, file);
free(name);
return result;
} else {
return false;
}
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const { AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const {
@ -689,17 +653,7 @@ AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const { AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return getParam(String(data).c_str(), post, file);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
AsyncWebParameter* result = getParam(name, post, file);
free(name);
return result;
} else {
return nullptr;
}
} }
AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const { AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
@ -735,7 +689,7 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const St
} }
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(fs.exists(path) || (!download && fs.exists(path+".gz"))) if(fs.exists(path) || (!download && fs.exists(path+F(".gz"))))
return new AsyncFileResponse(fs, path, contentType, download, callback); return new AsyncFileResponse(fs, path, contentType, download, callback);
return NULL; return NULL;
} }
@ -777,7 +731,7 @@ void AsyncWebServerRequest::send(int code, const String& contentType, const Stri
} }
void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){ void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(fs.exists(path) || (!download && fs.exists(path+".gz"))){ if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))){
send(beginResponse(fs, path, contentType, download, callback)); send(beginResponse(fs, path, contentType, download, callback));
} else send(404); } else send(404);
} }
@ -810,7 +764,7 @@ void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P co
void AsyncWebServerRequest::redirect(const String& url){ void AsyncWebServerRequest::redirect(const String& url){
AsyncWebServerResponse * response = beginResponse(302); AsyncWebServerResponse * response = beginResponse(302);
response->addHeader("Location",url); response->addHeader(F("Location"), url);
send(response); send(response);
} }
@ -832,12 +786,12 @@ bool AsyncWebServerRequest::authenticate(const char * hash){
if(_isDigest){ if(_isDigest){
String hStr = String(hash); String hStr = String(hash);
int separator = hStr.indexOf(":"); int separator = hStr.indexOf(':');
if(separator <= 0) if(separator <= 0)
return false; return false;
String username = hStr.substring(0, separator); String username = hStr.substring(0, separator);
hStr = hStr.substring(separator + 1); hStr = hStr.substring(separator + 1);
separator = hStr.indexOf(":"); separator = hStr.indexOf(':');
if(separator <= 0) if(separator <= 0)
return false; return false;
String realm = hStr.substring(0, separator); String realm = hStr.substring(0, separator);
@ -851,16 +805,16 @@ bool AsyncWebServerRequest::authenticate(const char * hash){
void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){ void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){
AsyncWebServerResponse * r = beginResponse(401); AsyncWebServerResponse * r = beginResponse(401);
if(!isDigest && realm == NULL){ if(!isDigest && realm == NULL){
r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\""));
} else if(!isDigest){ } else if(!isDigest){
String header = "Basic realm=\""; String header = F("Basic realm=\"");
header.concat(realm); header.concat(realm);
header.concat("\""); header += '"';
r->addHeader("WWW-Authenticate", header); r->addHeader(F("WWW-Authenticate"), header);
} else { } else {
String header = "Digest "; String header = F("Digest ");
header.concat(requestDigestAuthentication(realm)); header.concat(requestDigestAuthentication(realm));
r->addHeader("WWW-Authenticate", header); r->addHeader(F("WWW-Authenticate"), header);
} }
send(r); send(r);
} }
@ -875,17 +829,7 @@ bool AsyncWebServerRequest::hasArg(const char* name) const {
} }
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const { bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return hasArg(String(data).c_str());
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
bool result = hasArg( name );
free(name);
return result;
} else {
return false;
}
} }
@ -895,22 +839,11 @@ const String& AsyncWebServerRequest::arg(const String& name) const {
return arg->value(); return arg->value();
} }
} }
return SharedEmptyString; return emptyString;
} }
const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const { const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return arg(String(data).c_str());
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
const String & result = arg( String(name) );
free(name);
return result;
} else {
return SharedEmptyString;
}
} }
const String& AsyncWebServerRequest::arg(size_t i) const { const String& AsyncWebServerRequest::arg(size_t i) const {
@ -923,37 +856,27 @@ const String& AsyncWebServerRequest::argName(size_t i) const {
const String& AsyncWebServerRequest::pathArg(size_t i) const { const String& AsyncWebServerRequest::pathArg(size_t i) const {
auto param = _pathParams.nth(i); auto param = _pathParams.nth(i);
return param ? **param : SharedEmptyString; return param ? **param : emptyString;
} }
const String& AsyncWebServerRequest::header(const char* name) const { const String& AsyncWebServerRequest::header(const char* name) const {
AsyncWebHeader* h = getHeader(String(name)); AsyncWebHeader* h = getHeader(String(name));
return h ? h->value() : SharedEmptyString; return h ? h->value() : emptyString;
} }
const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const { const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data); return header(String(data).c_str());
size_t n = strlen_P(p); };
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
const String & result = header( (const char *)name );
free(name);
return result;
} else {
return SharedEmptyString;
}
};
const String& AsyncWebServerRequest::header(size_t i) const { const String& AsyncWebServerRequest::header(size_t i) const {
AsyncWebHeader* h = getHeader(i); AsyncWebHeader* h = getHeader(i);
return h ? h->value() : SharedEmptyString; return h ? h->value() : emptyString;
} }
const String& AsyncWebServerRequest::headerName(size_t i) const { const String& AsyncWebServerRequest::headerName(size_t i) const {
AsyncWebHeader* h = getHeader(i); AsyncWebHeader* h = getHeader(i);
return h ? h->name() : SharedEmptyString; return h ? h->name() : emptyString;
} }
String AsyncWebServerRequest::urlDecode(const String& text) const { String AsyncWebServerRequest::urlDecode(const String& text) const {
@ -980,26 +903,26 @@ String AsyncWebServerRequest::urlDecode(const String& text) const {
} }
const char * AsyncWebServerRequest::methodToString() const { const __FlashStringHelper *AsyncWebServerRequest::methodToString() const {
if(_method == HTTP_ANY) return "ANY"; if(_method == HTTP_ANY) return F("ANY");
else if(_method & HTTP_GET) return "GET"; else if(_method & HTTP_GET) return F("GET");
else if(_method & HTTP_POST) return "POST"; else if(_method & HTTP_POST) return F("POST");
else if(_method & HTTP_DELETE) return "DELETE"; else if(_method & HTTP_DELETE) return F("DELETE");
else if(_method & HTTP_PUT) return "PUT"; else if(_method & HTTP_PUT) return F("PUT");
else if(_method & HTTP_PATCH) return "PATCH"; else if(_method & HTTP_PATCH) return F("PATCH");
else if(_method & HTTP_HEAD) return "HEAD"; else if(_method & HTTP_HEAD) return F("HEAD");
else if(_method & HTTP_OPTIONS) return "OPTIONS"; else if(_method & HTTP_OPTIONS) return F("OPTIONS");
return "UNKNOWN"; return F("UNKNOWN");
} }
const char *AsyncWebServerRequest::requestedConnTypeToString() const { const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) { switch (_reqconntype) {
case RCT_NOT_USED: return "RCT_NOT_USED"; case RCT_NOT_USED: return F("RCT_NOT_USED");
case RCT_DEFAULT: return "RCT_DEFAULT"; case RCT_DEFAULT: return F("RCT_DEFAULT");
case RCT_HTTP: return "RCT_HTTP"; case RCT_HTTP: return F("RCT_HTTP");
case RCT_WS: return "RCT_WS"; case RCT_WS: return F("RCT_WS");
case RCT_EVENT: return "RCT_EVENT"; case RCT_EVENT: return F("RCT_EVENT");
default: return "ERROR"; default: return F("ERROR");
} }
} }

View File

@ -37,48 +37,52 @@ void* memchr(void* ptr, int ch, size_t count)
* Abstract Response * Abstract Response
* */ * */
const char* AsyncWebServerResponse::_responseCodeToString(int code) { const char* AsyncWebServerResponse::_responseCodeToString(int code) {
return reinterpret_cast<const char *>(responseCodeToString(code));
}
const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) { switch (code) {
case 100: return "Continue"; case 100: return F("Continue");
case 101: return "Switching Protocols"; case 101: return F("Switching Protocols");
case 200: return "OK"; case 200: return F("OK");
case 201: return "Created"; case 201: return F("Created");
case 202: return "Accepted"; case 202: return F("Accepted");
case 203: return "Non-Authoritative Information"; case 203: return F("Non-Authoritative Information");
case 204: return "No Content"; case 204: return F("No Content");
case 205: return "Reset Content"; case 205: return F("Reset Content");
case 206: return "Partial Content"; case 206: return F("Partial Content");
case 300: return "Multiple Choices"; case 300: return F("Multiple Choices");
case 301: return "Moved Permanently"; case 301: return F("Moved Permanently");
case 302: return "Found"; case 302: return F("Found");
case 303: return "See Other"; case 303: return F("See Other");
case 304: return "Not Modified"; case 304: return F("Not Modified");
case 305: return "Use Proxy"; case 305: return F("Use Proxy");
case 307: return "Temporary Redirect"; case 307: return F("Temporary Redirect");
case 400: return "Bad Request"; case 400: return F("Bad Request");
case 401: return "Unauthorized"; case 401: return F("Unauthorized");
case 402: return "Payment Required"; case 402: return F("Payment Required");
case 403: return "Forbidden"; case 403: return F("Forbidden");
case 404: return "Not Found"; case 404: return F("Not Found");
case 405: return "Method Not Allowed"; case 405: return F("Method Not Allowed");
case 406: return "Not Acceptable"; case 406: return F("Not Acceptable");
case 407: return "Proxy Authentication Required"; case 407: return F("Proxy Authentication Required");
case 408: return "Request Time-out"; case 408: return F("Request Time-out");
case 409: return "Conflict"; case 409: return F("Conflict");
case 410: return "Gone"; case 410: return F("Gone");
case 411: return "Length Required"; case 411: return F("Length Required");
case 412: return "Precondition Failed"; case 412: return F("Precondition Failed");
case 413: return "Request Entity Too Large"; case 413: return F("Request Entity Too Large");
case 414: return "Request-URI Too Large"; case 414: return F("Request-URI Too Large");
case 415: return "Unsupported Media Type"; case 415: return F("Unsupported Media Type");
case 416: return "Requested range not satisfiable"; case 416: return F("Requested range not satisfiable");
case 417: return "Expectation Failed"; case 417: return F("Expectation Failed");
case 500: return "Internal Server Error"; case 500: return F("Internal Server Error");
case 501: return "Not Implemented"; case 501: return F("Not Implemented");
case 502: return "Bad Gateway"; case 502: return F("Bad Gateway");
case 503: return "Service Unavailable"; case 503: return F("Service Unavailable");
case 504: return "Gateway Time-out"; case 504: return F("Gateway Time-out");
case 505: return "HTTP Version not supported"; case 505: return F("HTTP Version not supported");
default: return ""; default: return F("");
} }
} }
@ -125,33 +129,33 @@ void AsyncWebServerResponse::addHeader(const String& name, const String& value){
String AsyncWebServerResponse::_assembleHead(uint8_t version){ String AsyncWebServerResponse::_assembleHead(uint8_t version){
if(version){ if(version){
addHeader("Accept-Ranges","none"); addHeader(F("Accept-Ranges"), F("none"));
if(_chunked) if(_chunked)
addHeader("Transfer-Encoding","chunked"); addHeader(F("Transfer-Encoding"), F("chunked"));
} }
String out = String(); String out = String();
int bufSize = 300; int bufSize = 300;
char buf[bufSize]; char buf[bufSize];
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code)); snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
out.concat(buf); out.concat(buf);
if(_sendContentLength) { if(_sendContentLength) {
snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength); snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
out.concat(buf); out.concat(buf);
} }
if(_contentType.length()) { if(_contentType.length()) {
snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str()); snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
out.concat(buf); out.concat(buf);
} }
for(const auto& header: _headers){ for(const auto& header: _headers){
snprintf(buf, bufSize, "%s: %s\r\n", header->name().c_str(), header->value().c_str()); snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header->name().c_str(), header->value().c_str());
out.concat(buf); out.concat(buf);
} }
_headers.free(); _headers.free();
out.concat("\r\n"); out.concat(F("\r\n"));
_headLength = out.length(); _headLength = out.length();
return out; return out;
} }
@ -173,9 +177,9 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons
if(_content.length()){ if(_content.length()){
_contentLength = _content.length(); _contentLength = _content.length();
if(!_contentType.length()) if(!_contentType.length())
_contentType = "text/plain"; _contentType = F("text/plain");
} }
addHeader("Connection","close"); addHeader(F("Connection"), F("close"));
} }
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
@ -255,7 +259,7 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _ca
} }
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
addHeader("Connection","close"); addHeader(F("Connection"), F("close"));
_head = _assembleHead(request->version()); _head = _assembleHead(request->version());
_state = RESPONSE_HEADERS; _state = RESPONSE_HEADERS;
_ack(request, 0, 0); _ack(request, 0, 0);
@ -317,7 +321,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u
free(buf); free(buf);
return 0; return 0;
} }
outLen = sprintf((char*)buf+headLen, "%x", readLen) + headLen; outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
while(outLen < headLen + 4) buf[outLen++] = ' '; while(outLen < headLen + 4) buf[outLen++] = ' ';
buf[outLen++] = '\r'; buf[outLen++] = '\r';
buf[outLen++] = '\n'; buf[outLen++] = '\n';
@ -396,7 +400,7 @@ size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size
// If closing placeholder is found: // If closing placeholder is found:
if(pTemplateEnd) { if(pTemplateEnd) {
// prepare argument to callback // prepare argument to callback
const size_t paramNameLength = std::min(sizeof(buf) - 1, (unsigned int)(pTemplateEnd - pTemplateStart - 1)); const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
if(paramNameLength) { if(paramNameLength) {
memcpy(buf, pTemplateStart + 1, paramNameLength); memcpy(buf, pTemplateStart + 1, paramNameLength);
buf[paramNameLength] = 0; buf[paramNameLength] = 0;
@ -483,43 +487,48 @@ AsyncFileResponse::~AsyncFileResponse(){
} }
void AsyncFileResponse::_setContentType(const String& path){ void AsyncFileResponse::_setContentType(const String& path){
if (path.endsWith(".html")) _contentType = "text/html"; #if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
else if (path.endsWith(".htm")) _contentType = "text/html"; extern const __FlashStringHelper *getContentType(const String &path);
else if (path.endsWith(".css")) _contentType = "text/css"; _contentType = getContentType(path);
else if (path.endsWith(".json")) _contentType = "application/json"; #else
else if (path.endsWith(".js")) _contentType = "application/javascript"; if (path.endsWith(F(".html"))) _contentType = F("text/html");
else if (path.endsWith(".png")) _contentType = "image/png"; else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
else if (path.endsWith(".gif")) _contentType = "image/gif"; else if (path.endsWith(F(".css"))) _contentType = F("text/css");
else if (path.endsWith(".jpg")) _contentType = "image/jpeg"; else if (path.endsWith(F(".json"))) _contentType = F("application/json");
else if (path.endsWith(".ico")) _contentType = "image/x-icon"; else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
else if (path.endsWith(".svg")) _contentType = "image/svg+xml"; else if (path.endsWith(F(".png"))) _contentType = F("image/png");
else if (path.endsWith(".eot")) _contentType = "font/eot"; else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
else if (path.endsWith(".woff")) _contentType = "font/woff"; else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
else if (path.endsWith(".woff2")) _contentType = "font/woff2"; else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
else if (path.endsWith(".ttf")) _contentType = "font/ttf"; else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml");
else if (path.endsWith(".xml")) _contentType = "text/xml"; else if (path.endsWith(F(".eot"))) _contentType = F("font/eot");
else if (path.endsWith(".pdf")) _contentType = "application/pdf"; else if (path.endsWith(F(".woff"))) _contentType = F("font/woff");
else if (path.endsWith(".zip")) _contentType = "application/zip"; else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2");
else if(path.endsWith(".gz")) _contentType = "application/x-gzip"; else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf");
else _contentType = "text/plain"; else if (path.endsWith(F(".xml"))) _contentType = F("text/xml");
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf");
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip");
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip");
else _contentType = F("text/plain");
#endif
} }
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
_code = 200; _code = 200;
_path = path; _path = path;
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){ if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){
_path = _path+".gz"; _path = _path + F(".gz");
addHeader("Content-Encoding", "gzip"); addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process zipped templates _callback = nullptr; // Unable to process zipped templates
_sendContentLength = true; _sendContentLength = true;
_chunked = false; _chunked = false;
} }
_content = fs.open(_path, "r"); _content = fs.open(_path, fs::FileOpenMode::read);
_contentLength = _content.size(); _contentLength = _content.size();
if(contentType == "") if(contentType.length() == 0)
_setContentType(path); _setContentType(path);
else else
_contentType = contentType; _contentType = contentType;
@ -530,20 +539,20 @@ AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& c
if(download) { if(download) {
// set filename and force download // set filename and force download
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
} else { } else {
// set filename and force rendering // set filename and force rendering
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
} }
addHeader("Content-Disposition", buf); addHeader(F("Content-Disposition"), buf);
} }
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
_code = 200; _code = 200;
_path = path; _path = path;
if(!download && String(content.name()).endsWith(".gz") && !path.endsWith(".gz")){ if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){
addHeader("Content-Encoding", "gzip"); addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process gzipped templates _callback = nullptr; // Unable to process gzipped templates
_sendContentLength = true; _sendContentLength = true;
_chunked = false; _chunked = false;
@ -552,7 +561,7 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
_content = content; _content = content;
_contentLength = _content.size(); _contentLength = _content.size();
if(contentType == "") if(contentType.length() == 0)
_setContentType(path); _setContentType(path);
else else
_contentType = contentType; _contentType = contentType;
@ -562,11 +571,11 @@ AsyncFileResponse::AsyncFileResponse(File content, const String& path, const Str
char* filename = (char*)path.c_str() + filenameStart; char* filename = (char*)path.c_str() + filenameStart;
if(download) { if(download) {
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename); snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
} else { } else {
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename); snprintf_P(buf, sizeof (buf), PSTR("inline; filename=\"%s\""), filename);
} }
addHeader("Content-Disposition", buf); addHeader(F("Content-Disposition"), buf);
} }
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){ size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){

View File

@ -29,6 +29,11 @@ bool ON_AP_FILTER(AsyncWebServerRequest *request) {
return WiFi.localIP() != request->client()->localIP(); return WiFi.localIP() != request->client()->localIP();
} }
#ifndef HAVE_FS_FILE_OPEN_MODE
const char *fs::FileOpenMode::read = "r";
const char *fs::FileOpenMode::write = "w";
const char *fs::FileOpenMode::append = "a";
#endif
AsyncWebServer::AsyncWebServer(uint16_t port) AsyncWebServer::AsyncWebServer(uint16_t port)
: _server(port) : _server(port)
@ -52,7 +57,7 @@ AsyncWebServer::AsyncWebServer(uint16_t port)
} }
AsyncWebServer::~AsyncWebServer(){ AsyncWebServer::~AsyncWebServer(){
reset(); reset();
end(); end();
if(_catchAllHandler) delete _catchAllHandler; if(_catchAllHandler) delete _catchAllHandler;
} }
@ -118,8 +123,8 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
return; return;
} }
} }
request->addInterestingHeader("ANY"); request->addInterestingHeader(F("ANY"));
request->setHandler(_catchAllHandler); request->setHandler(_catchAllHandler);
} }
@ -183,7 +188,7 @@ void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
void AsyncWebServer::reset(){ void AsyncWebServer::reset(){
_rewrites.free(); _rewrites.free();
_handlers.free(); _handlers.free();
if (_catchAllHandler != NULL){ if (_catchAllHandler != NULL){
_catchAllHandler->onRequest(NULL); _catchAllHandler->onRequest(NULL);
_catchAllHandler->onUpload(NULL); _catchAllHandler->onUpload(NULL);

File diff suppressed because it is too large Load Diff

View File

@ -1,219 +1,224 @@
#define edit_htm_gz_len 4316
//File: edit.htm.gz, Size: 4365
#define edit_htm_gz_len 4365
const uint8_t edit_htm_gz[] PROGMEM = { const uint8_t edit_htm_gz[] PROGMEM = {
0x1F,0x8B,0x08,0x08,0x33,0x95,0x87,0x5E,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5, 0x1F,0x8B,0x08,0x08,0xC6,0xC7,0xA5,0x5E,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5,
0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0x50,0xD6,0x39,0x18,0x16,0x1E, 0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0xA5,0xAC,0x73,0x30,0x2C,0x50,
0x2B,0x7D,0x01,0x25,0x49,0x3B,0xCA,0xD8,0x3E,0xC5,0x56,0x12,0x15,0x5B,0xF6,0x2C,0x99,0x40,0xB3,0xFC, 0x56,0xFA,0x02,0x4A,0x42,0x3B,0xCA,0xD8,0x3E,0xC5,0x56,0x62,0x15,0x5B,0xF2,0x6C,0x99,0x40,0xB3,0xFC,
0xF7,0x9D,0x24,0x3F,0x43,0xE8,0x1E,0xDF,0xD6,0x07,0x91,0x74,0xBA,0xD3,0xDD,0xE9,0x9E,0x0A,0x7B,0x1B, 0xF7,0x9D,0x24,0x3F,0x43,0xE8,0x1E,0xDF,0xB6,0xAE,0x8D,0xA4,0xD3,0x9D,0xEE,0xA1,0x7B,0x29,0xD9,0xDD,
0xC7,0xE7,0x47,0xC3,0xAB,0x8B,0x13,0x6D,0xC6,0xA3,0x70,0x7F,0x4F,0xFC,0xD4,0x42,0x44,0xA7,0x9E,0x8E, 0x78,0x7E,0x7A,0x38,0xBE,0x3C,0x3B,0xD2,0x42,0x1E,0x47,0x7B,0xBB,0xE2,0x5F,0x2D,0x42,0x74,0xE6,0xE9,
0xA9,0xBE,0xBF,0x17,0x61,0x8E,0x00,0xC4,0x93,0x36,0xFE,0x3D,0x23,0x77,0x9E,0x7E,0x14,0x53,0x8E,0x29, 0x98,0xEA,0x7B,0xBB,0x31,0xE6,0x08,0x40,0x3C,0xE9,0xE2,0xDF,0x73,0x72,0xEB,0xE9,0x87,0x8C,0x72,0x4C,
0x6F,0xF3,0x87,0x04,0xEB,0x9A,0xAF,0x66,0x9E,0xCE,0xF1,0x3D,0xEF,0x08,0xE4,0x9E,0xE6,0xCF,0x50,0xCA, 0x79,0x97,0xDF,0x27,0x58,0xD7,0x7C,0x35,0xF3,0x74,0x8E,0xEF,0x78,0x4F,0x20,0x0F,0x34,0x3F,0x44,0x69,
0x30,0xF7,0x32,0x3E,0x69,0x3F,0x07,0x12,0x9C,0xF0,0x10,0xEF,0x9F,0x0C,0x2E,0xB4,0x93,0x80,0xF0,0x38, 0x86,0xB9,0x97,0xF3,0x69,0xF7,0x19,0x90,0xE0,0x84,0x47,0x78,0xEF,0x68,0x74,0xA6,0x1D,0x05,0x84,0xB3,
0xDD,0xEB,0xA8,0x95,0x3D,0xC6,0x1F,0x42,0xAC,0x09,0x42,0x39,0xBE,0xCF,0x98,0xAE,0x45,0x38,0x20,0xC8, 0x74,0xB7,0xA7,0x56,0x76,0x23,0x42,0x6F,0xB4,0x14,0x47,0x9E,0x8E,0x92,0x24,0xC2,0x5D,0xCE,0x72,0x3F,
0xD3,0x99,0x9F,0x62,0xC1,0x41,0x88,0xC6,0x38,0x5C,0x4C,0x62,0x38,0x92,0x91,0xCF,0xD8,0xED,0x6E,0x25, 0xEC,0x12,0xA0,0xA9,0x6B,0x61,0x8A,0xA7,0x9E,0xDE,0x43,0x3E,0xB6,0x61,0x41,0xD7,0xC4,0x71,0x9E,0x4E,
0xF7,0x3D,0x39,0x9D,0xA0,0x88,0x84,0x0F,0x2E,0x43,0x94,0xB5,0x19,0x4E,0xC9,0x64,0x69,0xFB,0xD1,0xE2, 0x62,0x34,0xC3,0xBD,0x3B,0xB5,0xA7,0x49,0x20,0x0B,0x59,0xCA,0xFD,0x9C,0x6B,0xFF,0x0A,0xFB,0x1F,0x20,
0x73,0x9B,0xD0,0x00,0xDF,0xBB,0xDB,0x8E,0xD3,0x4B,0x62,0x46,0x38,0x89,0xA9,0x8B,0xC6,0x2C,0x0E,0x33, 0x65,0xFC,0x3E,0xC2,0x0A,0xA4,0x64,0xF6,0xB3,0x4C,0xD7,0x62,0x1C,0x10,0x04,0x5C,0xF8,0x29,0x16,0x5A,
0x8E,0x7B,0x21,0x9E,0x70,0xF7,0x19,0x50,0x18,0xC7,0x69,0x80,0x53,0xB7,0x9B,0xDC,0x6B,0x00,0x22,0x81, 0x8B,0xD0,0x04,0x47,0x8B,0x29,0x03,0x35,0x65,0xE4,0x33,0x76,0xFB,0x5B,0xC9,0xDD,0x40,0x4E,0xA7,0x28,
0xF6,0xD5,0xCE,0xCE,0x4E,0x6F,0x8C,0xFC,0xDB,0x69,0x1A,0x67,0x34,0x68,0xFB,0x71,0x18,0xA7,0xEE,0x57, 0x26,0xD1,0xBD,0x9B,0x21,0x9A,0x75,0x33,0x9C,0x92,0xE9,0xD2,0xF6,0xE3,0xC5,0xE7,0x2E,0xA1,0x01,0xBE,
0x93,0x67,0xE2,0x6F,0x2F,0x20,0x2C,0x09,0xD1,0x83,0x4B,0x63,0x8A,0x01,0xF7,0xBE,0xCD,0x66,0x28,0x88, 0x73,0x9F,0x38,0xCE,0x20,0x61,0x19,0xE1,0x84,0x51,0x17,0x4D,0x32,0x16,0xE5,0x1C,0x0F,0x22,0x3C,0xE5,
0xE7,0xAE,0xA3,0x39,0x5A,0xD7,0x01,0x22,0xE9,0x74,0x8C,0x0C,0xC7,0x12,0x7F,0xED,0x1D,0xB3,0xF7,0xB7, 0xEE,0x53,0xA0,0x30,0x61,0x69,0x80,0x53,0xB7,0x9F,0xDC,0x69,0x00,0x22,0x81,0xF6,0xD5,0xF6,0xF6,0xF6,
0xF8,0x55,0xCB,0x73,0x4C,0xA6,0x33,0xEE,0x7E,0xEF,0x38,0x82,0x7F,0x2D,0x0B,0x17,0x21,0x61,0xBC,0x2D, 0x60,0x82,0xFC,0x9B,0x59,0xCA,0x72,0x1A,0x74,0x7D,0x16,0xB1,0xD4,0xFD,0x6A,0xFA,0x54,0xFC,0x19,0x04,
0x95,0xA3,0xCE,0xE3,0x71,0xE2,0x3A,0x92,0x75,0xF8,0x88,0x50,0x3A,0x25,0x14,0x06,0x09,0x0A,0x02,0x42, 0x24,0x4B,0x22,0x74,0xEF,0x52,0x46,0x31,0xE0,0xDE,0x75,0xB3,0x10,0x05,0x6C,0xEE,0x3A,0x9A,0xA3,0xF5,
0xA7,0xAE,0x42,0x0B,0xC9,0xA2,0x94,0x36,0xC5,0x21,0xE2,0xE4,0x0E,0xF7,0x22,0x42,0xDB,0x73,0x12,0xF0, 0x1D,0x20,0x92,0xCE,0x26,0xC8,0x70,0x2C,0xF1,0xC7,0xDE,0x36,0x07,0x7F,0x8B,0x5F,0xB5,0x3C,0xC7,0x64,
0x99,0xBB,0xEB,0x00,0x17,0x7E,0x96,0x32,0x90,0x29,0x89,0x09,0xE5,0x38,0x95,0x68,0x2C,0x41,0x74,0xA1, 0x16,0x72,0xF7,0x7B,0xC7,0x11,0xFC,0x6B,0x79,0xB4,0x88,0x48,0xC6,0xBB,0x52,0x39,0xEA,0x3C,0xCE,0x12,
0x44,0x15,0x0A,0x28,0xE5,0x24,0x34,0x24,0x14,0xB7,0xC7,0x61,0xEC,0xDF,0x96,0x27,0xED,0x26,0xF7,0xF9, 0xD7,0x91,0xAC,0xC3,0x47,0x8C,0xD2,0x19,0xA1,0x30,0x48,0x50,0x10,0x10,0x3A,0x73,0x15,0x5A,0x44,0x16,
0x59,0xEE,0x2C,0xBE,0xC3,0xE9,0xA2,0x52,0x97,0xC4,0x6E,0x00,0x1B,0xA4,0x31,0xC6,0x4B,0x9B,0xDF,0x65, 0x95,0xB4,0x60,0x07,0xC4,0xC9,0x2D,0x1E,0xC4,0x84,0x76,0xE7,0x24,0xE0,0xA1,0xBB,0xE3,0x00,0x17,0x7E,
0x00,0xB5,0xE4,0x27,0x48,0x99,0x53,0xAD,0x8B,0xD4,0x14,0x3C,0x47,0x21,0x34,0xC9,0xF8,0xE2,0xF1,0x55, 0x9E,0x66,0x20,0x53,0xC2,0x08,0xE5,0x38,0x95,0x68,0x59,0x82,0xE8,0x42,0x89,0x2A,0x14,0x50,0xC9,0x49,
0xC6,0x09,0xF2,0x09,0x7F,0x70,0x1D,0xB9,0x4D,0x5A,0x88,0xBB,0xE3,0x38,0x9A,0x50,0xB8,0xF6,0x1E,0xA7, 0x28,0x58,0x14,0x77,0x27,0x11,0xF3,0x6F,0xAA,0x93,0x76,0x92,0xBB,0xE2,0x2C,0x37,0x64,0xB7,0x38,0x5D,
0x01,0xA2,0xC8,0xEA,0xA7,0x04,0x85,0xD6,0xA0,0x52,0x79,0x3B,0x8A,0x3F,0xB7,0x33,0x18,0xC3,0x3C,0xC4, 0xD4,0xEA,0x92,0xD8,0x2D,0x60,0x8B,0x34,0xC6,0x78,0x69,0xF3,0xDB,0x1C,0xA0,0x96,0xFC,0x04,0x29,0x0B,
0x3E,0x57,0x1A,0x86,0x4B,0x18,0xDF,0x12,0xFE,0x18,0xF0,0x68,0xA1,0xA6,0x2B,0xA9,0xA1,0x99,0xBA,0xBD, 0xAA,0x4D,0x91,0xDA,0x82,0x17,0x28,0x84,0x26,0x39,0x5F,0x3C,0x34,0x25,0x4B,0x90,0x4F,0xF8,0xBD,0xEB,
0x2E,0xE8,0x46,0xF2,0xA1,0xC4,0x56,0x12,0xB5,0xC7,0x31,0xE7,0x71,0x24,0xED,0xAD,0x94,0x56,0x93,0x26, 0xC8,0x6D,0xF2,0x86,0xB8,0xDB,0x8E,0xA3,0x09,0x85,0x6B,0xEF,0x71,0x1A,0x20,0x8A,0xAC,0x61,0x4A,0x50,
0xF3,0xFC,0xD1,0x75,0xAC,0x57,0x7D,0x8D,0x7C,0x0F,0x94,0xCA,0x89,0x8F,0xC2,0x36,0x0A,0xC9,0x94,0xBA, 0x64,0x8D,0x6A,0x95,0x77,0x63,0xF6,0xB9,0x9B,0xC3,0x18,0xE6,0x11,0xF6,0xB9,0xD2,0x30,0x18,0x61,0x72,
0x11,0x09,0x82,0x10,0xD7,0x4C,0xD6,0xCD,0xD2,0xD0,0x08,0x10,0x47,0x2E,0x89,0xD0,0x14,0x77,0x12,0x3A, 0x43,0xF8,0x43,0xC0,0x83,0x85,0x86,0xAE,0xA4,0x86,0x42,0x65,0xBD,0x3E,0xE8,0x46,0xF2,0xA1,0xC4,0x56,
0x05,0x28,0xC3,0xBB,0x3B,0x16,0x79,0x7F,0x78,0x7E,0x39,0x77,0x5E,0xBF,0x98,0xC6,0x7D,0xF8,0x73,0x36, 0x12,0x75,0x27,0x8C,0x73,0x16,0xCB,0xFB,0x56,0x49,0xAB,0xC9,0x2B,0xF3,0xEC,0x81,0x39,0xD6,0xAB,0xBE,
0x18,0xCD,0x4E,0x46,0x53,0x18,0x1D,0x8A,0x69,0xFF,0xDD,0x51,0xFF,0x1D,0x7C,0x1C,0x75,0x5B,0x9F,0x26, 0x41,0x7E,0x00,0x4A,0xE5,0xC4,0x47,0x51,0x17,0x45,0x64,0x46,0xDD,0x98,0x04,0x41,0x84,0x07,0x0D,0x1B,
0xBF,0xF7,0xC5,0xF2,0x8B,0xE0,0x70,0x38,0x3A,0xE9,0xF7,0x5F,0x77,0x5E,0x9E,0xCD,0x3F,0xBC,0xDE,0x15, 0xE4,0x69,0x64,0x04,0x88,0x23,0x57,0xF9,0x59,0x42,0x67,0x00,0xCD,0xF0,0xCE,0xB6,0x45,0xDE,0x1F,0x9C,
0xE0,0xC3,0xD0,0xB9,0x7C,0x3F,0x73,0x46,0x5B,0x3F,0x44,0xC1,0x69,0x30,0xF3,0xA3,0x51,0xFF,0xDD,0x87, 0x9E,0xCF,0x9D,0xD7,0x2F,0x66,0x6C,0x08,0xFF,0x9D,0x8C,0x2E,0xC2,0xA3,0x8B,0x19,0x8C,0x0E,0xC4,0x74,
0xCB,0xBB,0xAB,0x68,0x34,0x1D,0x7C,0xE8,0xCE,0x3E,0x6E,0xBD,0x1F,0x7C,0xFC,0xF0,0xD3,0x2D,0xFE,0xF9, 0xF8,0xEE,0x70,0xF8,0x0E,0x3E,0x0E,0xFB,0x9D,0x4F,0xD3,0xDF,0x87,0x62,0xF9,0x45,0x70,0x30,0xBE,0x38,
0xF4,0xD5,0xC7,0xE1,0x1C,0x10,0x8E,0xE3,0xC1,0xE8,0xF2,0xF0,0xFD,0xE1,0xF4,0xE3,0xA1,0x7F,0x72,0x3F, 0x1A,0x0E,0x5F,0xF7,0x5E,0x9E,0xCC,0x3F,0xBC,0xDE,0x11,0xE0,0x83,0xC8,0x39,0x7F,0x1F,0x3A,0x17,0x5B,
0x0E,0xCF,0x0E,0x5F,0xF4,0xC7,0xFD,0x2D,0x1F,0xE3,0xE9,0xF0,0xF2,0x30,0x7B,0xF9,0xFA,0x7C,0x4A,0xC8, 0x3F,0xC4,0xC1,0x71,0x10,0xFA,0xF1,0xC5,0xF0,0xDD,0x87,0xF3,0xDB,0xCB,0xF8,0x62,0x36,0xFA,0xD0,0x0F,
0x6C,0xF0,0xF1,0x6C,0xE8,0x1F,0x3D,0x7B,0x33,0x3A,0xED,0x93,0xD9,0xD9,0xAB,0x4B,0xE7,0xF6,0xC5,0xEB, 0x3F,0x6E,0xBD,0x1F,0x7D,0xFC,0xF0,0xD3,0x0D,0xFE,0xF9,0xF8,0xD5,0xC7,0xF1,0x1C,0x10,0x9E,0xB3,0xD1,
0xA3,0x63,0xFF,0xEA,0xD5,0xD5,0xEE,0xF1,0x76,0xE7,0xFB,0xEF,0xDF,0x06,0xE7,0x84,0x0E,0xEF,0x3E,0xF7, 0xC5,0xF9,0xC1,0xFB,0x83,0xD9,0xC7,0x03,0xFF,0xE8,0x6E,0x12,0x9D,0x1C,0xBC,0x18,0x4E,0x86,0x5B,0x3E,
0xA7,0x47,0xF3,0xE7,0x0F,0x6C,0x38,0x7B,0x79,0x47,0x3B,0xEF,0xE3,0x4F,0x2F,0x1F,0xDE,0xC2,0xFF,0x8B, 0xC6,0xB3,0xF1,0xF9,0x41,0xFE,0xF2,0xF5,0xE9,0x8C,0x90,0x70,0xF4,0xF1,0x64,0xEC,0x1F,0x3E,0x7D,0x73,
0x8B,0xD6,0x78,0xD0,0x65,0xA3,0x77,0x2F,0xDF,0x6F,0xB1,0x1F,0x9E,0x25,0x87,0xC7,0xC7,0x77,0xD1,0xF8, 0x71,0x3C,0x24,0xE1,0xC9,0xAB,0x73,0xE7,0xE6,0xC5,0xEB,0xC3,0xE7,0xFE,0xE5,0xAB,0xCB,0x9D,0xE7,0x4F,
0xA2,0x13,0x05,0xB7,0x13,0xFE,0x7C,0x9B,0x27,0x57,0xD3,0xEC,0xE3,0xEF,0xCF,0x5E,0xCD,0x3A,0xE7,0x18, 0x7A,0xDF,0x7F,0xFF,0x36,0x38,0x25,0x74,0x7C,0xFB,0x79,0x38,0x3B,0x9C,0x3F,0xBB,0xCF,0xC6,0xE1,0xCB,
0x5D,0xCD,0x5A,0x0F,0x9F,0x1F,0x9E,0xCF,0x86,0xA7,0x77,0x67,0x21,0xBA,0x3F,0x3B,0xF3,0x3F,0x47,0xAD, 0x5B,0xDA,0x7B,0xCF,0x3E,0xBD,0xBC,0x7F,0x0B,0x7F,0xCF,0xCE,0x3A,0x93,0x51,0x3F,0xBB,0x78,0xF7,0xF2,
0x10,0xFD,0x70,0x3E,0x0C,0x51,0xDA,0x1D,0x05,0xFD,0x4E,0xEB,0x68,0xAB,0xBF,0xC3,0xD3,0xCB,0x23,0x7A, 0xFD,0x56,0xF6,0xC3,0xD3,0xE4,0xE0,0xF9,0xF3,0xDB,0x78,0x72,0xD6,0x8B,0x83,0x9B,0x29,0x7F,0xF6,0x84,
0xBC,0xFD,0x69,0xF0,0xFC,0xF0,0xB0,0x1B,0x8F,0x7F,0xDF,0x7A,0x71,0xBB,0xFB,0x62,0xB4,0xFB,0x6E,0xFC, 0x27,0x97,0xB3,0xFC,0xE3,0xEF,0x4F,0x5F,0x85,0xBD,0x53,0x8C,0x2E,0xC3,0xCE,0xFD,0xE7,0xFB,0x67,0xE1,
0xAE,0x3F,0xD8,0x39,0x1C,0xA1,0xAB,0xC1,0xBB,0xFE,0x64,0x67,0x3C,0x9B,0xBD,0x7E,0x3D,0xFC,0x29,0xE8, 0xF8,0xF8,0xF6,0x24,0x42,0x77,0x27,0x27,0xFE,0xE7,0xB8,0x13,0xA1,0x1F,0x4E,0xC7,0x11,0x4A,0xFB,0x17,
0x7F,0x4E,0xFB,0xE7,0xF3,0xFE,0xFD,0xC9,0xA8,0x7F,0xDA,0x7A,0x7D,0xF2,0xD6,0xE9,0x0E,0xAE,0xB6,0xA7, 0xC1,0xB0,0xD7,0x39,0xDC,0x1A,0x6E,0xF3,0xF4,0xFC,0x90,0x3E,0x7F,0xF2,0x69,0xF4,0xEC,0xE0,0xA0,0xCF,
0x6F,0x77,0xE7,0x87,0xEC,0xA4,0xFF,0xEE,0xD0,0x99,0xBE,0x6A,0x45,0xE8,0x63,0x7C,0xB4,0x3D,0x7D,0xB9, 0x26,0xBF,0x6F,0xBD,0xB8,0xD9,0x79,0x71,0xB1,0xF3,0x6E,0xF2,0x6E,0x38,0xDA,0x3E,0xB8,0x40,0x97,0xA3,
0x4B,0x2E,0xAE,0x50,0xFF,0xE5,0xE1,0x2B,0x46,0x2E,0xA3,0xD3,0x91,0xD3,0xEF,0xBF,0x39,0xC7,0x3F,0x1D, 0x77,0xC3,0xE9,0xF6,0x24,0x0C,0x5F,0xBF,0x1E,0xFF,0x14,0x0C,0x3F,0xA7,0xC3,0xD3,0xF9,0xF0,0xEE,0xE8,
0x6D,0xA3,0xD7,0x5B,0xFE,0x07,0xD0,0xFF,0xE8,0x67,0xFC,0x7D,0xAB,0x3F,0x3F,0x77,0x42,0xFF,0x07,0x3C, 0x62,0x78,0xDC,0x79,0x7D,0xF4,0xD6,0xE9,0x8F,0x2E,0x9F,0xCC,0xDE,0xEE,0xCC,0x0F,0xB2,0xA3,0xE1,0xBB,
0x3C,0xBD,0x1A,0xCA,0xDB,0x39,0x09,0x7F,0x1A,0xDE,0x0E,0xB2,0x77,0xD1,0xD1,0x91,0xA9,0xD1,0xB8,0x9D, 0x03,0x67,0xF6,0xAA,0x13,0xA3,0x8F,0xEC,0xF0,0xC9,0xEC,0xE5,0x0E,0x39,0xBB,0x44,0xC3,0x97,0x07,0xAF,
0xE2,0x04,0x23,0x5E,0x8F,0x55,0xA5,0x73,0x80,0x6D,0x55,0x56,0x98,0x3B,0xA9,0x88,0xAC,0xED,0x00,0xFB, 0x32,0x72,0x1E,0x1F,0x5F,0x38,0xC3,0xE1,0x9B,0x53,0xFC,0xD3,0xE1,0x13,0xF4,0x7A,0xCB,0xFF,0x00,0xFA,
0x71,0x8A,0xE4,0x1E,0x40,0xC0,0xA9,0x30,0xB1,0xE5,0x8F,0x32,0xD4,0x6A,0x2A,0xD2,0x6A,0x88,0x06,0x9A, 0xBF,0xF8,0x19,0x7F,0xDF,0x19,0xCE,0x4F,0x9D,0xC8,0xFF,0x01,0x8F,0x8F,0x2F,0xC7,0xD2,0x3A,0x47,0xD1,
0x51,0x38,0x42,0x44,0x28,0xE0,0xDC,0x11,0x1F,0xB7,0x13,0x72,0x8F,0xC3,0xB6,0x44,0x76,0x1D,0x73,0x21, 0x4F,0xE3,0x9B,0x51,0xFE,0x2E,0x3E,0x3C,0x34,0x35,0xCA,0xBA,0x29,0x4E,0x30,0xE2,0xCD,0x58,0x55,0x39,
0x9D,0xAD,0xD8,0x86,0x28,0x89,0x14,0xD5,0x62,0x21,0xF8,0x84,0x7C,0x91,0x1E,0xC0,0x65,0x22,0xF8,0xCC, 0x07,0xDC,0xAD,0xFA,0x16,0x16,0x4E,0x2A,0x22,0x6B,0x37,0xC0,0x3E,0x4B,0x91,0xDC,0x03,0x08,0x38,0x15,
0x7D,0x27,0x4E,0xDB,0xE3,0x6C,0x3A,0x21,0xF7,0xE0,0xCD,0x13,0x42,0x09,0xC7,0x5A,0x97,0x2D,0x7F,0x2C, 0x57,0x6C,0xF9,0xA3,0x0C,0xB5,0x9A,0x8A,0xB4,0x1A,0xA2,0x81,0x66,0x94,0x8E,0x10,0x13,0x0A,0x38,0xB7,
0xC8,0xDC,0xE2,0x87,0x49,0x8A,0x22,0xCC,0xB4,0xBF,0x49,0x66,0x31,0x49,0xE3,0x68,0x51,0x45,0x40,0x1E, 0xC4,0xC7,0xDD,0x84,0xDC,0xE1,0xA8,0x2B,0x91,0x5D,0xC7,0x5C,0x48,0x67,0x2B,0xB7,0x21,0x4A,0x62,0x45,
0xD7,0x26,0xCB,0xE5,0x57,0x59,0x12,0xC6,0x08,0xC4,0x5C,0x13,0x36,0x54,0x30,0x4D,0x85,0x5B,0x95,0x41, 0xB5,0x5C,0x08,0x3E,0x21,0x5F,0xA4,0x34,0x70,0x99,0x18,0x3E,0x0B,0xDF,0x61,0x69,0x77,0x92,0xCF,0xA6,
0xB5,0x70,0xB3,0x2D,0x70,0xCB,0x86,0x5B,0x6F,0xED,0x54,0xAE,0xDB,0x96,0x7B,0x45,0xB0,0xAF,0xAB,0xBE, 0xE4,0x0E,0xBC,0x79,0x4A,0x28,0xE1,0x58,0xEB,0x67,0xCB,0x1F,0x4B,0x32,0x37,0xF8,0x7E,0x9A,0xA2,0x18,
0x16,0x0F,0x6A,0xB1,0xEE,0x2B,0x0E,0x2A,0x5D,0x7F,0xB8,0x3A,0x44,0x45,0x86,0x8A,0x01,0x15,0xA5,0xBB, 0x67,0xDA,0xDF,0x24,0xB3,0x98,0xA6,0x2C,0x5E,0xD4,0x11,0x90,0xB3,0xC6,0x64,0xB9,0xFC,0x2A,0x4F,0x22,
0xBB,0x4E,0x2D,0x54,0xC0,0xBE,0xE5,0x57,0x58,0xA6,0x4C,0xEB,0xAB,0x24,0x85,0xDB,0xC0,0xF3,0x2F,0xD0, 0x86,0x40,0xCC,0x35,0x61,0x43,0x05,0xD3,0x54,0xB8,0x55,0x15,0x54,0x4B,0x37,0xDB,0x02,0xB7,0x6C,0xB9,
0x2C,0x65,0x6A,0xD2,0x06,0x9A,0x65,0xBA,0x53,0x22,0x54,0x39,0x4F,0xF2,0x5A,0x92,0x7E,0x2C,0x15,0x80, 0xF5,0xD6,0x76,0xED,0xBA,0x5D,0xB9,0x57,0x04,0xFB,0xA6,0xEA,0x1B,0xF1,0xA0,0x11,0xEB,0xBE,0xE2,0xA0,
0x4B,0x6E,0x9E,0x09,0x6E,0xBE,0xA8,0xD5,0xED,0xDD,0x6F,0x72,0x1E,0x76,0x9C,0x6F,0x96,0xB6,0xDA,0x5B, 0xD2,0xF5,0x87,0xAB,0x43,0x54,0x64,0xA8,0x19,0x50,0x51,0xBA,0xBF,0xE3,0x34,0x42,0x05,0xEC,0x5B,0x7E,
0xE6,0xE3,0xAE,0x03,0x7F,0x8A,0xB4,0xFB,0xBC,0x62,0x61,0xFC,0x4C,0xFC,0x2D,0x18,0x04,0x3A,0x75,0xE0, 0x85,0x65,0x9A,0xB7,0xBE,0x4A,0x52,0xB0,0x06,0x9E,0x7F,0x81,0x66,0x25,0x53,0x9B,0x36,0xD0,0xAC,0xD2,
0xF6,0xCE,0x0F,0xCF,0x83,0x71,0x01,0x54,0x82,0x3D,0x0D,0x4F,0x51,0x40,0x32,0xE6,0x3E,0x73,0xBE,0x51, 0x9D,0x12,0xA1,0xCE,0x79,0x92,0xD7,0x8A,0xF4,0x43,0xA9,0x00,0x5C,0x71,0xF3,0x54,0x70,0xF3,0x45,0xAD,
0xFA,0x84,0xEB,0x03,0xD9,0x8B,0xCB,0x55,0x93,0xCA,0x62,0x59,0x42,0xA8,0xB6,0xC5,0x34,0x71,0xDF,0x28, 0x3E,0xD9,0xF9,0xA6,0xE0,0x61,0xDB,0xF9,0x66,0x69,0xAB,0xBD,0x55,0x3E,0xEE,0x3B,0xF0,0x5F,0x99,0x76,
0x2D,0x0D,0xB3,0x9E,0xEA,0xC1,0x45,0x2A,0xD3,0x14,0xFB,0x17,0xCE,0x37,0x0B,0x9E,0x22,0xCA,0x26,0x71, 0x9F,0xD5,0x2C,0x4C,0x9E,0x8A,0x3F,0x25,0x83,0x40,0xA7,0x09,0x7C,0xB2,0xFD,0xC3,0xB3,0x60,0x52,0x02,
0x1A,0xB9,0x69,0xCC,0x11,0xC7,0x86,0x63,0x2E,0xBB,0xCE,0x3A,0xC0,0xF6,0xAE,0x13,0xE0,0xA9,0xB9,0x5C, 0x95,0x60,0x8F,0xC3,0x53,0x14,0x90,0x3C,0x73,0x9F,0x3A,0xDF,0x28,0x7D,0x82,0xF9,0x40,0xF6,0xD2,0xB8,
0xEE,0x75,0x20,0xA9,0xC9,0x8A,0xC7,0x4F,0x49,0xC2,0xF7,0x27,0x19,0xF5,0x05,0x0F,0xDA,0x14,0x1B,0xD8, 0x6A,0x52,0xDF,0xD8,0x2C,0x21,0x54,0xDB,0xCA,0x34,0x61,0x6F,0x94,0x56,0x17,0xB3,0x99,0xEA,0xC1,0x45,
0x5C,0xA4,0x98,0x67,0x29,0xD5,0x82,0xD8,0xCF,0x84,0xB1,0xDB,0x53,0xCC,0x4F,0x94,0xDD,0x1F,0x3E,0xBC, 0xEA,0xAB,0x29,0xF6,0x2F,0x9C,0x6F,0x16,0x3C,0x45,0x34,0x9B,0xB2,0x34,0x76,0x53,0xC6,0x11,0xC7,0x86,
0x0C,0x60,0xC7,0xB2,0x44,0xF0,0xD7,0x22,0x80,0x03,0xC3,0x79,0x39,0x4E,0x63,0x3F,0x24,0x18,0x20,0xF2, 0x63,0x2E,0xFB,0xCE,0x3A,0xC0,0x93,0x1D,0x27,0xC0,0x33,0x73,0xB9,0xDC,0xED,0xC9,0xA4,0x06,0x15,0x8F,
0x1A,0x3F,0x18,0xD8,0xE2,0x25,0x22,0xB6,0xC5,0xBA,0x61,0x14,0xDB,0x00,0x48,0xCD,0xC5,0x1D,0x68,0x01, 0x9F,0x92,0x84,0xEF,0x4D,0x73,0xEA,0x0B,0x1E,0xB4,0x19,0x36,0xB0,0xB9,0x48,0x31,0xCF,0x53,0xAA,0x05,
0x79,0xF8,0x9A,0xDF,0x58,0xC4,0xA3,0xF0,0xD1,0xCB,0xB7,0x93,0x7D,0x74,0xD0,0xEE,0xBA,0x68,0x9F,0x1C, 0xCC,0xCF,0xC5,0x65,0xB7,0x67,0x98,0x1F,0xA9,0x7B,0x7F,0x70,0xFF,0x32,0x80,0x1D,0xCB,0x0A,0xC1,0x5F,
0x74,0xC1,0xBF,0xCC,0x1A,0x7D,0x75,0xF2,0x4F,0x24,0xC4,0xA3,0xDC,0xE5,0xC4,0x41,0x15,0xB5,0x8E,0x41, 0x8B,0x00,0x0E,0x0C,0xE7,0x15,0x38,0xAD,0xFD,0x90,0x60,0x80,0xC8,0x6B,0x7C,0x6F,0x60,0x8B,0x57,0x88,
0x2E,0x66,0xA0,0x48,0xF3,0x3B,0xE3,0x7C,0xA0,0x19,0xD7,0xDF,0xB7,0x7F,0xB8,0xF9,0xA3,0x7B,0xED,0xB4, 0xD8,0x16,0xEB,0x86,0x51,0x6E,0x03,0x20,0x35,0x17,0xB7,0xA0,0x05,0xE4,0xE1,0x2B,0x7E,0x6D,0x11,0x8F,
0xBB,0x37,0xE6,0x6F,0x66,0x87,0xD8,0x1C,0x33,0x6E,0x50,0x74,0x47,0xA6,0x08,0xEC,0xDC,0x16,0xF9,0xB6, 0xC2,0xC7,0xA0,0xD8,0x4E,0xF6,0xD0,0x7E,0xB7,0xEF,0xA2,0x3D,0xB2,0xDF,0x07,0xFF,0x32,0x1B,0xF4,0xD5,
0x3F,0x05,0x29,0xCC,0x5E,0x79,0x06,0x51,0xEC,0x6D,0x39,0xCE,0x86,0x87,0x0F,0x50,0x88,0x81,0x75,0xFD, 0xC9,0x3F,0x91,0x08,0x5F,0x14,0x2E,0x27,0x0E,0xAA,0xA9,0xF5,0x0C,0x72,0x16,0x82,0x22,0xCD,0xEF,0x8C,
0xE4,0xF2,0xF2,0xFC,0xF2,0x5A,0x6F,0xE1,0x96,0x7E,0xE3,0x6A,0x7A,0x8B,0x9A,0x2E,0xB7,0x53,0x3C,0x49, 0xD3,0x91,0x66,0x5C,0x7D,0xDF,0xFD,0xE1,0xFA,0x8F,0xFE,0x95,0xD3,0xED,0x5F,0x9B,0xBF,0x99,0x3D,0x62,
0x31,0x9B,0x5D,0x20,0x3E,0x33,0x02,0xFB,0x0E,0x85,0x19,0xE8,0x42,0x30,0x12,0x7B,0xA0,0x38,0x7D,0x9C, 0x73,0x9C,0x71,0x83,0xA2,0x5B,0x32,0x43,0x9C,0xA5,0xB6,0xC8,0xB7,0xC3,0x19,0x48,0x61,0x0E,0xAA,0x33,
0x81,0xFD,0x50,0xDD,0xEC,0xC5,0x36,0xA1,0x14,0xA7,0xA7,0xC3,0xB7,0x6F,0x3C,0xFD,0x52,0x21,0x69,0x6F, 0x88,0x62,0x6F,0xCB,0x71,0x36,0x3C,0xBC,0x8F,0x22,0x0C,0xAC,0xEB,0x47,0xE7,0xE7,0xA7,0xE7,0x57,0x7A,
0x08,0xE3,0xBA,0x25,0x6F,0xC4,0x46,0x49,0x82,0x69,0x70,0x34,0x23,0x61,0x60,0xC4,0x66,0x4F,0x90,0xF0, 0x07,0x77,0xF4,0x6B,0x57,0xD3,0x3B,0xD4,0x74,0xB9,0x0D,0x45,0x67,0x8A,0xB3,0xF0,0x0C,0xF1,0xD0,0x08,
0x25,0x09,0x59,0x8C,0x00,0x05,0xDF,0x56,0x65,0xEB,0x04,0x24,0xD7,0x2D,0xDF,0x8E,0xB2,0x90,0x93,0x24, 0xEC,0x5B,0x14,0xE5,0xA0,0x0B,0xC1,0x08,0xF3,0x40,0x71,0xFA,0x24,0xE7,0x9C,0x51,0xDD,0x1C,0x30,0x9B,
0xC4,0xDE,0x46,0x17,0x66,0x14,0x45,0x00,0x13,0xA9,0x58,0xC0,0x48,0xE0,0xE9,0x2A,0x20,0xA9,0x30,0xB6, 0x50,0x8A,0xD3,0xE3,0xF1,0xDB,0x37,0x9E,0x7E,0xAE,0x90,0xB4,0x37,0x24,0xE3,0xBA,0x25,0x2D,0x62,0xA3,
0xF6,0x14,0x5F,0x9D,0x12,0x34,0x4E,0x09,0xEA,0xB8,0x09,0x48,0xA5,0x5B,0x81,0x5D,0xD5,0xCB,0x30,0xCB, 0x24,0xC1,0x34,0x38,0x0C,0x49,0x14,0x18,0xCC,0x1C,0x08,0x12,0xBE,0x24,0x21,0x8B,0x11,0xA0,0xE0,0xDB,
0x8F,0x2A,0x60,0x01,0x9E,0x20,0xE0,0xE4,0x3D,0x48,0x0E,0xAB,0x9D,0x75,0x07,0x05,0xEA,0x20,0xD6,0xD4, 0xAA,0x6C,0x9D,0x82,0xE4,0xBA,0xE5,0xDB,0x71,0x1E,0x71,0x92,0x44,0xD8,0xDB,0xE8,0xC3,0x8C,0xA2,0x18,
0x08,0xAB,0x6B,0x44,0x5D,0xE5,0x3A,0x64,0xA6,0x90,0xD3,0x26,0x72,0x5A,0x47,0x3E,0x92,0x16,0xB1,0x0E, 0x60,0x22,0x15,0x0B,0x18,0x09,0x3C,0x5D,0x05,0x24,0x15,0xC6,0xD6,0x9E,0xE2,0xAB,0x53,0x82,0xD6,0x29,
0x39,0x55,0xC8,0x61,0x43,0xC4,0x50,0x8A,0xA8,0xA2,0x5C,0x5B,0x68,0x13,0x04,0x02,0xEC,0xB0,0x2E,0x26, 0x41,0x13,0x37,0x01,0xA9,0x74,0x2B,0xB0,0xEB,0x7A,0x19,0x66,0xC5,0x51,0x25,0x2C,0xC0,0x53,0x04,0x9C,
0xCC,0xC0,0x29,0xD1,0x38,0xC4,0x81,0xB7,0xE1,0xC0,0x4C,0x14,0xD6,0xDE,0x96,0xB3,0xE6,0x8C,0x50,0x9D, 0xBC,0x17,0x92,0x43,0xF9,0xBD,0xEE,0xA0,0x40,0x1D,0x94,0xB5,0x35,0x92,0x35,0x35,0xA2,0x4C,0xB9,0x0E,
0x91,0x35,0xCE,0xC8,0xE4,0x19,0x24,0x01,0x25,0x42,0xEE,0xD0,0xAD,0x4C,0x6A,0xAD,0xB9,0xA2,0x8E,0xF3, 0x39,0x53,0xC8,0x69,0x1B,0x39,0x6D,0x22,0x1F,0xCA,0x1B,0xB1,0x0E,0x39,0x55,0xC8,0x51,0x4B,0xC4,0x48,
0x67,0xD8,0xBF,0x85,0xF2,0x7E,0x1D,0xFB,0x99,0x09,0xDB,0xE4,0x06,0xC1,0xC6,0x06,0x92,0x07,0x25,0xF2, 0x8A,0xA8,0xA2,0x5C,0x57,0x68,0x13,0x04,0x02,0xEC,0xA8,0x29,0x26,0xCC,0xC0,0x29,0xD1,0x24,0xC2,0x81,
0x20,0xD9,0xA3,0xC0,0x41,0x89,0x0D,0xEE,0xEF,0x89,0xE3,0xAC,0xA4,0xAE,0x93,0xF3,0x30,0xD0,0x08,0xD8, 0xB7,0xE1,0xC0,0x4C,0x14,0xD6,0xDE,0x96,0xB3,0xE6,0x8C,0x48,0x9D,0x91,0xB7,0xCE,0xC8,0xE5,0x19,0x24,
0xFF,0x3A,0xB2,0x89,0xE2,0x38,0x6A,0xAA,0x34,0xAA,0xA3,0x6B,0x03,0x74,0x87,0xD7,0x22,0x47,0xA6,0x95, 0x01,0x25,0x42,0xEE,0xD0,0xAD,0x5C,0x6A,0xAD,0xBD,0xA2,0x8E,0xF3,0x43,0xEC,0xDF,0x4C,0x98,0x5C,0x91,
0xDA,0x31,0xF5,0x43,0xE2,0xDF,0x7A,0x95,0x37,0x9B,0x0B,0xA3,0x3E,0x11,0xF4,0xB9,0x47,0xF1,0x5C,0xFB, 0x63,0x71,0xE2,0x06,0x92,0x34,0x13,0x49,0x53,0xB6,0x23,0x40,0x33,0xB1,0xC1,0xD3,0x3D,0x41,0xD9,0x4A,
0x09,0xA2,0xD3,0x31,0xE2,0xA8,0xC7,0x73,0x32,0x46,0x6E,0x3A,0x18,0x08,0x41,0x83,0x87,0x19,0x67,0x36, 0x9A,0xE2,0x6B,0xC3,0x88,0xDB,0xBA,0x44,0x88,0xDB,0x5A,0x8A,0x5B,0xDB,0x46,0xE8,0x16,0x6B,0xEB,0xB4,
0x0A,0x60,0xF9,0x62,0x34,0xD4,0x2D,0xBD,0x23,0x2E,0x46,0x07,0x87,0x26,0xE6,0xD2,0x2C,0x5D,0xCA,0xA2, 0x14,0x9B,0x6B,0x16,0x93,0x75,0x8B,0xB9,0x69,0xA5,0x36,0xA3,0x7E,0x44,0xFC,0x1B,0xAF,0x76,0x65,0x73,
0x32,0xB8,0x8F,0xD2,0xB0,0x5A,0x82,0x41,0x61,0x74,0x4B,0x2B,0x5A,0xCB,0x13,0x99,0x18,0xA5,0x02,0x0B, 0x61,0x34,0x27,0x82,0x13,0xEE,0x51,0x3C,0xD7,0x7E,0x82,0xD0,0xF4,0x1C,0x71,0x34,0xE0,0x05,0x19,0xA3,
0xA6,0x90,0x8F,0x6D,0x71,0x86,0x91,0x9B,0x80,0x6E,0x42,0xF4,0x53,0xF6,0x6B,0x98,0x16,0x6A,0x32,0x8D, 0xB8,0x37,0x18,0x08,0x41,0x47,0x0A,0x9E,0x98,0xD9,0x28,0x80,0xE5,0xB3,0x8B,0xB1,0x6E,0xE9,0x3D,0x61,
0x4A,0xA6,0x53,0x34,0x2F,0xEC,0x44,0x9D,0xDF,0x53,0x5E,0xEE,0x40,0x12,0xA6,0x8C,0x6B,0xD8,0xDB,0x71, 0x15,0x1D,0xBC,0x99,0x98,0x4B,0xB3,0xF2,0x27,0x8B,0xCA,0xC8,0x7E,0x91,0x46,0xF5,0x12,0x0C,0xCA,0x1B,
0x7E,0xD8,0xED,0xC1,0x6D,0x18,0xCA,0x77,0x01,0xB0,0xC7,0x6D,0xB0,0xAE,0x29,0x9F,0xF5,0xFC,0x96,0x97, 0xB7,0xB4,0xE2,0xB5,0x3C,0x91,0xA9,0x51,0xA9,0xB4,0x64,0x4A,0x74,0x87,0xE2,0x0C,0xA3,0xB0,0xBF,0x6E,
0x6B,0x25,0xF0,0xB8,0xCD,0xB2,0x31,0xE3,0x29,0xA4,0x38,0xC3,0xB7,0xFC,0x16,0x36,0x9B,0xC7,0xE8,0xAD, 0x42,0xE8,0x53,0x97,0xD7,0x30,0x2D,0xD4,0x66,0x1A,0x55,0x4C,0xA7,0x68,0x5E,0x5E,0x12,0x75,0xFE,0x40,
0xD8,0x0A,0x4C,0x2B,0x6E,0xB5,0x96,0x2B,0x0A,0x3A,0x1F,0xD4,0x34,0x84,0x84,0x86,0x70,0xC8,0xB0,0x46, 0xB9,0xB8,0x03,0x19,0x98,0x66,0x5C,0xC3,0xDE,0xB6,0xF3,0xC3,0xCE,0x00,0xEC,0x63,0x28,0xC7,0x05,0xC0,
0x6D,0x7C,0x8F,0xFD,0xA3,0x38,0x8A,0x90,0x20,0xC1,0xE0,0xEA,0xF2,0x89,0x6E,0x2E,0xAD,0x78,0xAD,0x66, 0x2E,0xB7,0x23,0x4C,0x67,0x3C,0x1C,0xF8,0x1D,0xAF,0xD0,0x4A,0xE0,0x71,0x3B,0xCB,0x27,0x19,0x4F,0x21,
0x9E,0x88,0x5C,0x16,0x7B,0x4A,0x8F,0x10,0x09,0x3D,0xDF,0x16,0xEE,0xC2,0x72,0xA1,0xBE,0x7C,0xCB,0x45, 0xBF,0x19,0xBE,0xE5,0x77,0xB0,0xD9,0x3E,0x46,0xEF,0x30,0x2B,0x30,0x2D,0xD6,0xE9,0x2C,0x57,0x14,0x74,
0x2C,0x52,0x28,0xD7,0xCE,0x8D,0x55,0x5E,0xDC,0x7A,0xA9,0xAA,0x7B,0x17,0x56,0xD7,0x08,0x41,0x66,0x75, 0x3A,0x6A,0x68,0x08,0x09,0x0D,0xE1,0x28,0xC3,0x1A,0xB5,0xF1,0x1D,0xF6,0x0F,0x59,0x1C,0x23,0x41,0x22,
0xD3,0x02,0xD6,0x0C,0x6D,0x15,0x54,0x5F,0x2E,0x2D,0x5F,0xF0,0x3F,0x43,0x74,0x8A,0xFF,0xBE,0x00,0x15, 0x03,0x23,0x17,0x13,0xDD,0x5C,0x5A,0x6C,0xAD,0x66,0x1E,0x09,0x5B,0x56,0xF6,0x98,0x1E,0x21,0x0C,0x7A,
0x8F,0xD2,0x4B,0x2D,0x0A,0x39,0xE4,0xC0,0xFD,0xC5,0x36,0xAE,0x7F,0xB5,0x6F,0x5A,0xA6,0x79,0xF0,0x75, 0xBE,0x2D,0x7C,0x25,0x2B,0x84,0xFA,0xB2,0x95,0xCB,0x40,0xA4,0x50,0xAE,0x9C,0x6B,0xAB,0x32,0xDC,0x5A,
0x47,0x6A,0xD8,0xE0,0xE6,0x75,0xF7,0xC6,0x42,0x00,0xB6,0xBF,0x33,0x7F,0xB1,0x25,0xB8,0x01,0xEB,0xDD, 0xA9,0x6A,0xBB,0xCB,0x5B,0xD7,0x8A,0x3F,0x66,0x6D,0x69,0x01,0x6B,0xC7,0xB5,0x1A,0xAA,0x2F,0x97,0x96,
0xC5,0x24,0xD0,0xC4,0x49,0xC2,0xB9,0xE3,0x89,0x86,0x36,0x37,0x0D,0x30,0xB9,0x86,0xB1,0xB6,0x78,0x4B, 0x2F,0xF8,0x0F,0x11,0x9D,0xE1,0xBF,0x2F,0x40,0xCD,0xA3,0x74,0x51,0x8B,0x42,0x02,0xD9,0x77,0x7F,0xB1,
0xB7,0x21,0x7D,0x2C,0x97,0xAB,0x49,0x6D,0x98,0x62,0xAC,0xB2,0x66,0x09,0x90,0xDC,0x83,0xCC,0xAB,0xF1, 0x8D,0xAB,0x5F,0xED,0xEB,0x8E,0x69,0xEE,0x7F,0xDD,0x93,0x1A,0x36,0xB8,0x79,0xD5,0xBF,0xB6,0x10,0x80,
0xAA,0x94,0x1A,0x5B,0x15,0x14,0x16,0x65,0xD6,0xB7,0xF3,0x82,0xC2,0xD3,0x45,0x45,0x01,0xB1,0x66,0x75, 0xED,0xEF,0xCC,0x5F,0x6C,0x09,0x6E,0xC1,0x06,0xB7,0x8C,0x04,0x9A,0x38,0x49,0x78,0x36,0x9B,0x6A,0x68,
0x59,0x76,0x7A,0xB0,0x5E,0x73,0xF9,0x6F,0xF7,0x48,0x34,0xD5,0x58,0xEA,0x7B,0xEA,0x2A,0x0E,0xC4,0x0F, 0x73,0xD3,0xE0,0x1E,0x6A,0x5D,0xD6,0x0E,0xEF,0xE8,0x36,0xE4,0x8E,0xE5,0x72,0x35,0xA3,0x8D,0x53,0x8C,
0xEF,0x5B,0x91,0xED,0x36,0x7F,0xF3,0xC7,0x9E,0xDE,0x82,0xFB,0xC5,0x36,0x8D,0xE7,0x86,0xD9,0xFA,0x56, 0x55,0xCA,0xAC,0x00,0x92,0x7B,0x90,0x79,0x35,0x58,0x55,0x52,0x63,0xAB,0x86,0xC2,0xA2,0x4C,0xF9,0x76,
0xD7,0x24,0x45,0x4F,0x8F,0xD0,0x7D,0xDE,0xEF,0x8B,0x82,0xA4,0xA7,0xC1,0xBC,0x6C,0x50,0xD5,0x82,0xEA, 0x51,0x4D,0x78,0xBA,0x28,0x27,0x20,0xAC,0xAC,0x2E,0xCB,0x36,0x0F,0xD6,0x1B,0xC1,0xE1,0xDB,0x5D,0x12,
0xB4,0x51,0xC6,0xE3,0x9E,0x56,0x14,0x3A,0xAA,0xD3,0xD4,0xB5,0xCE,0xFE,0xB7,0x95,0x0E,0x10,0x88,0x8E, 0xCF,0xB4,0x2C,0xF5,0x3D,0x65,0x8A,0x7D,0xF1,0x8F,0xF7,0xAD,0x48,0x75,0x9B,0xBF,0xF9,0x13,0x4F,0xEF,
0xD4,0x9D,0x10,0x19,0x9A,0x32,0x11,0xE0,0x70,0x23,0xFA,0x90,0xDC,0xED,0x54,0x0C,0x24,0x00,0x27,0x2B, 0x80,0x7D,0xB1,0x4D,0xD9,0xDC,0x30,0x3B,0xDF,0xEA,0x9A,0xA4,0xE8,0xE9,0x31,0xBA,0x2B,0x9A,0x7D,0x51,
0x99,0xD3,0x0A,0x0C,0x64,0x1E,0x18,0x8D,0x7C,0xBB,0x27,0x7A,0x93,0xFD,0x0B,0x55,0x74,0xEE,0x75,0xE4, 0x8D,0x0C,0x34,0x98,0x57,0xDD,0xA9,0x5A,0x50,0x6D,0x36,0xCA,0x39,0x1B,0x68,0x65,0x95,0xA3,0xDA,0x4C,
0x4C,0x5F,0xE7,0x18,0xA0,0x79,0x0A,0xE8,0x56,0x59,0xE6,0x8C,0xE3,0xE0,0xA1,0x56,0x1C,0xB1,0xC3,0x87, 0x5D,0xEB,0xED,0x7D,0x5B,0xEB,0x00,0x81,0xE8,0x48,0xD9,0x84,0xC8,0x20,0x96,0x8B,0x90,0x87,0x5B,0xD1,
0xA3,0x10,0x31,0x76,0x86,0x22,0x38,0xDF,0x8F,0x74,0x33,0x37,0xA5,0x7D,0x67,0x73,0xB3,0x89,0x94,0xE2, 0x87,0x14,0x6E,0xA7,0xA2,0x22,0x01,0x38,0x59,0x49,0x9B,0x56,0x60,0x20,0x73,0xDF,0x68,0x25,0xDB,0x5D,
0x08,0x7A,0x21,0xC5,0x15,0x86,0x50,0xE6,0xFA,0x40,0x78,0x73,0x73,0x1D,0x63,0xE2,0x8D,0xEA,0x4B,0x5C, 0xD1,0x98,0xEC,0x9D,0xA9,0x8A,0x73,0xB7,0x27,0x67,0xFA,0x3A,0xC7,0x00,0xCD,0x53,0x40,0xB7,0xAA,0x1A,
0x51,0x70,0xD7,0x22,0xFC,0xFD,0x4F,0xDC,0xA9,0x1C,0xFB,0x94,0x56,0x53,0xF0,0xD9,0xC7,0x6C,0x1F,0xC7, 0x67,0xC2,0x82,0xFB,0x46,0x65,0x94,0x1D,0xDC,0x1F,0x46,0x28,0xCB,0x4E,0x50,0x0C,0xE7,0xFB,0xB1,0x6E,
0x73,0x2A,0xB8,0x2A,0x59,0x4F,0xD7,0x28,0xB4,0xCA,0x0B,0xA5,0x25,0x07,0x39,0x5E,0x7B,0x92,0x2A,0x43, 0x16,0x57,0x69,0xCF,0xD9,0xDC,0x6C,0x23,0xA5,0x38,0x86,0x46,0x48,0x71,0x85,0x21,0x94,0xB9,0x3E,0x10,
0xAE,0x19,0x5D,0x01,0x03,0x73,0xC3,0x4B,0xF3,0xFF,0x11,0x56,0x95,0x04,0x4F,0xC9,0x1A,0x9A,0x56,0xB8, 0xDE,0xDC,0x5C,0xC7,0x98,0x78,0x54,0xFB,0x12,0x57,0x14,0xDC,0xB5,0x0C,0x7F,0xFF,0x13,0x77,0x2A,0xC1,
0x46,0x56,0x1C,0x62,0x8E,0x4B,0x49,0xC3,0xB5,0x92,0xB2,0xFF,0x87,0xDF,0xCA,0x43,0x88,0xAA,0x74,0xEB, 0x3E,0xA6,0xD5,0x14,0x7C,0xF6,0x21,0xDB,0xCF,0xD9,0x9C,0x0A,0xAE,0x2A,0xD6,0xD3,0x35,0x0A,0xAD,0xF3,
0x3E,0x12,0x90,0x3B,0x1D,0x72,0x85,0xD7,0xA4,0xC0,0xFC,0x34,0x0E,0xC3,0x61,0x9C,0x1C,0x3C,0xB1,0xEE, 0x42,0x75,0x93,0x83,0x02,0xAF,0x3B,0x4D,0xD5,0x45,0x6E,0x5C,0xBA,0x12,0x06,0xD7,0x0D,0x2F,0xCD,0xFF,
0x96,0xEB,0xF9,0xA0,0x60,0xB3,0xDA,0x62,0xF9,0x6B,0x89,0xBE,0xC1,0x13,0x7E,0xF0,0x14,0xE0,0x69,0xB2, 0x47,0x58,0x55,0x0F,0x3C,0x26,0x6B,0x64,0x5A,0xD1,0x1A,0x59,0x71,0x84,0x39,0xAE,0x24,0x8D,0xD6,0x4A,
0xD5,0x1E,0x2B,0xF0,0xB0,0x0D,0xCA,0x83,0xD5,0x9F,0x5B,0xBE,0xC5,0xCA,0xD9,0x55,0x2B,0x86,0xBB,0xF0, 0x9A,0xFD,0x3F,0xFC,0xD6,0x1E,0x42,0x54,0x99,0xDB,0xF4,0x91,0x80,0xDC,0xEA,0x90,0x2B,0xBC,0x36,0x05,
0x0B,0x25,0x79,0x42,0x49,0x16,0x59,0x1B,0xBA,0xAA,0x75,0xD1,0x5E,0x7A,0x41,0x4B,0x4F,0xEE,0xAB,0x35, 0xA8,0xF8,0x59,0x14,0x8D,0x59,0xB2,0xFF,0xC8,0xBA,0x5B,0xAD,0x17,0x83,0x92,0xCD,0x7A,0x8B,0xE5,0xAF,
0x1E,0x27,0x1E,0x53,0x4B,0xC8,0x20,0x16,0x5F,0xB9,0x94,0x75,0x01,0x25,0xF5,0x88,0x1D,0x4F,0x26,0x0C, 0x25,0xFA,0x06,0x4F,0xF9,0xFE,0x63,0x80,0xC7,0xC9,0xD6,0x7B,0xAC,0xC0,0xC3,0x36,0x28,0x0F,0x56,0x7F,
0xF3,0x0F,0x22,0x98,0x59,0x61,0x39,0x3F,0x95,0xC1,0xAC,0x07,0x53,0x1A,0xC5,0x19,0xC3,0x71,0xC6,0x9B, 0xEE,0xF8,0x56,0x56,0xCD,0x2E,0x3B,0x0C,0x6C,0xE1,0x97,0x4A,0xF2,0x84,0x92,0x2C,0xB2,0x36,0x74,0xD5,
0x65,0x4F,0x29,0xCC,0x5E,0xF0,0xC7,0x1F,0xE5,0x64,0x3F,0x68,0xA5,0xD5,0xF4,0x6A,0x8F,0xD5,0x26,0xFB, 0xEB,0xA2,0xB7,0xF4,0x82,0x8E,0x9E,0xDC,0xD5,0x6B,0x9C,0x25,0x5E,0xA6,0x96,0x90,0x41,0x2C,0xBE,0x62,
0xAC,0x15,0x42,0x28,0xF8,0x8F,0x2D,0x85,0xD4,0x2D,0x25,0x16,0xB1,0xD4,0x8A,0x95,0xA5,0xB0,0xCA,0xD6, 0x94,0x75,0x01,0x25,0xF5,0x88,0xCD,0xA6,0xD3,0x0C,0xF3,0x0F,0x22,0x98,0x59,0x51,0x35,0x3F,0x96,0xC1,
0x99,0xA8,0x4B,0x51,0xCD,0xDB,0x85,0x41,0xEB,0x66,0xD1,0x76,0xD5,0xBD,0x1C,0x59,0x6C,0x35,0x08,0xAC, 0x6C,0x00,0x53,0x1A,0xB3,0x3C,0xC3,0x2C,0xE7,0xED,0xB2,0xA7,0x12,0x66,0x37,0xF8,0xE3,0x8F,0x6A,0xB2,
0x2F,0x0E,0x7C,0x43,0x50,0x05,0xAD,0xBF,0x89,0xE7,0x38,0x3D,0x42,0x0C,0x1B,0x90,0x33,0xCB,0x88,0x25, 0x17,0x74,0xD2,0x7A,0x7A,0xB9,0x9B,0x35,0x26,0x7B,0x59,0x27,0x82,0x50,0xF0,0x1F,0xDF,0x14,0xD2,0xBC,
0xA1,0xA6,0x1B,0xAC,0xD9,0x05,0x4A,0xA0,0x0A,0x9C,0x17,0x1E,0x31,0x15,0xD5,0x37,0xC8,0x97,0x35,0x4E, 0x29,0x4C,0xC4,0x52,0x8B,0xA9,0x9B,0x92,0xD5,0x77,0x3D,0x13,0x45,0x29,0x6A,0x78,0xBB,0xB8,0xD0,0xBA,
0xC0,0xB6,0x78,0x30,0x00,0xA9,0x8F,0x55,0xB3,0x01,0x75,0x1A,0xB6,0x19,0x5C,0xF3,0x45,0x1A,0x27,0x68, 0x59,0xF6,0x5C,0x4D,0x2F,0x47,0x56,0xB6,0x1A,0x04,0xD6,0x17,0x07,0xBE,0x21,0xA8,0x82,0xD6,0xDF,0xB0,
0x2A,0x9B,0x6B,0x58,0x13,0xEE,0x51,0x50,0xAB,0xA5,0xD6,0xB2,0x1E,0x95,0x69,0xFD,0x71,0x56,0xC7,0x32, 0x39,0x4E,0x0F,0x51,0x86,0x0D,0xC8,0x99,0x55,0xC4,0x92,0x50,0xD3,0x0D,0xD6,0xEC,0x02,0x25,0x50,0x05,
0x73,0x43,0x85,0xB0,0x9A,0xBC,0xB9,0xC9,0xE6,0x84,0xFB,0x33,0xE1,0xDB,0x3E,0x30,0xAC,0x73,0xE8,0x0C, 0x2E,0x0A,0x0F,0x46,0x45,0xE9,0x0D,0xF2,0xE5,0xAD,0x13,0xB0,0x2D,0x5E,0x0B,0x40,0xEA,0xE7,0xAA,0xD3,
0x5C,0x39,0x9A,0xF1,0xA8,0x1A,0x85,0xF9,0xF0,0x13,0xCB,0x07,0x3E,0x2B,0x46,0xF7,0x35,0x60,0x4C,0x0B, 0x80,0x3A,0x0D,0x83,0xAD,0x59,0x72,0x96,0xB2,0x04,0xCD,0x64,0x67,0x0D,0x6B,0xC2,0x3D,0x4A,0x6A,0x8D,
0x70,0x4C,0x27,0xF9,0x90,0x50,0x52,0xD0,0x29,0x80,0xC5,0x67,0x92,0xE4,0xA3,0x64,0x56,0x8C,0x66,0xF8, 0xD4,0x5A,0xD5,0xA3,0x32,0xAD,0x3F,0xCC,0xEA,0x58,0x66,0x6E,0xA8,0x10,0x56,0x93,0x37,0x37,0xB3,0x39,
0xBE,0xC4,0x8B,0x0B,0x68,0x80,0x75,0x57,0x5D,0xDF,0x86,0xB3,0xCC,0x07,0xDD,0x4A,0xFC,0xE0,0xBF,0x13, 0xE1,0x7E,0x28,0x7C,0xDB,0x07,0x86,0x75,0x0E,0x6D,0x81,0x2B,0x47,0x21,0x8F,0xEB,0x51,0x54,0x0C,0x3F,
0x3F,0xA1,0xD3,0x42,0x98,0xA4,0x18,0x4D,0x49,0x21,0xCA,0x38,0x4A,0xBE,0xC8,0x08,0xFB,0xB7,0x7D,0xC1, 0x65,0xC5,0xC0,0xCF,0xCA,0xD1,0x5D,0x03,0xC8,0x68,0x09,0x66,0x74,0x5A,0x0C,0x09,0x25,0x25,0x9D,0x12,
0xF1,0xC9,0x9B,0x93,0xE1,0x49,0xBD,0x44,0xAC,0xBF,0x22,0xF0,0xBF,0x6A,0xD3,0xB9,0xE9,0x1A,0x49,0xDD, 0x58,0x7E,0x26,0x49,0x31,0x4A,0xC2,0x72,0x14,0xE2,0xBB,0x0A,0x8F,0x95,0xD0,0x00,0xEB,0xAE,0x32,0xDF,
0x53,0x60,0xE6,0x8B,0xCF,0xB3,0x38,0x90,0x45,0x1F,0x04,0x7C,0x23,0x01,0xEA,0xBA,0x69,0x36,0x1E,0x1A, 0x86,0xB3,0x2C,0x06,0xFD,0x5A,0xFC,0xE0,0xBF,0x13,0x3F,0xA1,0xB3,0x52,0x98,0xA4,0x1C,0xCD,0x48,0x29,
0xD2,0xC6,0x03,0x46,0x15,0xE8,0xF3,0x97,0x01,0xD0,0xD3,0xE6,0x66,0x8D,0x11,0xB9,0x5E,0x3D,0x7E,0x50, 0xCA,0x24,0x4E,0xBE,0xC8,0x48,0xF6,0x6F,0xFB,0x82,0xE7,0x47,0x6F,0x8E,0xC6,0x47,0xCD,0x12,0xB1,0xF9,
0x4B,0x57,0x75,0x9A,0x74,0x36,0xF4,0x54,0x41,0x83,0xCC,0xB2,0x65,0x20,0x1E,0xCD,0x1D,0xDD,0x12,0xDD, 0x84,0xC0,0xFF,0xAA,0x47,0xE7,0xA6,0x6B,0x24,0x4D,0x4F,0x81,0x99,0x2F,0x3E,0x4F,0x58,0x20,0x8B,0x3E,
0x03,0xD9,0xF7,0xA1,0x6D,0x68,0x99,0xAA,0xE1,0xF7,0x3C,0x8F,0x5E,0xFB,0x37,0xB2,0x69,0xDC,0xDC,0x44, 0x08,0xF8,0x46,0x02,0xD4,0x75,0xD3,0x6C,0xBD,0x32,0xA4,0xAD,0xD7,0x8B,0x3A,0xD0,0x17,0xCF,0x02,0xA0,
0xCD,0xA2,0xC7,0x70,0x2C,0x09,0x14,0x07,0xCA,0x91,0x6C,0x57,0x41,0x16,0x60,0xCB,0xB1,0x5E,0x0D,0xCE, 0xA7,0xCD,0xCD,0x06,0x23,0x72,0xBD,0x7E,0xF9,0xA0,0x96,0xAE,0xEA,0x34,0xE9,0x6C,0xE8,0xB1,0x82,0x06,
0xCF,0xEC,0x04,0xA5,0xE0,0x71,0xD4,0xAC,0x07,0x89,0x50,0x09,0xD8,0xD4,0xF6,0x8B,0x93,0x5A,0x35,0x2E, 0x99,0x55,0xCB,0x40,0x3C,0x5A,0x38,0xBA,0x25,0xBA,0x07,0xB2,0xE7,0x43,0xDB,0xD0,0x31,0x55,0xB7,0xEF,
0xBF,0x17,0x72,0xF9,0xD2,0x4A,0x0D,0x41,0x4E,0xF5,0xB9,0x22,0x87,0xE7,0x0F,0x78,0x90,0x7A,0x92,0x22, 0x79,0x1E,0xBD,0xF2,0xAF,0x65,0xC7,0xB8,0xB9,0x89,0xDA,0x45,0x8F,0xE1,0x58,0x12,0x28,0x0E,0x94,0x23,
0x0D,0x55,0x51,0x24,0xA9,0x07,0x71,0x7E,0x97,0xAD,0xEF,0x41,0x2D,0x3E,0x23,0xAC,0xDE,0x85,0x34,0x5C, 0xD9,0xAB,0x82,0x2C,0xC0,0x96,0x63,0xBD,0x1A,0x9D,0x9E,0xD8,0x09,0x4A,0xC1,0xE3,0xA8,0xD9,0x0C,0x12,
0xFE,0x6F,0xDE,0xD8,0xB2,0x18,0x29,0x72,0xAB,0x05,0xB5,0xFA,0xF6,0x4F,0x5D,0x8E,0x6C,0x98,0x16,0x55, 0x91,0x12,0xB0,0xAD,0xED,0x17,0x47,0x8D,0x6A,0x5C,0x7E,0x29,0xE4,0xF2,0xA5,0x95,0x1A,0x82,0x9C,0x6A,
0x84,0xAC,0xCC,0x51,0x87,0xCC,0x42,0xA8,0x6E,0xD1,0x7F,0xEA,0x21,0xB4,0xF0,0x10,0xFA,0x38,0x40,0xE0, 0x72,0x45,0x0E,0x2F,0x5E,0xEF,0x20,0xF5,0x24,0x65,0x1A,0xAA,0xA3,0x48,0xD2,0x0C,0xE2,0xFC,0x36,0xD7,
0xFB,0x86,0xB7,0x97,0xA7,0xF4,0xC6,0xC0,0xD9,0x6D,0xAF,0x8C,0x22,0x00,0x91,0x31,0xA4,0x01,0xF8,0xC4, 0xD7,0xF7,0x96,0x3C,0x24,0x59,0xB3,0x0B,0x69,0xB9,0xFC,0xDF,0xB4,0xD8,0xB2,0x1C,0x29,0x72,0xAB,0x05,
0xE4,0xFA,0x27,0x74,0x87,0xD4,0xFB,0x5D,0x05,0x5D,0x1F,0x2A,0xE4,0x76,0xFF,0x37,0x18,0x95,0x3B,0x9B, 0xB5,0xFA,0xBA,0x52,0x19,0x47,0x36,0x4C,0x75,0x61,0xCD,0xEA,0xEB,0xA8,0x43,0x66,0x21,0x54,0xB7,0xE8,
0x81,0x88,0xC1,0xF0,0x71,0x3C,0xE1,0x8D,0xF0,0x54,0xC5,0xAC,0x7A,0x74,0x02,0x77,0xCD,0xBD,0x59,0xE3, 0x3F,0xF5,0x10,0x5A,0x7A,0x08,0x7D,0x18,0x20,0xF0,0x5D,0xCB,0xDB,0xAB,0x53,0x06,0x13,0xE0,0xEC,0x66,
0x35,0xFD,0xD6,0x5C,0x0F,0x0C,0xF2,0x49,0xDF,0xAB,0x05,0x22,0x85,0x51,0xB7,0x9E,0xF5,0xED,0x4A,0xB3, 0x50,0x45,0x11,0x80,0xC8,0x18,0xD2,0x02,0x7C,0xCA,0xE4,0xFA,0x27,0x74,0x8B,0xD4,0xE3,0x5D,0x0D,0x5D,
0xA1,0x59,0x9F,0xFE,0xA5,0xFF,0xE1,0x03,0x66,0xB3,0xA2,0x75,0x07,0x37,0xAF,0xCD,0x74,0x5D,0x64,0x2B, 0x1F,0x2A,0xE4,0x76,0xFF,0x37,0x18,0x55,0x3B,0xDB,0x81,0x28,0x83,0xE1,0xC3,0x78,0xC2,0x5B,0xE1,0xA9,
0x3F,0xC4,0x28,0x1D,0x88,0x2E,0x51,0x65,0x88,0xA5,0xBA,0x40,0x4F,0xBA,0xAE,0x01,0x1A,0xEB,0xC8,0x87, 0x8E,0x59,0xCD,0xE8,0x04,0xEE,0xBA,0x2C,0x0C,0xCB,0x1B,0xFA,0x6D,0xBA,0xDE,0xE6,0xE6,0xA3,0xBE,0xD7,
0x62,0x5B,0xE8,0x01,0x10,0x4A,0x28,0x05,0x28,0xF5,0x62,0xA0,0x59,0x5B,0x44,0xB0,0x88,0xA0,0xE1,0x89, 0x08,0x44,0x0A,0xA3,0x79,0x7B,0xD6,0xB7,0x2B,0xED,0x86,0x66,0x7D,0xFA,0x97,0xFE,0x87,0xF7,0x33,0x3B,
0x69,0x7C,0x8B,0x48,0x7D,0x3B,0x01,0x08,0xC9,0xBF,0x4F,0x86,0xBE,0xCE,0xCA,0x2F,0x42,0x12,0xAA,0x20, 0x2B,0x5B,0x77,0x70,0xF3,0xC6,0x4C,0xD7,0x45,0xB6,0xF2,0x23,0x8C,0xD2,0x91,0xE8,0x12,0x55,0x86,0x58,
0xEA,0xFA,0xCD,0xFC,0x2D,0xAC,0x7A,0x86,0xC0,0x85,0xDF,0xE4,0x06,0xB2,0x21,0xD1,0x98,0x28,0x08,0x06, 0x2A,0x03,0x7A,0xD2,0x75,0x0D,0xD0,0x58,0x4F,0xBE,0x12,0xDB,0x42,0x0F,0x80,0x50,0x41,0x29,0x40,0xA9,
0x98,0x31,0xC9,0xB8,0x10,0xEC,0x6D,0x1C,0x80,0x5C,0x80,0xD6,0x89,0x60,0x24,0x0E,0x02,0x11,0x05,0x60, 0xC7,0x80,0x66,0x63,0x11,0xC1,0x22,0x82,0x86,0x87,0x51,0x76,0x83,0x48,0x73,0x3B,0x01,0x08,0x29,0xBE,
0x38,0xC3,0x51,0x0E,0xE1,0x30,0x14,0x20,0x24,0x40,0x5F,0x4B,0x5D,0x0D,0x64,0xE1,0x45,0xE8,0xD4,0xEB, 0x4C,0x86,0xBE,0xCE,0x2A,0x0C,0x21,0x09,0xD5,0x10,0x65,0x7E,0xB3,0x78,0x08,0xAB,0x9F,0x21,0x70,0xE9,
0x76,0x1C,0xEB,0x31,0xD9,0x11,0xC3,0x83,0x78,0xC2,0x87,0x68,0xCC,0x8C,0x0D,0xC7,0x5C,0xB3,0x03,0x40, 0x37,0xC5,0x05,0xD9,0x90,0x68,0x99,0x28,0x08,0x46,0x38,0xCB,0x24,0xE3,0x42,0xB0,0xB7,0x2C,0x00,0xB9,
0x03,0x08,0x27,0xC6,0x96,0xB9,0x1E,0xFD,0x43,0x9C,0xDE,0xE2,0xD4,0xD8,0xE8,0xE6,0xFC,0x9C,0x42,0xC1, 0x00,0xAD,0x17,0xC3,0x48,0x1C,0x04,0x22,0x0A,0xC0,0x38,0xC4,0x71,0x01,0xE1,0x30,0x14,0x20,0x24,0x40,
0x14,0xC2,0x7F,0xDE,0x07,0xB5,0xDF,0xE1,0x37,0x84,0xE2,0x9C,0x32,0x03,0xE4,0x59,0x3C,0xBF,0x48,0x09, 0x5F,0x4B,0x5D,0x8D,0x64,0xE1,0x45,0xE8,0xCC,0xEB,0xF7,0x1C,0xEB,0x21,0xD9,0x8B,0x0C,0x8F,0xD8,0x94,
0xE5,0x6F,0x65,0x5F,0x98,0x63,0xF9,0xEA,0xB9,0x42,0x86,0xA0,0xE2,0x1D,0x63,0x21,0xA2,0x99,0xDB,0x78, 0x8F,0xD1,0x24,0x33,0x36,0x1C,0x73,0xCD,0x0E,0x00,0x8D,0x20,0x9C,0x18,0x5B,0xE6,0x7A,0xF4,0x0F,0x2C,
0xCD,0xB0,0xC6,0x70,0x5F,0x10,0x5C,0xDD,0xC5,0x9C,0x50,0x57,0x3F,0xE2,0x69,0xD8,0x1E,0xE8,0x56,0x84, 0xBD,0xC1,0xA9,0xB1,0xD1,0x2F,0xF8,0x39,0x86,0x82,0x29,0x82,0xBF,0x7C,0x08,0x6A,0xBF,0xC5,0x6F,0x08,
0x7C,0x98,0xA8,0x3D,0x30,0x5F,0x5A,0xC2,0x6B,0xDD,0x7A,0x34,0xD9,0xA8,0x26,0x55,0x01,0xBD,0xF6,0xA9, 0xC5,0x05,0xE5,0x0C,0x90,0x43,0x36,0x3F,0x4B,0x09,0xE5,0x6F,0x65,0x5F,0x58,0x60,0xF9,0xEA,0xB9,0x42,
0xA7,0x7A,0xB9,0x10,0xC0,0xC3,0x30,0x1E,0x1B,0xE2,0x75,0x7A,0x21,0x3C,0xDE,0xA5,0x4B,0x53,0xA4,0xA7, 0x86,0xA0,0xF2,0x1D,0x63,0x21,0xA2,0x99,0xDB,0x7A,0xCD,0xB0,0x26,0x60,0x2F,0x08,0xAE,0xEE,0x62,0x4E,
0x2F,0xBF,0xCA,0xF8,0x10,0x76,0xB9,0x85,0x6B,0x8F,0x4A,0x2D,0x5D,0x17,0x6F,0x35,0x80,0x87,0x82,0x73, 0xA8,0xAB,0x1F,0xF2,0x34,0xEA,0x8E,0x74,0x2B,0x46,0x3E,0x4C,0xD4,0x1E,0x98,0x2F,0x2D,0xE1,0xB5,0x6E,
0x1A,0x3E,0xB8,0x90,0x16,0xFF,0x42,0xEA,0x8C,0x06,0xF1,0x97,0xA4,0xFE,0xB8,0x22,0xF5,0xC7,0x75,0x52, 0x33,0x9A,0x6C,0xD4,0x93,0xBA,0x80,0x5E,0xFB,0xD4,0x53,0xBF,0x5C,0x08,0xE0,0x41,0xC4,0x26,0x86,0x78,
0xE3,0xE6,0x6D,0xC1,0x64,0x04,0x64,0xDF,0x22,0x8A,0xA6,0x38,0x85,0x05,0x71,0x08,0x5C,0xC0,0x3F,0xE3, 0x9A,0x5E,0x08,0x8F,0x77,0xE9,0xD2,0x14,0xE9,0xE9,0xCB,0xAF,0x32,0x3E,0x84,0x5D,0x6E,0xE1,0xC6,0xA3,
0x2C,0xC5,0x5F,0xE6,0x6C,0x30,0x23,0x13,0xBE,0xCA,0x9F,0x5A,0xFD,0x77,0x5C,0xA6,0xF8,0x09,0x2E,0x8B, 0x52,0x47,0xD7,0xC5,0x5B,0x0D,0xE0,0xA1,0xE0,0x94,0x46,0xF7,0x2E,0xA4,0xC5,0xBF,0x90,0x3A,0xA7,0x01,
0x02,0xB3,0xCC,0x1B,0x7F,0xFD,0x54,0x22,0x9D,0xDA,0x83,0x2B,0xAC,0xBB,0xEB,0xBF,0xF0,0xBB,0xEA,0xC4, 0xFB,0x92,0xD4,0x1F,0x57,0xA4,0xFE,0xB8,0x4E,0x6A,0xDC,0xB6,0x16,0x4C,0x2E,0x80,0xEC,0x5B,0x44,0xD1,
0x2F,0xA7,0x4F,0xF1,0xE1,0xE2,0xA5,0x15,0x08,0x9B,0x68,0x96,0xA5,0x31,0x3D,0x84,0x0A,0xFE,0x0D,0x88, 0x0C,0xA7,0xB0,0x20,0x0E,0x01,0x03,0xFC,0x33,0xCE,0x52,0xFC,0x65,0xCE,0x46,0x21,0x99,0xF2,0x55,0xFE,
0x60,0x28,0x73,0xC4,0xDE,0x62,0x69,0x71,0xCF,0x00,0x55,0x42,0x17,0x0C,0xC2,0xF9,0xB2,0xAE,0xB5,0x67, 0xD4,0xEA,0xBF,0xE3,0x32,0xC5,0x8F,0x70,0x59,0x16,0x98,0x55,0xDE,0xF8,0xEB,0xA7,0x12,0xE9,0xD4,0x1E,
0x90,0x22,0x41,0x09,0xC0,0x24,0x24,0xDA,0xCE,0xF5,0xC1,0xE6,0x4D,0x0B,0x32,0x94,0x07,0x1F,0xA6,0x07, 0x98,0xB0,0xE9,0xAE,0xFF,0xC2,0xEF,0xEA,0x13,0xBF,0x9C,0x3E,0xC5,0x87,0x8B,0x97,0x56,0x20,0xEE,0x44,
0x83,0xCD,0x9B,0xEF,0xCC,0xCE,0x94,0xD4,0x6A,0x22,0x99,0xEB,0x40,0xAF,0xD7,0xF4,0xC6,0x43,0x4B,0x08, 0xBB,0x2C,0x65,0xF4,0x00,0x2A,0xF8,0x37,0x20,0x82,0xA1,0xAE,0x23,0xF6,0x16,0x4B,0x8B,0x7B,0x06,0xA8,
0x61,0xF5,0x5C,0x58,0x46,0x56,0x0B,0xCB,0x57,0x2F,0xF8,0x08,0x11,0x9D,0xC2,0x87,0x8C,0x1D,0x10,0x9E, 0x12,0xBA,0x60,0x10,0xCE,0x97,0x75,0xAD,0x1D,0x42,0x8A,0x04,0x25,0x00,0x93,0x90,0x68,0x7B,0x57,0xFB,
0xD6,0x7C,0xBF,0xA2,0x67,0x6A,0x04,0x58,0xB5,0x87,0x2A,0x9D,0xC3,0x4F,0x5D,0xB4,0x57,0xBC,0x16,0x0C, 0x9B,0xD7,0x1D,0xC8,0x50,0x1E,0x7C,0x98,0x1E,0x0C,0x36,0xAF,0xBF,0x33,0x7B,0x33,0xD2,0xA8,0x89,0x64,
0x15,0x59,0x88,0x7B,0x6A,0x50,0x8F,0xB1,0x22,0x49,0x17,0x97,0x96,0x83,0xCD,0xA5,0x30,0x75,0x3C,0x21, 0xAE,0x03,0xBD,0x5E,0xD1,0x6B,0x0F,0x2D,0x21,0x84,0x35,0x73,0x61,0x15,0x59,0x2D,0x2C,0x5F,0xBD,0xE0,
0x14,0x07,0x7A,0x99,0x57,0x7F,0x7E,0xFB,0xE6,0x94,0xF3,0xE4,0x52,0x69,0x16,0x68,0x35,0x17,0xAA,0x0B, 0x23,0x42,0x74,0x06,0x1F,0x32,0x76,0x40,0x78,0x5A,0xF3,0xE5,0x8A,0x9E,0xAB,0x11,0x60,0x35,0x1E,0xAA,
0x37,0x17,0x3C,0x7D,0x28,0x2A,0x34,0xE1,0xB0,0x2A,0xE4,0xFC,0x7C,0x3E,0xFE,0x84,0x7D,0xC8,0x47,0x6F, 0x74,0x0E,0xFF,0xEA,0xA2,0xBD,0xE2,0x8D,0x60,0xA8,0xC8,0x42,0xDC,0x53,0x83,0x66,0x8C,0x15,0x49,0xBA,
0x19,0x24,0xB3,0x2D,0x5B,0xA0,0x0F,0x87,0x17,0xF6,0xAE,0xED,0x40,0xC9,0xE0,0x23,0x48,0xD9,0xE2,0xE6, 0x34,0x5A,0x01,0x36,0x97,0x7A,0x0E,0xE0,0x29,0xA1,0x38,0xD0,0xAB,0xBC,0xFA,0xF3,0xDB,0x37,0xC7,0x9C,
0x96,0xFF,0x08,0x79,0xFB,0x9F,0x21,0x13,0x88,0xBB,0x0C,0x02,0x6B,0x81,0xDF,0xC4,0x9D,0xA5,0xF1,0x5C, 0x27,0xE7,0x4A,0xB3,0x40,0xAB,0xBD,0x50,0x1B,0xDC,0x5C,0xF0,0xF4,0xBE,0xAC,0xD0,0x84,0xC3,0xAA,0x90,
0xA2,0x9D,0xA4,0xA9,0xB8,0x94,0xE1,0x8C,0x30,0x6D,0x0C,0x8B,0x0C,0xA7,0x5A,0x10,0x63,0xA6,0xD1,0x98, 0xF3,0xF3,0xE9,0xE4,0x13,0xF6,0x21,0x1F,0xBD,0xCD,0x20,0x99,0x6D,0xD9,0x02,0x7D,0x3C,0x3E,0xB3,0x77,
0x6B,0x2C,0x4B,0x92,0x38,0xE5,0x2B,0xFA,0xB0,0xF5,0xE2,0xC1,0xE6,0x5D,0x86,0x33,0x1C,0xE4,0xCB,0x38, 0x6C,0x07,0x4A,0x06,0xD0,0xAB,0x1F,0x0A,0xCB,0x2D,0xFF,0x11,0xF2,0x93,0x7F,0x86,0x4C,0x20,0xEE,0x66,
0xAD,0xEB,0x45,0x16,0x57,0xB0,0x0E,0x66,0x7F,0x7D,0x93,0x97,0x5A,0x19,0xA5,0x22,0x0F,0x6C,0x74,0xD5, 0x10,0x58,0x4B,0xFC,0x36,0x6E,0x98,0xB2,0xB9,0x44,0x3B,0x4A,0x53,0x61,0x94,0x71,0x48,0x32,0x6D,0x02,
0x1C,0xA4,0x9B,0x01,0x55,0x8F,0x66,0x61,0xB8,0xEC,0xAD,0xD0,0x82,0x8E,0x2B,0xE6,0xB1,0xFC,0x8A,0x62, 0x8B,0x19,0x4E,0xB5,0x80,0xE1,0x4C,0xA3,0x8C,0x6B,0x59,0x9E,0x24,0x2C,0xE5,0x2B,0xFA,0xB0,0xF5,0xF2,
0xF1,0x5B,0x6E,0xE2,0xEE,0xCA,0x8B,0x6A,0x93,0xAA,0x63,0x6D,0x18,0x1B,0x58,0x23,0x94,0x71,0x44,0x7D, 0xC1,0xE6,0x5D,0x8E,0x73,0x1C,0x14,0xCB,0x38,0x6D,0xEA,0x45,0x16,0x57,0xBF,0x0B,0xB8,0x77,0x75,0x5D,
0x71,0x8D,0x4A,0x17,0x66,0x51,0x66,0x89,0xED,0x3D,0xE1,0x9F,0xD2,0x96,0x9E,0xCC,0xED,0x52,0x32,0xEA, 0x94,0x5A,0x39,0xA5,0x22,0x0F,0x6C,0xF4,0xD5,0x1C,0xA4,0x0B,0x81,0xAA,0x47,0xF3,0x28,0x5A,0x0E,0x56,
0xE9,0xBA,0xA8,0xB8,0xB0,0x28,0x5C,0x51,0xC4,0x6A,0x64,0xCB,0x88,0x6D,0x52,0xAF,0x00,0xF7,0xE4,0x3B, 0x68,0x41,0xC7,0xC5,0x38,0x13,0xF6,0xF0,0x16,0xBF,0x15,0x57,0xDC,0x5D,0x79,0x51,0x6D,0x53,0x75,0xAC,
0x78,0x73,0xFF,0x2A,0x1B,0x45,0x99,0x8D,0x00,0xA6,0x15,0xFB,0x4C,0xDA,0xF2,0x74,0x99,0xA9,0x0F,0xA4, 0x0D,0x63,0x03,0x6B,0x84,0x66,0x1C,0x51,0x5F,0x98,0x51,0xE9,0xC2,0x2C,0xCB,0x2C,0xB1,0x7D,0x20,0xFC,
0xEB,0x4A,0x03,0x8E,0x30,0x9F,0xC5,0xC1,0x81,0x7E,0xA0,0xBB,0x3A,0xFC,0xDB,0xD4,0x2D,0xD8,0x85,0xA9, 0x53,0xDE,0xA5,0x47,0x73,0xBB,0x94,0x8C,0x7A,0xBA,0x2E,0x2A,0x2E,0x2C,0x0A,0x57,0x14,0x67,0x0D,0xB2,
0x0F,0xEE,0x3F,0xBA,0x7C,0x09,0x21,0x2D,0x89,0x29,0xA6,0xDC,0x40,0x10,0xD9,0x3D,0xBD,0xB5,0x06,0x52, 0x55,0xC4,0x36,0xA9,0x57,0x82,0x07,0xF2,0x1D,0xBC,0xBD,0x7F,0x95,0x8D,0xB2,0xCC,0x46,0x00,0xD3,0xCA,
0x90,0xBF,0x46,0x37,0x66,0xAF,0xA9,0x6C,0x3C,0x5F,0xB9,0xCE,0xC6,0x65,0xD8,0x31,0x15,0x51,0xEE,0x01, 0x7D,0x26,0xED,0x78,0xBA,0xCC,0xD4,0xFB,0xD2,0x75,0xE5,0x05,0x8E,0x31,0x0F,0x59,0xB0,0xAF,0xEF,0xEB,
0x98,0xE7,0xF8,0xD1,0x3B,0xB6,0x45,0x1F,0xB7,0x23,0xE6,0x62,0x47,0xF0,0x2C,0xB1,0x06,0x80,0x25,0x5C, 0xAE,0x0E,0xFF,0x6F,0xEA,0x16,0xEC,0xC2,0xD4,0x07,0xF7,0xBF,0x38,0x7F,0x09,0x21,0x2D,0x61,0x14,0x53,
0xAF,0x52,0xF2,0x53,0x25,0x16,0xB5,0x7D,0x14,0x86,0xE2,0xEB,0x77,0xE0,0x55,0x1C,0x96,0x31,0x4B,0x10, 0x6E,0x20,0x88,0xEC,0x9E,0xDE,0x59,0x03,0x29,0xC9,0x5F,0xA1,0x6B,0x73,0xD0,0x56,0x36,0x9E,0xAF,0x98,
0x61,0x20,0x00,0xC3,0x43,0x7C,0x0F,0x2E,0x2D,0xEB,0x24,0x65,0x43,0x79,0x5F,0x02,0xA4,0x79,0xCD,0x8C, 0xB3,0x65,0x0C,0x9B,0x51,0x11,0xE5,0xEE,0x81,0x79,0x8E,0x1F,0xBC,0x63,0x5B,0xF4,0x61,0x3B,0x62,0x2E,
0x4C,0xAB,0x9C,0x6D,0x6E,0x72,0xBB,0x30,0x13,0xA3,0x40,0x62,0x22,0xE8,0x1B,0xA6,0xE8,0x39,0x8C,0xBA, 0xB6,0x05,0xCF,0x12,0x6B,0x04,0x58,0xC2,0xF5,0x6A,0x25,0x3F,0x56,0x62,0x51,0xDB,0x47,0x51,0x24,0xBE,
0x90,0x22,0x87,0x3E,0xD2,0xB6,0xD1,0x54,0x43,0x82,0xA9,0x51,0xC0,0x80,0xB3,0x2C,0x0D,0x21,0x52,0x8B, 0x7B,0x07,0x5E,0xC5,0x61,0x79,0x66,0x09,0x22,0x19,0x08,0x90,0xE1,0x31,0xBE,0x03,0x97,0x96,0x75,0x92,
0x2A,0xA2,0xB1,0x8D,0x61,0x1A,0xC0,0x01,0xEE,0x5F,0x22,0x4B,0x54,0x5A,0xB7,0x8A,0x01,0x4F,0x15,0xDF, 0xBA,0x43,0x45,0x5F,0x02,0xA4,0x79,0xE3,0x1A,0x99,0x56,0x35,0xDB,0xDC,0xE4,0x76,0x79,0x4D,0x8C,0x12,
0x4D,0x7A,0x3C,0xBF,0x95,0x53,0x2C,0x23,0x5F,0xF3,0x77,0x0F,0x2D,0x1D,0x25,0x49,0x48,0x54,0x60,0xEE, 0x29,0x13,0x41,0xDF,0x30,0x45,0xCF,0x61,0x34,0x85,0x14,0x39,0xF4,0x81,0xB6,0x8D,0xB6,0x1A,0x12,0x4C,
0xC0,0xC3,0xF7,0x7C,0xDE,0x06,0xB3,0x8A,0xDA,0x70,0x82,0xB2,0x82,0x40,0x5F,0xC3,0xA1,0x6A,0xBB,0x2C, 0x8D,0x12,0x06,0x9C,0xE5,0x69,0x04,0x91,0x5A,0x54,0x11,0xAD,0x6D,0x19,0xA6,0x01,0x1C,0xE0,0xFE,0x25,
0xF1,0x60,0xE1,0xAE,0xB8,0x68,0xA5,0x3F,0x63,0xC5,0x47,0x9B,0x74,0x9A,0x7C,0xD6,0x6E,0x7B,0x6F,0x67, 0xB2,0x44,0xA5,0xCD,0x5B,0x31,0xE2,0xA9,0xE2,0xBB,0x4D,0x8F,0x17,0x56,0x39,0xC6,0x32,0xF2,0xB5,0x7F,
0x05,0x84,0xC6,0xE2,0x7B,0x76,0xC8,0x35,0x90,0x8E,0xCA,0xD3,0x8A,0x0E,0xA8,0x1E,0x17,0xEC,0x24,0x63, 0x2C,0x69,0xC9,0x9F,0x39,0x12,0x15,0x98,0x7B,0xF0,0xF0,0x3D,0x9F,0x77,0xC5,0x57,0xEF,0x5D,0x38,0x41,
0x33,0x63,0x01,0xAC,0xBB,0xDC,0x52,0xBA,0x72,0xB1,0xA5,0xEC,0xD6,0xA5,0x56,0x61,0x1F,0x2E,0xA4,0x90, 0xDD,0x82,0x40,0x5F,0xC3,0xA1,0x6A,0xBB,0x2C,0xF1,0x60,0xE1,0xAE,0xB8,0x68,0xAD,0x3F,0x63,0xC5,0x47,
0x46,0xFC,0xF8,0xE3,0x0F,0x39,0xAB,0x6E,0x1B,0x66,0x2B,0x17,0xBE,0x54,0x6F,0xAC,0x45,0x66,0x14,0x46, 0xDB,0x74,0xDA,0x7C,0x36,0xAC,0xBD,0xBB,0xBD,0x02,0x42,0x13,0x96,0x72,0x03,0x72,0x0D,0xA4,0xA3,0xEA,
0xBF,0x1A,0xAB,0xF6,0x3A,0xAA,0xB1,0x29,0x7E,0x41,0x41,0x23,0x81,0x27,0x92,0xAC,0x9E,0x7F,0x05,0x01, 0xB4,0xB2,0x03,0x6A,0xC6,0x05,0x3B,0xC9,0xB3,0xD0,0x58,0x00,0xEB,0x2E,0xB7,0x94,0xAE,0x5C,0x6C,0xA9,
0xC3,0x09,0xC4,0x25,0x1B,0x3A,0xA1,0xF2,0xB7,0x3B,0xF5,0xFC,0xD7,0x3B,0x57,0x71,0xF7,0xCB,0xD6,0xCC, 0x7B,0xEB,0x52,0xAB,0xBC,0x1F,0x2E,0xA4,0x90,0x56,0xFC,0xF8,0xE3,0x0F,0x39,0xAB,0xAD,0x0D,0xB3,0x15,
0xF3,0xCA,0xBA,0x5A,0x45,0x1D,0xB5,0xC1,0x7B,0xE2,0xD7,0x16,0x74,0x05,0x86,0xCE,0x55,0x0D,0xEC,0xF2, 0x83,0x2F,0xD5,0x1B,0x6B,0x99,0x19,0xC5,0xA5,0x5F,0x8D,0x55,0xBB,0x3D,0xD5,0xD8,0x94,0xBF,0x4E,0xD0,
0x6C,0x71,0xAE,0x95,0x2F,0x22,0xF6,0x40,0x7D,0x11,0x35,0x4B,0x2A,0x33,0x50,0x7F,0xA3,0x9B,0x55,0x3B, 0x48,0xE0,0x89,0x24,0xAB,0x17,0x5F,0x41,0xC0,0x70,0x0A,0x71,0xC9,0x86,0x4E,0xA8,0xFA,0x39,0xAA,0x5E,
0xCD,0x65,0xC5,0x99,0x78,0x9E,0xD3,0xE2,0xFC,0x2D,0xBB,0x9E,0xEB,0x81,0x7D,0x68,0x96,0x85,0xB8,0x85, 0xFC,0x1E,0x75,0x15,0x77,0xAF,0x6A,0xCD,0x3C,0xAF,0xAA,0xAB,0x55,0xD4,0x51,0x1B,0xBC,0x47,0x7E,0xB3,
0x8B,0x6A,0xB2,0x5B,0x2E,0xA7,0x20,0x1E,0x6C,0xA8,0x76,0x65,0xC9,0x7A,0x80,0x4A,0xBF,0xAB,0x8B,0x2A, 0xA0,0x2B,0x30,0x74,0xAE,0x6A,0x60,0x57,0x67,0x8B,0x73,0xAD,0x62,0x11,0x65,0xF7,0xD4,0x17,0x51,0xB3,
0xBB,0xAF,0x2E,0x17,0x1D,0x57,0xF9,0xB5,0x4D,0xED,0x37,0x4E,0x2A,0x1A,0x44,0x3E,0xC8,0xCB,0xFD,0xCD, 0xA2,0x12,0x82,0xFA,0x5B,0xDD,0xAC,0xDA,0x69,0x2E,0x6B,0xCE,0xC4,0xF3,0x9C,0xC6,0x8A,0xB7,0xEC,0x66,
0x37,0xFA,0x27,0xD1,0x14,0xC6,0xFE,0x9F,0xFB,0x0C,0xD2,0xB3,0xD2,0x2B,0x00,0x00 0xAE,0x07,0xF6,0xA1,0x59,0x16,0xE2,0x96,0x2E,0xAA,0xC9,0x6E,0xB9,0x9A,0x82,0x78,0xB0,0xA1,0xDE,0x95,
0x27,0xEB,0x01,0x2A,0xFD,0xAE,0x2E,0xAA,0xEC,0xBE,0xBA,0x5C,0x76,0x5C,0xD5,0xD7,0x36,0x8D,0x9F,0x9B,
0xD4,0x34,0x88,0x7C,0x90,0x97,0xFB,0xDB,0x6F,0xF4,0x8F,0xA2,0x29,0x8C,0xBD,0x3F,0x01,0x2C,0xED,0xAE,
0xEF,0x83,0x2C,0x00,0x00
}; };