60x.4.7 Sep 13, 2022

pull/34/head
jameszah 2022-09-13 22:26:17 -06:00 zatwierdzone przez GitHub
rodzic d4b79f152b
commit 2242501cb9
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
8 zmienionych plików z 4775 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,59 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// based on https://github.com/holgerlembke/ESPxWebFlMgr
//
// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier: MIT
//
#include "CRC32.h"
// Conditionally use pgm memory if it is available.
#if defined(PROGMEM)
#define FLASH_PROGMEM PROGMEM
#define FLASH_READ_DWORD(x) (pgm_read_dword_near(x))
#else
#define FLASH_PROGMEM
#define FLASH_READ_DWORD(x) (*(uint32_t*)(x))
#endif
static const uint32_t crc32_table[] FLASH_PROGMEM = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
CRC32::CRC32()
{
reset();
}
void CRC32::reset()
{
_state = ~0L;
}
void CRC32::update(const uint8_t& data)
{
// via http://forum.arduino.cc/index.php?topic=91179.0
uint8_t tbl_idx = 0;
tbl_idx = _state ^ (data >> (0 * 4));
_state = FLASH_READ_DWORD(crc32_table + (tbl_idx & 0x0f)) ^ (_state >> 4);
tbl_idx = _state ^ (data >> (1 * 4));
_state = FLASH_READ_DWORD(crc32_table + (tbl_idx & 0x0f)) ^ (_state >> 4);
}
uint32_t CRC32::finalize() const
{
return ~_state;
}

Wyświetl plik

