ESP32-CAM-Video-Recorder-ju.../v58/ESPxWebFlMgr.cpp

903 wiersze
29 KiB
C++

// mods by James Zahary Dec 28, 2021 https://github.com/jameszah/ESPxWebFlMgr
// 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"
#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=\"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>";
}
// 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)) ) {
ESPxWebFlMgr_FileSystem.remove( subdir + "/" + fn); // Add slash
}
}
// +--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+
// 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(fn);
//Serial.println(fn2);
ESPxWebFlMgr_FileSystem.rename( subdir + "/" + fn, subdir + "/" + fn2);
}
}
}
// dummy answer
fileManager->send(200, "text/plain", "");
delay(1);
}