@ -0,0 +1,77 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// based on https://github.com/holgerlembke/ESPxWebFlMgr
//
// Copyright (c) 2013 Christopher Baker <https://christopherbaker.net>
//
// SPDX-License-Identifier: MIT
//
#pragma once
#include "Arduino.h"
/// \brief A class for calculating the CRC32 checksum from arbitrary data.
/// \sa http://forum.arduino.cc/index.php?topic=91179.0
class CRC32
{
public:
/// \brief Initialize an empty CRC32 checksum.
CRC32();
/// \brief Reset the checksum claculation.
void reset();
/// \brief Update the current checksum caclulation with the given data.
/// \param data The data to add to the checksum.
void update(const uint8_t& data);
/// \brief Update the current checksum caclulation with the given data.
/// \tparam Type The data type to read.
/// \param data The data to add to the checksum.
template <typename Type>
void update(const Type& data)
{
update(&data, 1);
}
/// \brief Update the current checksum caclulation with the given data.
/// \tparam Type The data type to read.
/// \param data The array to add to the checksum.
/// \param size Size of the array to add.
template <typename Type>
void update(const Type* data, size_t size)
{
size_t nBytes = size * sizeof(Type);
const uint8_t* pData = (const uint8_t*)data;
for (size_t i = 0; i < nBytes; i++)
{
update(pData[i]);
}
}
/// \returns the caclulated checksum.
uint32_t finalize() const;
/// \brief Calculate the checksum of an arbitrary data array.
/// \tparam Type The data type to read.
/// \param data A pointer to the data to add to the checksum.
/// \param size The size of the data to add to the checksum.
/// \returns the calculated checksum.
template <typename Type>
static uint32_t calculate(const Type* data, size_t size)
{
CRC32 crc;
crc.update(data, size);
return crc.finalize();
}
private:
/// \brief The internal checksum state.
uint32_t _state = ~0L;
};

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -0,0 +1,927 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// Jan 12, 2022 - adds dates/times to display
// based on https://github.com/holgerlembke/ESPxWebFlMgr
#include <Arduino.h>
#include <inttypes.h>
#include "ESPxWebFlMgr.h"
#include "ESPxWebFlMgrWp.h"
#include "ESPxWebFlMgrWpF.h"
#include "crc32.h"
#include <time.h> //jz
#ifdef ESP8266
#include <ESP8266WebServer.h>
#include <FS.h>
#endif
#ifdef ESP32
#include <WebServer.h>
#include <FS.h>
#include <SD_MMC.h> //jz #include <SPIFFS.h>
#include <detail/RequestHandlersImpl.h>
#endif
String getContentType(const String& path) {
#ifdef ESP8266
return mime::getContentType(path);
#endif
#ifdef ESP32
return StaticRequestHandler::getContentType(path);
#endif
}
//*****************************************************************************************************
ESPxWebFlMgr::ESPxWebFlMgr(word port) {
_Port = port;
}
//*****************************************************************************************************
ESPxWebFlMgr::~ESPxWebFlMgr() {
end();
}
//*****************************************************************************************************
void ESPxWebFlMgr::begin() {
#ifdef ESP8266
fileManager = new ESP8266WebServer(_Port);
#endif
#ifdef ESP32
fileManager = new WebServer(_Port);
#endif
#ifdef fileManagerServerStaticsInternally
fileManager->on("/", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerIndexpage, this));
fileManager->on("/fm.css", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerCSS, this));
fileManager->on("/fm.js", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerJS, this));
#endif
fileManager->on("/bg.css", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerGetBackGround, this));
fileManager->on("/i", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerFileListInsert, this));
fileManager->on("/c", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerCommandExecutor, this));
fileManager->on("/e", HTTP_GET, std::bind(&ESPxWebFlMgr::fileManagerFileEditorInsert, this));
// file receiver with attached file to form
fileManager->on("/r", HTTP_POST, std::bind(&ESPxWebFlMgr::fileManagerReceiverOK, this),
std::bind(&ESPxWebFlMgr::fileManagerReceiver, this));
fileManager->onNotFound(std::bind(&ESPxWebFlMgr::fileManagerNotFound, this));
fileManager->begin();
}
//*****************************************************************************************************
void ESPxWebFlMgr::end() {
if (fileManager) {
delete fileManager;
fileManager = NULL;
}
}
//*****************************************************************************************************
void ESPxWebFlMgr::handleClient() {
if (fileManager) {
fileManager->handleClient();
}
}
//*****************************************************************************************************
void ESPxWebFlMgr::setViewSysFiles(bool vsf) {
_ViewSysFiles = vsf;
}
//*****************************************************************************************************
bool ESPxWebFlMgr::getViewSysFiles(void) {
return _ViewSysFiles;
}
//*****************************************************************************************************
void ESPxWebFlMgr::setSysFileStartPattern(String sfsp) {
_SysFileStartPattern = sfsp;
}
//*****************************************************************************************************
String ESPxWebFlMgr::getSysFileStartPattern(void) {
return _SysFileStartPattern;
}
//*****************************************************************************************************
// privates start here
//*****************************************************************************************************
//*****************************************************************************************************
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerGetBackGround(void) {
fileManager->send(200, F("text/css"), ".background {background-color: " + _backgroundColor + ";}");
}
//*****************************************************************************************************
void ESPxWebFlMgr::setBackGroundColor(const String backgroundColor) {
_backgroundColor = backgroundColor;
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerNotFound(void) {
String uri = fileManager->uri();
if (uri == "/") {
uri = "/fm.html";
}
String contentTyp = getContentType(uri);
if (ESPxWebFlMgr_FileSystem.exists(uri)) {
File f = ESPxWebFlMgr_FileSystem.open(uri, "r");
if (f) {
if (fileManager->streamFile(f, contentTyp) != f.size()) {
// Serial.println(F("Sent less data than expected!"));
// We should panic a little bit.
}
f.close();
}
} else
{
fileManager->send(404, F("text/plain"), F("URI not found."));
}
}
//*****************************************************************************************************
String ESPxWebFlMgr::dispIntDotted(size_t i) {
String res = "";
while (i != 0) {
int r = i % 1000;
res = String(i % 1000) + res;
i /= 1000;
if ( (r < 100) && (i > 0) ) {
res = "0" + res;
if (r < 10) {
res = "0" + res;
}
}
if (i != 0) {
res = "," + res; //jz dot to comma ;-)
}
}
return res;
}
//*****************************************************************************************************
size_t ESPxWebFlMgr::totalBytes(void) {
#ifdef ESP8266
FSInfo info;
ESPxWebFlMgr_FileSystem.info(info);
return info.totalBytes;
#endif
#ifdef ESP32
return (ESPxWebFlMgr_FileSystem.totalBytes() / 1024);
#endif
}
//*****************************************************************************************************
size_t ESPxWebFlMgr::usedBytes(void) {
#ifdef ESP8266
FSInfo info;
ESPxWebFlMgr_FileSystem.info(info);
return info.usedBytes;
#endif
#ifdef ESP32
return (ESPxWebFlMgr_FileSystem.usedBytes() / 1024);
#endif
}
//*****************************************************************************************************
String ESPxWebFlMgr::dispFileString(size_t fs) {
if (fs < 0) {
return "-0";
}
if (fs == 0) {
return "0 kB";
}
if (fs < 1000) {
return String(fs) + " kB";
}
// switch from bytes to kilobytes due to 4gb+ sd cards //jz
//String units[] = { "B", "kB", "MB", "GB", "TB" };
String units[] = { "kB", "MB", "GB", "TB" };
int digitGroups = (int) (log10(fs) / log10(1024));
//return String(fs / pow(1024, digitGroups)) + " " + units[digitGroups] + " <small>(" + dispIntDotted(fs) + " kB)</small>";
return String(fs / pow(1024, digitGroups)) + " " + units[digitGroups] ;
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerIndexpage(void) {
fileManager->send(200, F("text/html"), FPSTR(ESPxWebFlMgrWpindexpage));
delay(1);
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerJS(void) {
fileManager->send(200, F("text/javascript"), FPSTR(ESPxWebFlMgrWpjavascript));
delay(1);
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerCSS(void) {
fileManager->send(200, F("text/css"), FPSTR(ESPxWebFlMgrWpcss));
delay(1);
}
//*****************************************************************************************************
String ESPxWebFlMgr::CheckFileNameLengthLimit(String fn) {
// SPIFFS file name limit. Is there a way to get the max length from SPIFFS/LittleFS?
// SPIFFS_OBJ_NAME_LEN is spiffs.... but not very clean.
if (fn.length() > 32) {
int len = fn.length();
fn.remove(29);
fn += String(len);
}
return fn;
}
//*****************************************************************************************************
String ESPxWebFlMgr::colorline(int i) {
if (i % 2 == 0) {
return "ccu";
} else {
return "ccg";
}
}
//*****************************************************************************************************
boolean ESPxWebFlMgr::allowAccessToThisFile(const String filename) {
return ! filename.startsWith(_SysFileStartPattern);
}
//*****************************************************************************************************
// jz kludge for switching folders on the sd card
String subdir = "/";
//*****************************************************************************************************
//[make FS from esp8266 and esp32 compatible]**********************************************************
// this is the way MS DOS 3.x (?) did it with Int21 findfirst/findnext/findclose
#ifdef ESP8266
File ESPxWebFlMgr::nextFile(Dir &dir) {
dir.next();
return dir.openFile("r");
}
File ESPxWebFlMgr::firstFile(Dir &dir) {
dir = ESPxWebFlMgr_FileSystem.openDir("/");
return nextFile(dir);
}
#endif
#ifdef ESP32
#define Dir File
File ESPxWebFlMgr::nextFile(Dir &dir) {
return dir.openNextFile();
}
File ESPxWebFlMgr::firstFile(Dir &dir) {
dir = ESPxWebFlMgr_FileSystem.open(subdir, "r"); //jz dir = ESPxWebFlMgr_FileSystem.open("/", "r");
return nextFile(dir);
}
#endif
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerFileListInsert(void) { // must get arg with /i to list that folder
fileManager->setContentLength(CONTENT_LENGTH_UNKNOWN);
fileManager->send(200, F("text/html"), String());
fileManager->sendContent(F("<div class=\"cc\"><div class=\"gc\">"));
bool gzipperexists = ( (ESPxWebFlMgr_FileSystem.exists("/gzipper.js.gz")) ||
(ESPxWebFlMgr_FileSystem.exists("/gzipper.js")) );
//Serial.println(fileManager->args());
//Serial.println(fileManager->argName(0));
//Serial.println(fileManager->arg(0));
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "subdir") ) {
subdir = fileManager->arg(0);
//Serial.print("Arg: "); Serial.println(fileManager->arg(0));
/*
if (fileManager->arg(0) == "/"){
subdir = "/";
} else if (subdir == "/") {
subdir = fileManager->arg(0);
} else {
subdir = fileManager->arg(0);
}
Serial.print("New subdir: "); Serial.println(subdir);
*/
} else {
subdir = "/";
}
//Serial.print("Final subdir: "); Serial.println(subdir);
// first file is "go to root"
String fcd;
String direct = "ccd"; //jz bland color for directory
String fn = "/";
fcd = "<div "
"class=\"ccl " + direct + "\""
"onclick=\"opendirectory('" + fn + "')\""
">&nbsp;&nbsp;" + fn + " - GOTO ROOT DIR -" + "</div>";
fcd += "<div class=\"ccz " + direct + "\">&nbsp;" + " " + "&nbsp;</div>";
fcd += "<div class=\"cct " + direct + "\">&nbsp;" + dispIntDotted(0) + "&nbsp;</div>";
fcd += "<div class=\"ccr " + direct + "\">&nbsp;";
fcd += "&nbsp;&nbsp;</div>";
fileManager->sendContent(fcd);
// List files
int i = 0;
Dir dir;
File file = firstFile(dir);
while (file) {
String fn = file.name();
/*
Serial.print("FN: >");
Serial.print(fn);
Serial.print("<");
Serial.println();
*/
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
/*
String fc = "<div id=\"file"+String(i)+"\" "
"data-filename=\""+fn+"\""
"class=\"ccl " + colorline(i) + "\""
"onclick=\"downloadfile('" + fn + "')\""
">&nbsp;&nbsp;" + fn + "</div>";
*/
String fc;
String nsd;
if (subdir == "/") {
nsd = "/";
} else {
nsd = subdir + "/";
}
if (file.isDirectory()) {
String direct = "ccd"; //jz bland color for directory
fc = "<div "
"class=\"ccl " + direct + "\""
"onclick=\"opendirectory('" + nsd + fn + "')\""
">&nbsp;&nbsp;" + fn + " - DIR -" + "</div>";
} else {
//Serial.println(subdir);
//Serial.println(fn);
fc = "<div "
"class=\"ccl " + colorline(i) + "\""
"onclick=\"downloadfile('" + nsd + fn + "')\""
">&nbsp;&nbsp;" + fn + "</div>";
}
// sd_mmc not recognizing daylight savings time ???? jz may 21 2022 - seemed to work before dst
//time_t t= file.getLastWrite();
time_t t= file.getLastWrite() - 3600;
struct tm * tmstruct = localtime(&t);
//Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
char ccz[100];
sprintf(ccz, " %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
fc += "<div class=\"ccz " + colorline(i) + "\">&nbsp;" + String(ccz) + "&nbsp;</div>"; //jz
// File f = dir.openFile("r");
fc += "<div class=\"cct " + colorline(i) + "\">&nbsp;" + dispIntDotted(file.size()) + "&nbsp;</div>";
fc += "<div class=\"ccr " + colorline(i) + "\">&nbsp;"
"<button title=\"Delete\" onclick=\"deletefile('" + fn + "')\" class=\"b\">D</button> "
"<button title=\"Rename\" onclick=\"renamefile('" + fn + "')\" class=\"b\">R</button> ";
// no gziped version and (zipper or gziped zipper) exists
if ( (! (fn.endsWith(".gz")) ) && gzipperexists) {
fc += "<button title=\"Compress\" onclick=\"compressurlfile('" + fn + "')\" class=\"b\">C</button> ";
}
// for editor
#ifndef fileManagerEditEverything
String contentTyp = getContentType(fn);
if ( (contentTyp.startsWith("text/")) ||
(contentTyp.startsWith("application/j")) ) // boldly assume: json, javascript and everything else is edible....
#endif
{
fc += "<button title=\"Edit\" onclick=\"editfile('" + fn + "')\" class=\"b\">E</button> ";
}
fc += "&nbsp;&nbsp;</div>";
fileManager->sendContent(fc);
i++;
}
file = nextFile(dir);
}
// fileManager->sendContent("<span id=\"filecount\" data-count=\""+String(i)+"\"></span>");
String sinfo = "&nbsp; Size: " +
dispFileString(totalBytes()) +
", used: " +
dispFileString(usedBytes());
/*
fileManager->sendContent(F(" FS blocksize: "));
fileManager->sendContent(String(info.blockSize));
fileManager->sendContent(F(", pageSize: "));
fileManager->sendContent(String(info.pageSize));
*/
fileManager->sendContent(F("</div></div>"));
fileManager->sendContent(F("##"));
fileManager->sendContent(sinfo);
fileManager->sendContent("");
delay(1);
}
//*****************************************************************************************************
String ESPxWebFlMgr::escapeHTMLcontent(String html) {
//html.replace("<","&lt;");
//html.replace(">","&gt;");
html.replace("&", "&amp;");
return html;
}
// in place editor
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerFileEditorInsert(void) {
//Serial.println("Edit");
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "edit") ) {
String fn = "/" + fileManager->arg(0);
if ( (! _ViewSysFiles) && (!allowAccessToThisFile(fn)) ) {
fileManager->send(404, F("text/plain"), F("Illegal."));
return;
}
fileManager->setContentLength(CONTENT_LENGTH_UNKNOWN);
fileManager->send(200, F("text/html"), String());
fileManager->sendContent(ESPxWebFlMgrWpFormIntro);
if (ESPxWebFlMgr_FileSystem.exists(subdir + "/" + fn)) {
File f = ESPxWebFlMgr_FileSystem.open(subdir + "/" + fn, "r");
if (f) {
do {
String l = f.readStringUntil('\n') + '\n';
l = escapeHTMLcontent(l);
fileManager->sendContent(l);
} while (f.available());
f.close();
}
}
fileManager->sendContent(ESPxWebFlMgrWpFormExtro1);
fileManager->sendContent(fn);
fileManager->sendContent(ESPxWebFlMgrWpFormExtro2);
fileManager->sendContent("");
} else {
fileManager->send(404, F("text/plain"), F("Illegal."));
}
delay(1);
}
// Drag and Drop
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
// https://www.ab-heute-programmieren.de/drag-and-drop-upload-mit-html5/#Schritt_3_Eine_Datei_hochladen
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerReceiverOK(void) {
// Serial.println("fileManagerReceiverOK");
fileManager->send(200);
delay(1);
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerReceiver(void) {
// Serial.println("fileManagerReceiver");
HTTPUpload& upload = fileManager->upload();
// Serial.println("Server upload Status: " + String(upload.status));
if (upload.status == UPLOAD_FILE_START) {
String filename = upload.filename;
if (!filename.startsWith("/")) {
filename = "/" + filename;
}
// Serial.print("handleFileUpload Name: ");
// Serial.println(filename);
if (! ( (_ViewSysFiles) || (allowAccessToThisFile(filename)) ) ) {
filename = "/illegalfilename";
}
// cut length
filename = CheckFileNameLengthLimit(filename);
fsUploadFile = ESPxWebFlMgr_FileSystem.open(subdir + filename, "w");
} else if (upload.status == UPLOAD_FILE_WRITE) {
// Serial.print("handleFileUpload Data: ");
// Serial.println(upload.currentSize);
if (fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) {
fsUploadFile.close();
// fsUploadFile = NULL;
}
// Serial.print("handleFileUpload Size: ");
// Serial.println(upload.totalSize);
}
delay(1);
}
struct __attribute__ ((__packed__)) zipFileHeader {
uint32_t signature; // 0x04034b50;
uint16_t versionneeded;
uint16_t bitflags;
uint16_t comp_method;
uint16_t lastModFileTime;
uint16_t lastModFileDate;
uint32_t crc_32;
uint32_t comp_size;
uint32_t uncompr_size;
uint16_t fname_len;
uint16_t extra_field_len;
};
struct __attribute__ ((__packed__)) zipDataDescriptor {
uint32_t signature; // 0x08074b50
uint32_t crc32;
uint32_t comp_size;
uint32_t uncompr_size;
};
struct __attribute__ ((__packed__)) zipEndOfDirectory {
uint32_t signature; // 0x06054b50;
uint16_t nrofdisks;
uint16_t diskwherecentraldirectorystarts;
uint16_t nrofcentraldirectoriesonthisdisk;
uint16_t totalnrofcentraldirectories;
uint32_t sizeofcentraldirectory;
uint32_t ofsetofcentraldirectoryrelativetostartofarchiv;
uint16_t commentlength;
};
struct __attribute__ ((__packed__)) zipCentralDirectoryFileHeader {
uint32_t signature; // 0x02014b50
uint16_t versionmadeby;
uint16_t versionneededtoextract;
uint16_t flag;
uint16_t compressionmethode;
uint16_t lastModFileTime;
uint16_t lastModFileDate;
uint32_t crc_32;
uint32_t comp_size;
uint32_t uncompr_size;
uint16_t fname_len;
uint16_t extra_len;
uint16_t comment_len;
uint16_t diskstart;
uint16_t internalfileattr;
uint32_t externalfileattr;
uint32_t relofsoflocalfileheader;
// nun filename, extra field, comment
};
//*****************************************************************************************************
int ESPxWebFlMgr::WriteChunk(const char* b, size_t l) {
// Serial.print(" Chunk: " + String(l) + " ");
const char * footer = "\r\n";
char chunkSize[11];
sprintf(chunkSize, "%zx\r\n", l);
fileManager->client().write(chunkSize, strlen(chunkSize));
fileManager->client().write(b, l);
fileManager->client().write(footer, 2);
return strlen(chunkSize) + l + 2;
}
//*****************************************************************************************************
/* https://en.wikipedia.org/wiki/Zip_(file_format)
https://www.fileformat.info/tool/hexdump.htm
https://hexed.it/?hl=de
HxD https://mh-nexus.de/de/
This code needs some memory:
4 * <nr. of files> + copybuffersize
Uses no compression, because, well, code size. Should be good for 4mb.
*/
void ESPxWebFlMgr::getAllFilesInOneZIP(void) {
const byte copybuffersize = 100;
fileManager->setContentLength(CONTENT_LENGTH_UNKNOWN);
// fileManager->sendHeader(F("Content-Type"), F("text/text"));
// fileManager->sendHeader(F("Transfer-Encoding"), F("chunked"));
// fileManager->sendHeader(F("Connection"), F("close"));
fileManager->sendHeader(F("Content-Disposition"), F("attachment; filename=alles.zip"));
fileManager->sendHeader(F("Content-Transfer-Encoding"), F("binary"));
fileManager->send(200, F("application/octet-stream"), "");
// Pass 0: count files
int files = 0;
{
Dir dir;
File file = firstFile(dir);
while (file) {
String fn = file.name();
if (!file.isDirectory() && (file.size() != 0)) { //jz
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
files++;
}
}
file = nextFile(dir);
}
//Serial.println("Files: " + String(files));
}
// Store the crcs
uint32_t crc_32s[files];
// Pass 1: local headers + file
{
zipFileHeader zip;
zip.signature = 0x04034b50;
zip.versionneeded = 0;
zip.bitflags = 1 << 3;
zip.comp_method = 0; // stored
zip.lastModFileTime = 0x4fa5;
zip.lastModFileDate = 0xe44e;
zip.extra_field_len = 0;
int i = 0;
Dir dir;
File file = firstFile(dir);
while (file) {
String fn = file.name();
if (!file.isDirectory() && (file.size() != 0) ) { //jz
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
if (fn.indexOf("/") == 0) {
fn.remove(0, 1); // "/" filenames beginning with "/" dont work for Windows....
}
zip.comp_size = 0;
zip.uncompr_size = 0;
zip.crc_32 = 0;
zip.fname_len = fn.length();
WriteChunk((char*)&zip, sizeof(zip));
WriteChunk(fn.c_str(), zip.fname_len);
//Serial.print("Send: " + fn);
// File f = dir.open("r",FILE_READ);
int len = file.size();
//Serial.print("\nsending "); Serial.print(fn);
//Serial.print(" len is "); Serial.println(len);
// send crc+len later...
zipDataDescriptor datadiscr;
datadiscr.signature = 0x08074b50;
datadiscr.comp_size = len;
datadiscr.uncompr_size = len;
const char * footer = "\r\n";
char chunkSize[11];
sprintf(chunkSize, "%zx\r\n", len);
fileManager->client().write(chunkSize, strlen(chunkSize));
{ // pff.
CRC32 crc;
byte b[copybuffersize];
int lenr = len;
while (lenr > 0) {
byte r = (lenr > copybuffersize) ? copybuffersize : lenr;
file.read(b, r);
crc.update(b, r);
fileManager->client().write(b, r);
lenr -= r;
// Serial.print(lenr);Serial.print(","); //jz
}
//Serial.println(" done");
datadiscr.crc32 = crc.finalize();
crc_32s[i] = datadiscr.crc32;
}
fileManager->client().write(footer, 2);
WriteChunk((char*)&datadiscr, sizeof(datadiscr));
// f.close();
i++;
/** /
Serial.print(" ");
Serial.print(l);
Serial.println();
/**/
}
}
file = nextFile(dir);
}
}
// Pass 2: Central directory Structur
{
zipEndOfDirectory eod;
eod.signature = 0x06054b50;
eod.nrofdisks = 0;
eod.diskwherecentraldirectorystarts = 0;
eod.nrofcentraldirectoriesonthisdisk = 0;
eod.totalnrofcentraldirectories = 0;
eod.sizeofcentraldirectory = 0;
eod.ofsetofcentraldirectoryrelativetostartofarchiv = 0;
eod.commentlength = 0;
zipCentralDirectoryFileHeader CDFH;
CDFH.signature = 0x02014b50;
CDFH.versionmadeby = 0;
CDFH.versionneededtoextract = 0;
CDFH.flag = 0;
CDFH.compressionmethode = 0; // Stored
CDFH.lastModFileTime = 0x4fa5;
CDFH.lastModFileDate = 0xe44e;
CDFH.extra_len = 0;
CDFH.comment_len = 0;
CDFH.diskstart = 0;
CDFH.internalfileattr = 0x01;
CDFH.externalfileattr = 0x20;
CDFH.relofsoflocalfileheader = 0;
int i = 0;
Dir dir;
File file = firstFile(dir);
while (file) {
String fn = file.name();
if (!file.isDirectory() && (file.size() != 0)) { //jz
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
if (fn.indexOf("/") == 0) {
fn.remove(0, 1); // "/" filenames beginning with "/" dont work for Windows....
}
// Serial.print("CDFH: " + fn);
// File f = dir.open("r",FILE_READ);
int len = file.size();
//Serial.print("\nsending "); Serial.print(fn); //jz
//Serial.print(" len is "); Serial.println(len);
CDFH.comp_size = len;
CDFH.uncompr_size = len;
CDFH.fname_len = fn.length();
CDFH.crc_32 = crc_32s[i];
// f.close();
WriteChunk((char*)&CDFH, sizeof(CDFH));
WriteChunk(fn.c_str(), CDFH.fname_len);
int ofs = sizeof(zipFileHeader) + len + CDFH.fname_len + sizeof(zipDataDescriptor);
// next position
CDFH.relofsoflocalfileheader += ofs;
// book keeping
eod.nrofcentraldirectoriesonthisdisk++;
eod.totalnrofcentraldirectories++;
eod.ofsetofcentraldirectoryrelativetostartofarchiv += ofs;
eod.sizeofcentraldirectory += sizeof(CDFH) + CDFH.fname_len;
i++;
}
}
file = nextFile(dir);
}
// Serial.print("EOD: ");
WriteChunk((char*)&eod, sizeof(eod));
// Serial.println();
}
const char * endchunk = "0\r\n\r\n";
fileManager->client().write(endchunk, 5);
fileManager->sendContent("");
delay(1);
}
//*****************************************************************************************************
void ESPxWebFlMgr::fileManagerCommandExecutor(void) {
// https://www.youtube.com/watch?v=KSxTxynXiBs
/*
for (uint8_t i = 0; i < fileManager->args(); i++) {
Serial.print(i);
Serial.print(" ");
Serial.print(fileManager->argName(i));
Serial.print(": ");
Serial.print(fileManager->arg(i));
Serial.println();
}
*/
// no Args: DIE!
if (fileManager->args() == 0) {
return;
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// one arg, "za", zip all and download
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "za") ) {
getAllFilesInOneZIP();
// does it all
return;
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// one arg, "dwn", download
// must happen in the context of the webpage... thus via "window.location.href="/c?dwn="+filename;"
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "dwn") ) {
String fn = fileManager->arg(0);
Serial.println(fn);
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
//Serial.println("allowed");
File f = ESPxWebFlMgr_FileSystem.open("/" + fn, "r"); // add slash for esp32_arduino 2.0
if (f) {
//Serial.println("got it open");
fileManager->sendHeader(F("Content-Type"), F("text/text"));
fileManager->sendHeader(F("Connection"), F("close"));
//Serial.print(">");Serial.print(fn);Serial.println("<");
//Serial.println(fn.indexOf("/"));
if (fn.indexOf("/") == 0) {
fileManager->sendHeader(F("Content-Disposition"), "attachment; filename=" + fn.substring(1));
} else {
fileManager->sendHeader(F("Content-Disposition"), "attachment; filename=" + fn);
}
fileManager->sendHeader(F("Content-Transfer-Encoding"), F("binary"));
if (fileManager->streamFile(f, "application/octet-stream") != f.size()) {
Serial.println(F("Sent less data than expected!"));
}
f.close();
return;
} else {
Serial.print("Could not open file "); Serial.println(fn);
}
}
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// one arg, "opd", opendirectory
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "opd") ) {
String fn = fileManager->arg(0);
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
delay(1);
}
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// one arg, "del", delete
if ( (fileManager->args() == 1) && (fileManager->argName(0) == "del") ) {
String fn = fileManager->arg(0);
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
int x = ESPxWebFlMgr_FileSystem.remove( subdir + "/" + fn); // Add slash
//delay(1000);
if (!x) {
Serial.print("remove failed, try rmdir ");
Serial.print( subdir + "/" + fn );
int y = ESPxWebFlMgr_FileSystem.rmdir( "/" + fn); // Add slash
//delay(1000);
if (!y) {
Serial.print("rmdir failed, directory must be empty! ");
}
}
}
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// one arg, "ren", rename
if ( (fileManager->args() == 2) && (fileManager->argName(0) == "ren") ) {
String fn = fileManager->arg(0);
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn)) ) {
String fn2 = CheckFileNameLengthLimit(fileManager->arg(1));
if ( (_ViewSysFiles) || (allowAccessToThisFile(fn2)) ) {
Serial.println(subdir);
Serial.println(fn);
Serial.println(fn2);
ESPxWebFlMgr_FileSystem.rename( subdir + "/" + fn, subdir + "/" + fn2);
}
}
}
// dummy answer
fileManager->send(200, "text/plain", "");
delay(1);
}

Wyświetl plik

@ -0,0 +1,162 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// Jan 12, 2022 - adds dates/times to display
// based on https://github.com/holgerlembke/ESPxWebFlMgr
// inline guard. Did I mention that c/c++ is broken by design?
#ifndef ESPxWebFlMgr_h
#define ESPxWebFlMgr_h
/*
Changes
V1.03
x removed all SPIFFS from ESP32 version, switched fully to LittleFS
x fixed rename+delete for ESP32+LittleFS (added "/")
V1.02
x fixed the way to select the file system by conditional defines
V1.01
+ added file name progress while uploading
x fixed error in ZIP file structure (zip.bitflags needs a flag)
V1.00
+ out of V0.9998...
+ ESP8266: LittleFS is default
+ javascript: added "msgline();"
+ javascript: added "Loading..." as a not-working-hint to show that Javascript is disabled
+ cleaning up the "/"-stuff (from SPIFF with leading "/" to LittleFS without...)
+ Warning: esp8266 2.7.4 has an error in mime::getContentType(path) for .TXT. Fix line 65 is { kTxtSuffix, kTxt },
+ review of "edit file", moved some stuff to ESPxWebFlMgrWpF.h
*/
#include <Arduino.h>
#include <inttypes.h>
// file system default for esp8266 is LittleFS, for ESP32 it is SPIFFS (no time to check...)
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
//
#include <LittleFS.h>
#define ESPxWebFlMgr_FileSystem LittleFS
/*
#include <SPIFFS.h>
#define ESPxWebFlMgr_FileSystem SPIFFS
*/
#endif
#ifdef ESP32
#include <WiFi.h>
#include <WebServer.h>
#include <FS.h>
#include <SD_MMC.h> //jz #include <LittleFS.h>
#define ESPxWebFlMgr_FileSystem SD_MMC //jz #define ESPxWebFlMgr_FileSystem LittleFS
#endif
#ifndef ESPxWebFlMgr_FileSystem
#pragma message ("ESPxWebFlMgr_FileSystem not defined.")
#endif
/* undefine this to save about 10k code space.
it requires to put the files from "<library>/filemanager" into the FS. No free lunch.
*/
#define fileManagerServerStaticsInternally
// will show the Edit-Button for every file type, even binary and such.
//#define fileManagerEditEverything
class ESPxWebFlMgr {
private:
word _Port ;
#ifdef ESP8266
ESP8266WebServer * fileManager = NULL;
#endif
#ifdef ESP32
WebServer * fileManager = NULL;
#endif
bool _ViewSysFiles = false;
String _SysFileStartPattern = "/.";
File fsUploadFile;
String _backgroundColor = "black";
void fileManagerNotFound(void);
String dispIntDotted(size_t i);
String dispFileString(size_t fs);
String CheckFileNameLengthLimit(String fn);
// the webpage
void fileManagerIndexpage(void);
void fileManagerJS(void);
void fileManagerCSS(void);
void fileManagerGetBackGround(void);
// javascript xmlhttp includes
String colorline(int i);
String escapeHTMLcontent(String html);
void fileManagerFileListInsert(void);
void fileManagerFileEditorInsert(void);
boolean allowAccessToThisFile(const String filename);
void fileManagerCommandExecutor(void);
void fileManagerReceiverOK(void);
void fileManagerReceiver(void);
// Zip-File uncompressed/stored
void getAllFilesInOneZIP(void);
int WriteChunk(const char* b, size_t l);
// helper: fs.h from esp32 and esp8266 don't have a compatible solution
// for getting a file list from a directory
#ifdef ESP32
#define Dir File
#endif
File nextFile(Dir &dir);
File firstFile(Dir &dir);
// and not to get this data about usage...
size_t totalBytes(void);
size_t usedBytes(void);
public:
ESPxWebFlMgr(word port);
virtual ~ESPxWebFlMgr();
void begin();
void end();
virtual void handleClient();
// This must be called before the webpage is loaded in the browser...
// must be a valid css color name, see https://en.wikipedia.org/wiki/Web_colors
void setBackGroundColor(const String backgroundColor);
void setViewSysFiles(bool vsf);
bool getViewSysFiles(void);
void setSysFileStartPattern(String sfsp);
String getSysFileStartPattern(void);
};
#endif
/*
History
-- 2019-07-07
+ Renamed to ESPxWebFlMgr and made it work with esp32 and esp8266
+ separated file manager web page, "build script" to generate it
-- 2019-07-06
+ "Download all files" creates a zip file from all files and downloads it
+ option to set background color
- html5 fixes
-- 2019-07-03
+ Public Release on https://github.com/holgerlembke/ESP8266WebFlMgr
Things to do
?? unify file system access for SPIFFS, LittleFS and SDFS
*/

Wyświetl plik

@ -0,0 +1,541 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// Jan 12, 2022 - adds dates/times to display
// based on https://github.com/holgerlembke/ESPxWebFlMgr
// inline guard. Did I mention that c/c++ is broken by design?
#ifndef ESPxWebFlMgrWp_h
#define ESPxWebFlMgrWp_h
// this file has been created by makeESPxWebFlMgrWp\do.cmd
//*****************************************************************************************************
static const char ESPxWebFlMgrWpindexpage[] PROGMEM = R"==x==(
<!DOCTYPE html>
<html lang="en">
<head>
<title>FileManager</title>
<meta charset="utf-8"/>
<link rel="stylesheet" type="text/css" href="/bg.css">
<link rel="stylesheet" type="text/css" href="/fm.css">
<script src="/fm.js"></script>
<script src="/gzipper.js"></script>
</head>
<body class="background">
<div id="gc">
<div class="o1">&nbsp;</div>
<div class="o2">&nbsp;</div>
<div class="o3" id="o3">&nbsp;</div>
<div class="o4">&nbsp;</div>
<div class="m1">
<div class="s11">&nbsp;</div>
<div class="s12">
<div class="s13 background">&nbsp;</div>
</div>
</div>
<div class="m2" ondrop="dropHandler(event);" ondragover="dragOverHandler(event);">
File<br />
Drop<br />
Zone<br />
</div>
<div class="m3">
<div class="s31">&nbsp;</div>
<div class="s32">
<div class="s33 background">&nbsp;</div>
</div>
</div>
<div class="u1">&nbsp;</div>
<div class="u2" onclick="downloadall();">Download all files</div>
<div class="u3" id="msg">Loading...</div>
<div class="u4">&nbsp;</div>
<div class="c" id="fi">
File list should appear here.
</div>
</div>
</body>
</html>
)==x==";
static const char ESPxWebFlMgrWpjavascript[] PROGMEM = R"==x==(
function compressurlfile(source) {
msgline("Fetching file...");
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
var data = this.responseText;
var gzip = require('gzip-js'), options = { level: 9, name: source, timestamp: parseInt(Date.now() / 1000, 10) };
var out = gzip.zip(data, options);
var bout = new Uint8Array(out); // out is 16 bits...
msgline("Sending compressed file...");
var sendback = new XMLHttpRequest();
sendback.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
getfileinsert();
}
};
sendback.open('POST', '/r');
var formdata = new FormData();
var blob = new Blob([bout], { type: "application/octet-binary" });
formdata.append(source + '.gz', blob, source + '.gz');
sendback.send(formdata);
}
};
request.open('GET', source, true);
request.send(null);
}
var subdir;
function getfileinsert() {
msgline("Fetching files infos...");
subdir = '/';
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
var res = this.responseText.split("##");
document.getElementById('fi').innerHTML = res[0];
document.getElementById("o3").innerHTML = res[1];
msgline("");
}
};
request.open('GET', '/i', true);
request.send(null);
}
function getfileinsert2(strddd) {
msgline("Fetching files infos...");
subdir = strddd;
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
var res = this.responseText.split("##");
document.getElementById('fi').innerHTML = res[0];
document.getElementById("o3").innerHTML = res[1];
msgline("");
}
};
request.open('GET', '/i?subdir=' + strddd, true); // must send the subdir variable to get that folder //jz
request.send(null);
}
function executecommand(command) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
getfileinsert2(subdir);
}
};
xhr.open('GET', '/c?' + command, true);
xhr.send(null);
}
function downloadfile(filename) {
window.location.href = "/c?dwn=" + filename;
}
function opendirectory(filename) {
getfileinsert2(filename);
}
function deletefile(filename) {
if (confirm("Really delete " + filename)) {
msgline("Refresh when done deleting..."); //jz msgline("Please wait. Delete in progress...");
executecommand("del=" + filename);
}
}
function renamefile(filename) {
var newname = prompt("new name for " + filename, filename);
if (newname != null) {
msgline("Refresh when done renaming ..."); //jz msgline("Please wait. Rename in progress...");
executecommand("ren=" + filename + "&new=" + newname);
}
}
var editxhr;
function editfile(filename) {
msgline("Please wait. Creating editor...");
editxhr = new XMLHttpRequest();
editxhr.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
document.getElementById('fi').innerHTML = editxhr.responseText;
document.getElementById("o3").innerHTML = "Edit " + filename;
msgline("");
}
};
editxhr.open('GET', '/e?edit=' + filename, true);
editxhr.send(null);
}
function sved(filename) {
var content = document.getElementById('tect').value;
// utf-8
content = unescape(encodeURIComponent(content));
var xhr = new XMLHttpRequest();
xhr.open("POST", "/r", true);
var boundary = '-----whatever';
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
var body = "" +
'--' + boundary + '\r\n' +
'Content-Disposition: form-data; name="uploadfile"; filename="' + filename + '"' + '\r\n' +
'Content-Type: text/plain' + '\r\n' +
'' + '\r\n' +
content + '\r\n' +
'--' + boundary + '--\r\n' + // \r\n fixes upload delay in ESP8266WebServer
'';
// ajax does not do xhr.setRequestHeader("Content-length", body.length);
xhr.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
getfileinsert();
}
}
xhr.send(body);
}
function abed() {
getfileinsert();
}
var uploaddone = true; // hlpr for multiple file uploads
function uploadFile(file, islast) {
uploaddone = false;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
// console.log(xhr.status);
var DONE = this.DONE || 4;
if (this.readyState === DONE) {
if (islast) {
getfileinsert2(subdir);
console.log('last file');
}
uploaddone = true;
}
};
xhr.open('POST', '/r');
var formdata = new FormData();
//var newname = subdir + '/' + file.name; //jz didnt work, so do it in c++
//file.name = newname;
formdata.append('uploadfile', file);
// not sure why, but with that the upload to esp32 is stable.
formdata.append('dummy', 'dummy');
xhr.send(formdata);
}
var globaldropfilelisthlpr = null; // read-only-list, no shift()
var transferitem = 0;
var uploadFileProzessorhndlr = null;
function uploadFileProzessor() {
if (uploaddone) {
if (transferitem==globaldropfilelisthlpr.length) {
clearInterval(uploadFileProzessorhndlr);
} else {
var file = globaldropfilelisthlpr[transferitem];
msgline("Please wait. Transferring file "+file.name+"...");
console.log('process file ' + file.name);
transferitem++;
uploadFile(file,transferitem==globaldropfilelisthlpr.length);
}
}
}
/*
function dropHandlerALT(ev) {
console.log('File(s) dropped');
document.getElementById('msg').innerHTML = "Please wait. Transferring file...";
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.items.length; i++) {
// If dropped items aren't files, reject them
if (ev.dataTransfer.items[i].kind === 'file') {
var file = ev.dataTransfer.items[i].getAsFile();
uploadFile(file);
console.log('.1. file[' + i + '].name = ' + file.name);
}
}
} else {
// Use DataTransfer interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.files.length; i++) {
console.log('.2. file[' + i + '].name = ' + ev.dataTransfer.files[i].name);
}
}
}
*/
function dropHandler(ev) {
console.log('File(s) dropped');
globaldropfilelisthlpr = ev.dataTransfer;
transferitem = 0;
msgline("Please wait. Transferring file...");
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
if (ev.dataTransfer.items) {
var data = ev.dataTransfer;
globaldropfilelisthlpr = data.files;
uploadFileProzessorhndlr = setInterval(uploadFileProzessor,1000);
console.log('Init upload list.');
} else {
// Use DataTransfer interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.files.length; i++) {
console.log('.2. file[' + i + '].name = ' + ev.dataTransfer.files[i].name);
}
}
}
function dragOverHandler(ev) {
console.log('File(s) in drop zone');
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
}
function msgline(msg) {
document.getElementById('msg').innerHTML = msg;
}
function downloadall() {
msgline("Sending all files in one zip.");
window.location.href = "/c?za=all";
msgline("");
}
//->
window.onload = getfileinsert;
)==x==";
//*****************************************************************************************************
static const char ESPxWebFlMgrWpcss[] PROGMEM = R"==g==(
div {
margin: 1px;
padding: 0px;
font-family: 'Segoe UI', Verdana, sans-serif;
}
#gc {
display: grid;
grid-template-columns: 80px 25% auto 30px;
grid-template-rows: 20px 30px auto 30px 20px;
grid-template-areas: "o1 o2 o3 o4" "m1 c c c" "m2 c c c" "m3 c c c" "u1 u2 u3 u4";
}
.o1 {
grid-area: o1;
background-color: #9999CC;
border-top-left-radius: 20px;
margin-bottom: 0px;
}
.o2 {
grid-area: o2;
background-color: #9999FF;
margin-bottom: 0px;
}
.o3 {
grid-area: o3;
background-color: #CC99CC;
margin-bottom: 0px;
white-space: nowrap;
}
.o4 {
grid-area: o4;
background-color: #CC6699;
border-radius: 0 10px 10px 0;
margin-bottom: 0px;
}
.m1 {
grid-area: m1;
margin-top: 0px;
background-color: #9999CC;
display: grid;
grid-template-columns: 60px 20px;
grid-template-rows: 20px;
grid-template-areas: "s11 s12";
}
.s12 {
margin: 0px;
background-color: #9999CC;
}
.s13 {
margin: 0px;
border-top-left-radius: 20px;
height: 30px;
}
.m2 {
display: flex;
justify-content: center;
align-items: center;
grid-area: m2;
background-color: #CC6699;
width: 60px;
}
.m3 {
grid-area: m3;
margin-bottom: 0px;
background-color: #9999CC;
display: grid;
grid-template-columns: 60px 20px;
grid-template-rows: 20px;
grid-template-areas: "s31 s32";
}
.s32 {
margin: 0px;
background-color: #9999CC;
}
.s33 {
margin: 0px;
border-bottom-left-radius: 20px;
height: 30px;
}
.u1 {
grid-area: u1;
background-color: #9999CC;
border-bottom-left-radius: 20px;
margin-top: 0px;
}
.u2 {
grid-area: u2;
cursor: pointer;
background-color: #CC6666;
margin-top: 0px;
padding-left: 10px;
vertical-align: middle;
font-size: 80%;
}
.u2:hover {
background-color: #9999FF;
color: white;
}
.u3 {
grid-area: u3;
padding-left: 10px;
background-color: #FF9966;
font-size: 80%;
margin-top: 0px;
}
.u4 {
grid-area: u4;
background-color: #FF9900;
border-radius: 0 10px 10px 0;
margin-top: 0px;
}
.c {
grid-area: c;
}
#fi .b {
background-color: Transparent;
border: 1px solid #9999FF;
border-radius: 1px;
padding: 0px;
width: 30px;
cursor: pointer;
}
#fi .b:hover {
background-color: #9999FF;
color: white;
}
.cc {
width: min-content;
margin: 10px 0px;
}
.gc div {
padding: 1px;
}
.ccg {
height: 1.5em;
background-color: #A5A5FF;
}
.ccu {
height: 1.5em;
background-color: #FE9A00;
}
.ccd {
height: 1.5em;
background-color: #e8e2d8;
}
.ccl {
border-radius: 5px 0 0 5px;
cursor: pointer;
}
.ccl:hover {
border-radius: 5px 0 0 5px;
color: white;
cursor: pointer;
}
.ccr {
border-radius: 0 5px 5px 0;
}
.cct {
text-align: right;
}
.ccz {
text-align: right;
}
.gc {
display: grid;
grid-template-columns: repeat(4, max-content);
}
)==g==";
#endif

Wyświetl plik

@ -0,0 +1,23 @@
// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// based on https://github.com/holgerlembke/ESPxWebFlMgr
// inline guard. Still broken by design?
#ifndef ESPxWebFlMgrWpF_h
#define ESPxWebFlMgrWpF_h
static const char ESPxWebFlMgrWpFormIntro[] PROGMEM =
R"==x==(<form><textarea id="tect" rows="25" cols="80">)==x==";
static const char ESPxWebFlMgrWpFormExtro1[] PROGMEM =
R"==x==(</textarea></form><button title="Save file" onclick="sved(')==x==";
// noot sure what the <script> part is for.
static const char ESPxWebFlMgrWpFormExtro2[] PROGMEM =
R"==x==(');" >Save</button>&nbsp;<button title="Abort editing" onclick="abed();">Abort editing</button>
<script id="info">document.getElementById('o3').innerHTML = "File:";</script>)==x==";
#endif

Wyświetl plik

@ -0,0 +1,159 @@
//https://www.mischianti.org/online-converter-file-to-cpp-gzip-byte-array-3/
//File: config.txt, Size: 2438
#define config_txt_len 2438
const char config_txt[] PROGMEM = {
0x64, 0x65, 0x73, 0x6B, 0x6C, 0x65, 0x6E, 0x73, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x63, 0x61, 0x6D,
0x65, 0x72, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x0D, 0x0A, 0x31, 0x31, 0x20, 0x20, 0x2F, 0x2F,
0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x69, 0x7A, 0x65, 0x20, 0x20, 0x31, 0x31, 0x3D, 0x68,
0x64, 0x0D, 0x0A, 0x31, 0x38, 0x30, 0x30, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x6C, 0x65, 0x6E, 0x67,
0x74, 0x68, 0x20, 0x6F, 0x66, 0x20, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x20, 0x69, 0x6E, 0x20, 0x73,
0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x0D, 0x0A, 0x30, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x69, 0x6E,
0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x20, 0x2D, 0x20, 0x6D, 0x73, 0x20, 0x62, 0x65, 0x74, 0x77,
0x65, 0x65, 0x6E, 0x20, 0x72, 0x65, 0x63, 0x6F, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72,
0x61, 0x6D, 0x65, 0x73, 0x20, 0x0D, 0x0A, 0x31, 0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x70, 0x65,
0x65, 0x64, 0x75, 0x70, 0x20, 0x2D, 0x20, 0x6D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x6C, 0x79, 0x20,
0x66, 0x72, 0x61, 0x6D, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x0D, 0x0A, 0x30, 0x20, 0x20, 0x2F,
0x2F, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x64, 0x65, 0x6C, 0x61, 0x79, 0x20, 0x2D, 0x20,
0x6D, 0x73, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6E, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61,
0x6D, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x0D, 0x0A, 0x47, 0x4D, 0x54,
0x20, 0x2F, 0x2F, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0x0D, 0x0A, 0x61, 0x70,
0x20, 0x20, 0x2F, 0x2F, 0x20, 0x73, 0x73, 0x69, 0x64, 0x20, 0x77, 0x69, 0x66, 0x69, 0x20, 0x6E,
0x61, 0x6D, 0x65, 0x0D, 0x0A, 0x6D, 0x72, 0x70, 0x65, 0x61, 0x6E, 0x75, 0x74, 0x20, 0x20, 0x2F,
0x2F, 0x20, 0x73, 0x73, 0x69, 0x64, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x0D,
0x0A, 0x0D, 0x0A, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x0D, 0x0A, 0x41,
0x62, 0x6F, 0x76, 0x65, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x73, 0x20, 0x2D, 0x20, 0x31, 0x20, 0x69,
0x74, 0x65, 0x6D, 0x20, 0x70, 0x65, 0x72, 0x20, 0x6C, 0x69, 0x6E, 0x65, 0x20, 0x66, 0x6F, 0x6C,
0x6C, 0x6F, 0x77, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x32, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65,
0x73, 0x0D, 0x0A, 0x0D, 0x0A, 0x64, 0x65, 0x73, 0x6B, 0x6C, 0x65, 0x6E, 0x73, 0x20, 0x2D, 0x20,
0x63, 0x61, 0x6D, 0x65, 0x72, 0x61, 0x20, 0x6E, 0x61, 0x6D, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20,
0x66, 0x69, 0x6C, 0x65, 0x73, 0x2C, 0x20, 0x6D, 0x64, 0x6E, 0x73, 0x2C, 0x20, 0x65, 0x74, 0x63,
0x0D, 0x0A, 0x31, 0x31, 0x20, 0x2D, 0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x69, 0x7A, 0x65,
0x20, 0x31, 0x31, 0x20, 0x69, 0x73, 0x20, 0x68, 0x64, 0x20, 0x37, 0x32, 0x30, 0x70, 0x20, 0x31,
0x32, 0x38, 0x30, 0x78, 0x37, 0x32, 0x30, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x66, 0x6F, 0x72, 0x20,
0x6F, 0x76, 0x32, 0x36, 0x34, 0x30, 0x20, 0x63, 0x61, 0x6D, 0x65, 0x72, 0x61, 0x20, 0x38, 0x3D,
0x76, 0x67, 0x61, 0x2C, 0x20, 0x39, 0x3D, 0x73, 0x76, 0x67, 0x61, 0x2C, 0x20, 0x31, 0x30, 0x3D,
0x78, 0x67, 0x61, 0x2C, 0x20, 0x31, 0x31, 0x3D, 0x68, 0x64, 0x2C, 0x20, 0x31, 0x32, 0x3D, 0x73,
0x78, 0x67, 0x61, 0x2C, 0x20, 0x31, 0x33, 0x3D, 0x75, 0x78, 0x67, 0x61, 0x0D, 0x0A, 0x20, 0x2D,
0x20, 0x66, 0x6F, 0x72, 0x20, 0x6F, 0x76, 0x35, 0x36, 0x34, 0x30, 0x20, 0x63, 0x61, 0x6D, 0x65,
0x72, 0x61, 0x20, 0x61, 0x64, 0x64, 0x20, 0x31, 0x34, 0x3D, 0x66, 0x68, 0x64, 0x2C, 0x20, 0x31,
0x37, 0x3D, 0x71, 0x78, 0x67, 0x61, 0x2C, 0x20, 0x31, 0x38, 0x3D, 0x71, 0x68, 0x64, 0x2C, 0x20,
0x32, 0x31, 0x3D, 0x71, 0x73, 0x78, 0x67, 0x61, 0x0D, 0x0A, 0x31, 0x38, 0x30, 0x30, 0x20, 0x2D,
0x20, 0x6C, 0x65, 0x6E, 0x67, 0x74, 0x68, 0x20, 0x6F, 0x66, 0x20, 0x76, 0x69, 0x64, 0x65, 0x6F,
0x20, 0x69, 0x6E, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x0D, 0x0A, 0x30, 0x20, 0x2D,
0x20, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6C, 0x20, 0x2D, 0x20, 0x6D, 0x69, 0x6C, 0x6C,
0x69, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6E, 0x20,
0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x72, 0x65, 0x63, 0x6F, 0x72,
0x64, 0x69, 0x6E, 0x67, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x30, 0x20, 0x69, 0x73, 0x20, 0x61, 0x73,
0x20, 0x66, 0x61, 0x73, 0x74, 0x20, 0x61, 0x73, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x69, 0x62, 0x6C,
0x65, 0x20, 0x62, 0x6F, 0x75, 0x6E, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x61, 0x6D, 0x65, 0x72,
0x61, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x6F, 0x72, 0x20, 0x73, 0x64, 0x20, 0x77, 0x72,
0x69, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, 0x0D, 0x0A, 0x20, 0x2D, 0x20,
0x35, 0x30, 0x30, 0x20, 0x69, 0x73, 0x20, 0x32, 0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x20,
0x70, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x20, 0x28, 0x73, 0x75, 0x62, 0x6A,
0x65, 0x63, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x29, 0x0D, 0x0A,
0x20, 0x2D, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x20, 0x69, 0x73, 0x20, 0x30, 0x2E, 0x31, 0x20,
0x66, 0x72, 0x61, 0x6D, 0x65, 0x73, 0x20, 0x70, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E,
0x64, 0x20, 0x6F, 0x72, 0x20, 0x31, 0x30, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x73, 0x20,
0x70, 0x65, 0x72, 0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x76, 0x67,
0x61, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x61, 0x78, 0x20, 0x32, 0x35, 0x20, 0x66, 0x70, 0x73, 0x2C,
0x20, 0x68, 0x64, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x61, 0x78, 0x20, 0x31, 0x32, 0x2E, 0x35, 0x20,
0x66, 0x70, 0x73, 0x2C, 0x20, 0x75, 0x78, 0x67, 0x61, 0x20, 0x61, 0x74, 0x20, 0x31, 0x32, 0x2E,
0x35, 0x20, 0x66, 0x70, 0x73, 0x20, 0x65, 0x74, 0x63, 0x0D, 0x0A, 0x31, 0x20, 0x2D, 0x20, 0x73,
0x70, 0x65, 0x65, 0x64, 0x75, 0x70, 0x20, 0x2D, 0x20, 0x6D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x6C,
0x79, 0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x72, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x73,
0x70, 0x65, 0x65, 0x64, 0x20, 0x75, 0x70, 0x20, 0x70, 0x6C, 0x61, 0x79, 0x62, 0x61, 0x63, 0x6B,
0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x31, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x61, 0x6C, 0x74,
0x69, 0x6D, 0x65, 0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x32, 0x34, 0x20, 0x77, 0x69, 0x6C, 0x6C,
0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x61, 0x20, 0x31, 0x20, 0x66, 0x70, 0x73, 0x20, 0x72, 0x65,
0x63, 0x6F, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x61, 0x74, 0x20, 0x32, 0x34, 0x20, 0x66, 0x70,
0x73, 0x20, 0x6F, 0x6E, 0x20, 0x79, 0x6F, 0x75, 0x72, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x75, 0x74,
0x65, 0x72, 0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x33, 0x30, 0x30, 0x20, 0x77, 0x69, 0x6C, 0x6C,
0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x31, 0x30, 0x20, 0x73, 0x65, 0x63, 0x20, 0x70, 0x65, 0x72,
0x20, 0x66, 0x72, 0x61, 0x6D, 0x65, 0x20, 0x61, 0x74, 0x20, 0x33, 0x30, 0x20, 0x66, 0x70, 0x73,
0x20, 0x6F, 0x6E, 0x20, 0x79, 0x6F, 0x75, 0x72, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x75, 0x74, 0x65,
0x72, 0x0D, 0x0A, 0x30, 0x20, 0x2D, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x64, 0x65, 0x6C,
0x61, 0x79, 0x20, 0x2D, 0x20, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64,
0x73, 0x20, 0x6F, 0x66, 0x20, 0x64, 0x65, 0x6C, 0x61, 0x79, 0x20, 0x62, 0x65, 0x74, 0x77, 0x65,
0x65, 0x6E, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x72, 0x61,
0x6D, 0x65, 0x73, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x61, 0x6C, 0x6C,
0x6F, 0x77, 0x73, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x74, 0x6F, 0x20, 0x73, 0x6C, 0x6F, 0x77, 0x20,
0x64, 0x6F, 0x77, 0x6E, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x69, 0x6E, 0x67, 0x20, 0x74,
0x6F, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x20, 0x70,
0x6F, 0x77, 0x65, 0x72, 0x20, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x6E, 0x64, 0x20,
0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x75, 0x73, 0x61, 0x67, 0x65, 0x0D, 0x0A, 0x20,
0x2D, 0x20, 0x61, 0x6C, 0x73, 0x6F, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x20, 0x61, 0x20,
0x66, 0x69, 0x78, 0x65, 0x64, 0x20, 0x72, 0x61, 0x74, 0x65, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6F,
0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6F, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20,
0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x20, 0x61, 0x74, 0x20, 0x79, 0x6F,
0x75, 0x72, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x75, 0x74, 0x65, 0x72, 0x0D, 0x0A, 0x20, 0x2D, 0x20,
0x76, 0x67, 0x61, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x20,
0x61, 0x62, 0x6F, 0x75, 0x74, 0x20, 0x33, 0x30, 0x2D, 0x33, 0x33, 0x20, 0x66, 0x70, 0x73, 0x20,
0x64, 0x65, 0x70, 0x65, 0x6E, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x6F, 0x6E, 0x20, 0x77, 0x69, 0x66,
0x69, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x6E, 0x65, 0x73, 0x73,
0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x73, 0x20, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6E,
0x20, 0x74, 0x68, 0x65, 0x20, 0x6A, 0x70, 0x65, 0x67, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x68, 0x64,
0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x20, 0x61, 0x62, 0x6F,
0x75, 0x74, 0x20, 0x31, 0x30, 0x2D, 0x31, 0x32, 0x20, 0x66, 0x70, 0x73, 0x20, 0x0D, 0x0A, 0x20,
0x2D, 0x20, 0x75, 0x78, 0x67, 0x61, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x6F, 0x6E, 0x6C, 0x79,
0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x20, 0x61, 0x74, 0x20, 0x33, 0x20, 0x66, 0x70, 0x73,
0x20, 0x0D, 0x0A, 0x20, 0x2D, 0x20, 0x6F, 0x76, 0x35, 0x36, 0x34, 0x30, 0x20, 0x63, 0x61, 0x6D,
0x65, 0x72, 0x61, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x72, 0x65, 0x63, 0x6F, 0x72, 0x64, 0x20,
0x31, 0x32, 0x38, 0x30, 0x78, 0x37, 0x32, 0x30, 0x20, 0x61, 0x74, 0x20, 0x32, 0x35, 0x66, 0x70,
0x73, 0x2C, 0x20, 0x65, 0x74, 0x63, 0x0D, 0x0A, 0x47, 0x4D, 0x54, 0x20, 0x2D, 0x20, 0x74, 0x69,
0x6D, 0x65, 0x7A, 0x6F, 0x6E, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x64, 0x61, 0x74, 0x65, 0x73,
0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x73, 0x20, 0x6F, 0x6E, 0x20, 0x79, 0x6F,
0x75, 0x72, 0x20, 0x66, 0x69, 0x6C, 0x65, 0x73, 0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x6D, 0x6F,
0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x3A, 0x20, 0x4D, 0x53, 0x54, 0x37, 0x4D, 0x44, 0x54, 0x2C,
0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2F, 0x32, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x2C, 0x4D,
0x31, 0x31, 0x2E, 0x31, 0x2E, 0x30, 0x2F, 0x32, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x0D, 0x0A,
0x20, 0x20, 0x2D, 0x20, 0x65, 0x61, 0x73, 0x74, 0x65, 0x72, 0x6E, 0x3A, 0x20, 0x45, 0x53, 0x54,
0x35, 0x45, 0x44, 0x54, 0x2C, 0x4D, 0x33, 0x2E, 0x32, 0x2E, 0x30, 0x2C, 0x4D, 0x31, 0x31, 0x2E,
0x31, 0x2E, 0x30, 0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x72, 0x61, 0x6C,
0x20, 0x65, 0x75, 0x72, 0x6F, 0x70, 0x65, 0x3A, 0x20, 0x43, 0x45, 0x54, 0x2D, 0x31, 0x43, 0x45,
0x53, 0x54, 0x2D, 0x32, 0x2C, 0x4D, 0x33, 0x2E, 0x35, 0x2E, 0x30, 0x2F, 0x30, 0x32, 0x3A, 0x30,
0x30, 0x3A, 0x30, 0x30, 0x2C, 0x4D, 0x31, 0x30, 0x2E, 0x35, 0x2E, 0x30, 0x2F, 0x30, 0x33, 0x3A,
0x30, 0x30, 0x3A, 0x30, 0x30, 0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x55, 0x6C, 0x61, 0x61, 0x6E,
0x62, 0x61, 0x61, 0x74, 0x61, 0x72, 0x2C, 0x20, 0x4D, 0x6F, 0x6E, 0x67, 0x6F, 0x6C, 0x69, 0x61,
0x3A, 0x20, 0x55, 0x4C, 0x41, 0x54, 0x2D, 0x38, 0x55, 0x4C, 0x41, 0x53, 0x54, 0x2C, 0x4D, 0x33,
0x2E, 0x35, 0x2E, 0x30, 0x2F, 0x32, 0x2C, 0x4D, 0x39, 0x2E, 0x35, 0x2E, 0x30, 0x2F, 0x32, 0x20,
0x0D, 0x0A, 0x20, 0x20, 0x2D, 0x20, 0x66, 0x69, 0x6E, 0x64, 0x20, 0x74, 0x69, 0x6D, 0x65, 0x7A,
0x6F, 0x6E, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, 0x3A, 0x20, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20,
0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F, 0x2F, 0x73, 0x69, 0x74, 0x65, 0x73, 0x2E, 0x67,
0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x61, 0x2F, 0x75, 0x73, 0x61, 0x70,
0x69, 0x65, 0x6E, 0x73, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6F, 0x70, 0x6E, 0x6F, 0x64, 0x65, 0x2F,
0x74, 0x69, 0x6D, 0x65, 0x2D, 0x7A, 0x6F, 0x6E, 0x65, 0x73, 0x0D, 0x0A, 0x73, 0x73, 0x69, 0x64,
0x31, 0x32, 0x33, 0x34, 0x20, 0x2D, 0x20, 0x79, 0x6F, 0x75, 0x72, 0x20, 0x77, 0x69, 0x66, 0x69,
0x20, 0x73, 0x73, 0x69, 0x64, 0x20, 0x75, 0x73, 0x65, 0x72, 0x6E, 0x61, 0x6D, 0x65, 0x0D, 0x0A,
0x20, 0x2D, 0x20, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6B, 0x65, 0x79,
0x77, 0x6F, 0x72, 0x64, 0x20, 0x22, 0x61, 0x70, 0x22, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x63, 0x63,
0x65, 0x73, 0x73, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x70, 0x68, 0x6F, 0x6E, 0x65, 0x20, 0x6F,
0x72, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x75, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x63,
0x6F, 0x6E, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x20, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
0x79, 0x6F, 0x75, 0x72, 0x20, 0x73, 0x73, 0x69, 0x64, 0x2F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F,
0x72, 0x64, 0x20, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x65, 0x62, 0x20, 0x70, 0x6F,
0x72, 0x74, 0x61, 0x6C, 0x20, 0x31, 0x39, 0x32, 0x2E, 0x31, 0x36, 0x38, 0x2E, 0x34, 0x2E, 0x31,
0x20, 0x6F, 0x72, 0x20, 0x45, 0x53, 0x50, 0x33, 0x32, 0x2D, 0x43, 0x41, 0x4D, 0x2C, 0x0D, 0x0A,
0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x73, 0x69, 0x6E, 0x67, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65,
0x64, 0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x20, 0x70, 0x72, 0x6F, 0x76, 0x69,
0x64, 0x65, 0x64, 0x20, 0x28, 0x61, 0x6E, 0x64, 0x20, 0x6F, 0x6E, 0x20, 0x73, 0x65, 0x72, 0x69,
0x61, 0x6C, 0x20, 0x6D, 0x6F, 0x6E, 0x69, 0x74, 0x6F, 0x72, 0x29, 0x0D, 0x0A, 0x20, 0x2D, 0x20,
0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x6B, 0x65, 0x79, 0x77, 0x6F, 0x72, 0x64, 0x20, 0x22, 0x73,
0x73, 0x69, 0x64, 0x31, 0x32, 0x33, 0x34, 0x22, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x6E, 0x6F, 0x20,
0x77, 0x69, 0x66, 0x69, 0x0D, 0x0A, 0x6D, 0x72, 0x70, 0x65, 0x61, 0x6E, 0x75, 0x74, 0x20, 0x2D,
0x20, 0x79, 0x6F, 0x75, 0x72, 0x20, 0x77, 0x69, 0x66, 0x69, 0x20, 0x73, 0x73, 0x69, 0x64, 0x20,
0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x0D, 0x0A, 0x0D, 0x0A, 0x53, 0x6F, 0x75, 0x72,
0x63, 0x65, 0x43, 0x6F, 0x64, 0x65, 0x3A, 0x20, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3A, 0x2F,
0x2F, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x6A, 0x61, 0x6D, 0x65,
0x73, 0x7A, 0x61, 0x68, 0x2F, 0x45, 0x53, 0x50, 0x33, 0x32, 0x2D, 0x43, 0x41, 0x4D, 0x2D, 0x56,
0x69, 0x64, 0x65, 0x6F, 0x2D, 0x52, 0x65, 0x63, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x2D, 0x6A, 0x75,
0x6E, 0x69, 0x6F, 0x72, 0x0D, 0x0A, 0x4F, 0x6E, 0x65, 0x2D, 0x43, 0x6C, 0x69, 0x63, 0x6B, 0x20,
0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x65, 0x72, 0x3A, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73,
0x3A, 0x2F, 0x2F, 0x6A, 0x61, 0x6D, 0x65, 0x73, 0x7A, 0x61, 0x68, 0x2E, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2E, 0x69, 0x6F, 0x2F, 0x45, 0x53, 0x50, 0x33, 0x32, 0x2D, 0x43, 0x41, 0x4D, 0x2D,
0x56, 0x69, 0x64, 0x65, 0x6F, 0x43, 0x61, 0x6D, 0x2F, 0x0D, 0x0A, 0x4A, 0x61, 0x6D, 0x65, 0x73,
0x20, 0x5A, 0x61, 0x68, 0x61, 0x72, 0x79, 0x20, 0x2D, 0x20, 0x4A, 0x61, 0x6E, 0x20, 0x31, 0x31,
0x2C, 0x20, 0x32, 0x30, 0x32, 0x32
};