defaults to 0 - i.e. the default is to require auth if configured
* it WILL serve on / - so conflicting urls could occur. I beleive native TAS urls will have priority.
* you can serve multiple folders, and they can each be auth or noauth
*
* by default, it also enables cors on the webserver - this allows you to have
* a website external to TAS which can access the files, else the browser refuses.
*/
#include "detail/RequestHandlersImpl.h"
//#define SERVING_DEBUG
// class to allow us to request auth when required.
// StaticRequestHandler is in the above header
class StaticRequestHandlerAuth : public StaticRequestHandler {
public:
StaticRequestHandlerAuth(FS& fs, const char* path, const char* uri, const char* cache_header, bool requireAuth):
StaticRequestHandler(fs, path, uri, cache_header)
{
_requireAuth = requireAuth;
}
bool _requireAuth;
// we replace the handle method,
// and look for authentication only if we would serve the file.
// if we check earlier, then we will reject as unauth even though a later
// handler may be public, and so fail to serve public files.
bool handle(WebServer& server, HTTPMethod requestMethod, String requestUri) override {
if (!canHandle(requestMethod, requestUri))
return false;
//log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handle: request=%s _uri=%s"), requestUri.c_str(), _uri.c_str());
#endif
String path(_path);
if (!_isFile) {
// Base URI doesn't point to a file.
// If a directory is requested, look for index file.
if (requestUri.endsWith("/"))
requestUri += "index.htm";
// Append whatever follows this URI in request to get the file path.
path += requestUri.substring(_baseUriLength);
}
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handle: path=%s, isFile=%d"), path.c_str(), _isFile);
#endif
String contentType = getContentType(path);
// look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for
// if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) {
String pathWithGz = path + FPSTR(mimeTable[gz].endsWith);
if(_fs.exists(pathWithGz))
path += FPSTR(mimeTable[gz].endsWith);
}
File f = _fs.open(path, "r");
if (!f || !f.available()){
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler missing file?"));
return false;
}
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler file open %d"), f.available());
#endif
if (_requireAuth && !WebAuthenticate()) {
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "serv of %s denied"), requestUri.c_str());
#endif
server.requestAuthentication();
return true;
}
if (_cache_header.length() != 0)
server.sendHeader("Cache-Control", _cache_header);
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler sending"));
#endif
uint8_t buff[512];
uint32_t bread;
uint32_t flen = f.available();
WiFiClient download_Client = server.client();
server.setContentLength(flen);
server.send(200, contentType, "");
// transfer is about 150kb/s
uint32_t cnt = 0;
while (f.available()) {
bread = f.read(buff, sizeof(buff));
cnt += bread;
#ifdef SERVING_DEBUG
//AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler sending %d/%d"), cnt, flen);
#endif
uint32_t bw = download_Client.write((const char*)buff, bread);
if (!bw) { break; }
yield();
}
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler sent %d/%d"), cnt, flen);
#endif
if (cnt != flen){
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "::handler incomplete file send: sent %d/%d"), cnt, flen);
}
// It does seem that on lesser ESP32, this causes a problem? A lockup...
//server.streamFile(f, contentType);
f.close();
download_Client.stop();
#ifdef SERVING_DEBUG
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "::handler done"));
#endif
return true;
}
};
void UFSServe(void) {
bool result = false;
if (XdrvMailbox.data_len > 0) {
char *fpath = strtok(XdrvMailbox.data, ",");
char *url = strtok(nullptr, ",");
char *noauth = strtok(nullptr, ",");
if (fpath && url) {
if (Webserver) { // fail if no Webserver yet.
StaticRequestHandlerAuth *staticHandler;
if (noauth && *noauth == '1'){
staticHandler = new StaticRequestHandlerAuth(*ffsp, fpath, url, (char *)nullptr, false);
} else {
staticHandler = new StaticRequestHandlerAuth(*ffsp, fpath, url, (char *)nullptr, true);
}
if (staticHandler) {
//Webserver->serveStatic(url, *ffsp, fpath);
Webserver->addHandler(staticHandler);
Webserver->enableCORS(true);
result = true;
}
}
}
}
if (!result) {
ResponseCmndFailed();
} else {
ResponseCmndDone();
}
}
#endif // UFILESYS_STATIC_SERVING
void UFSRun(void) {
if (XdrvMailbox.data_len > 0) {
char fname[UFS_FILENAME_SIZE];
if (UfsExecuteCommandFile(UfsFilename(fname, XdrvMailbox.data))) {
ResponseClear();
} else {
ResponseCmndFailed();
}
} else {
bool not_active = UfsExecuteCommandFileReady();
UfsData.run_file_pos = -1;
ResponseCmndChar(not_active ? PSTR(D_JSON_DONE) : PSTR(D_JSON_ABORTED));
}
}
/*********************************************************************************************\
* Web support
\*********************************************************************************************/
#ifdef USE_WEBSERVER
const char UFS_WEB_DIR[] PROGMEM =
"";
const char UFS_CURRDIR[] PROGMEM =
"%s: %s
";
#ifndef D_CURR_DIR
#define D_CURR_DIR "Folder"
#endif
const char UFS_FORM_FILE_UPGc[] PROGMEM =
"" D_FS_SIZE " %s MB - " D_FS_FREE " %s MB";
const char UFS_FORM_FILE_UPGc1[] PROGMEM =
"
%s";
const char UFS_FORM_FILE_UPGc2[] PROGMEM =
"
";
const char UFS_FORM_FILE_UPG[] PROGMEM =
""
"
";
const char UFS_FORM_SDC_DIRa[] PROGMEM =
"";
const char UFS_FORM_SDC_DIRc[] PROGMEM =
"
";
const char UFS_FORM_FILE_UPGb[] PROGMEM =
""
"";
const char UFS_FORM_FILE_UPGb1[] PROGMEM =
"";
const char UFS_FORM_FILE_UPGb2[] PROGMEM =
""
""
"" D_UPLOAD_STARTED " ...
";
const char UFS_FORM_SDC_DIR_NORMAL[] PROGMEM =
"";
const char UFS_FORM_SDC_DIR_HIDDABLE[] PROGMEM =
" class='hf'";
const char UFS_FORM_SDC_DIRd[] PROGMEM =
"%s
";
const char UFS_FORM_SDC_DIRb[] PROGMEM =
"%s %19s %8d %s %s
";
const char UFS_FORM_SDC_HREF[] PROGMEM =
"ufsd?download=%s/%s";
#ifdef GUI_TRASH_FILE
const char UFS_FORM_SDC_HREFdel[] PROGMEM =
//"🗑"; // 🗑️
"🔥"; // 🔥
#endif // GUI_TRASH_FILE
#ifdef GUI_EDIT_FILE
#define FILE_BUFFER_SIZE 1024
const char UFS_FORM_SDC_HREFedit[] PROGMEM =
"📝"; // 📝
const char HTTP_EDITOR_FORM_START[] PROGMEM =
"";
#endif // #ifdef GUI_EDIT_FILE
void HandleUploadUFSDone(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
HTTPUpload& upload = Webserver->upload();
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
WifiConfigCounter();
UploadServices(1);
WSContentStart_P(PSTR(D_INFORMATION));
WSContentSendStyle();
WSContentSend_P(PSTR("" D_UPLOAD " " D_FAILED "
"), WebColor(COL_TEXT_WARNING));
char error[100];
if (Web.upload_error < 10) {
GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors);
} else {
snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error);
}
WSContentSend_P(error);
Web.upload_error = 0;
} else {
WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
}
WSContentSend_P(PSTR("
"));
XdrvCall(FUNC_WEB_ADD_MANAGEMENT_BUTTON);
WSContentStop();
}
void UfsDirectory(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_MANAGE_FILE_SYSTEM));
uint8_t depth = 0;
uint8_t isdir = 0;
strcpy(ufs_path, "/");
if (Webserver->hasArg(F("dir"))) {
String stmp = Webserver->arg(F("dir"));
ufs_dir = atoi(stmp.c_str());
}
if (ufs_dir == 1) {
dfsp = ufsp;
} else {
if (ffsp) {
dfsp = ffsp;
}
}
if (Webserver->hasArg(F("delete"))) {
String stmp = Webserver->arg(F("delete"));
char *cp = (char*)stmp.c_str();
File download_file = dfsp->open(cp, UFS_FILE_READ);
if (download_file) {
if (download_file.isDirectory()) {
download_file.close();
dfsp->rmdir(cp);
} else {
download_file.close();
dfsp->remove(cp);
}
}
}
if (Webserver->hasArg(F("download"))) {
String stmp = Webserver->arg(F("download"));
char *cp = (char*)stmp.c_str();
if (UfsDownloadFile(cp)) {
// is directory
strcpy(ufs_path, cp);
isdir = 1;
} else {
return;
}
}
WSContentStart_P(PSTR(D_MANAGE_FILE_SYSTEM));
WSContentSendStyle();
WSContentSend_P(HTTP_DIV_F1_BLOCK);
WSContentSend_P(HTTP_FIELDSET_LEGEND, PSTR(D_MANAGE_FILE_SYSTEM));
char ts[FLOATSZ];
dtostrfd((float)UfsInfo(0, ufs_dir == 2 ? 1:0) / 1000, 3, ts);
char fs[FLOATSZ];
dtostrfd((float)UfsInfo(1, ufs_dir == 2 ? 1:0) / 1000, 3, fs);
WSContentSend_PD(UFS_FORM_FILE_UPGc, WebColor(COL_TEXT), ts, fs);
if (ufs_dir) {
WSContentSend_P(UFS_FORM_FILE_UPGc1, (ufs_dir == 1)?2:1, (ufs_dir == 1)?PSTR("SDCard"):PSTR("FlashFS"));
}
WSContentSend_P(UFS_FORM_FILE_UPGc2);
WSContentSend_P(UFS_FORM_FILE_UPG);
if (isdir){
// if a folder, show 'folder: xxx' if not '/'
if (ufs_path[0] != '/' || strlen(ufs_path) != 1){
WSContentSend_P(UFS_CURRDIR, PSTR(D_CURR_DIR), ufs_path);
}
}
WSContentSend_P(UFS_FORM_SDC_DIRa);
if (ufs_type) {
UfsListDir(ufs_path, depth);
}
WSContentSend_P(UFS_FORM_SDC_DIRc);
#ifdef GUI_EDIT_FILE
WSContentSend_P(HTTP_FORM_GET_ACTION, PSTR("ufse"));
WSContentSend_P(UFS_FORM_FILE_UPGb, ufs_path);
#endif
if (!UfsIsSDC()) {
WSContentSend_P(UFS_FORM_FILE_UPGb1);
}
WSContentSend_P(UFS_FORM_FILE_UPGb2);
WSContentSpaceButton(BUTTON_MANAGEMENT);
WSContentStop();
Web.upload_file_type = UPL_UFSFILE;
}
void UfsListDir(char *path, uint8_t depth) {
char name[UFS_FILENAME_SIZE];
char npath[128];
char format[12];
sprintf(format, PSTR("%%-%ds"), 24 - depth);
File dir = dfsp->open(path, UFS_FILE_READ);
if (dir) {
dir.rewindDirectory();
if (strlen(path)>1) {
ext_snprintf_P(npath, sizeof(npath), PSTR("ufsd?download=%s"), path);
for (uint32_t cnt = strlen(npath) - 1; cnt > 0; cnt--) {
if (npath[cnt] == '/') {
if (npath[cnt - 1] == '=') {
npath[cnt + 1] = 0;
} else {
npath[cnt] = 0;
}
break;
}
}
WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, PSTR(".."));
}
char *ep;
while (true) {
WiFiClient client = Webserver->client();
// abort if the client disconnected
// if there is a huge folder, then this gives a way out by refresh of browser
if (!client.connected()){
break;
}
File entry = dir.openNextFile();
if (!entry) {
break;
}
// esp32 returns path here, shorten to filename
ep = (char*)entry.name();
if (*ep == '/') { ep++; }
char *lcp = strrchr(ep,'/');
if (lcp) {
ep = lcp + 1;
}
uint32_t tm = entry.getLastWrite();
String tstr = GetDT(tm);
char *pp = path;
if (!*(pp + 1)) { pp++; }
char *cp = name;
// osx formatted disks contain a lot of stuff we dont want
bool hiddable = UfsReject((char*)ep);
if (!hiddable || !UfsIsSDC() ) {
for (uint8_t cnt = 0; cnt 1) {
strcat(path, "/");
}
strcat(path, ep);
UfsListDir(path, depth + 4);
path[plen] = 0;
#endif
} else {
#ifdef GUI_EDIT_FILE
char editpath[128];
ext_snprintf_P(editpath, sizeof(editpath), UFS_FORM_SDC_HREFedit, ppe, epe);
#else
char editpath[2];
editpath[0]=0;
#endif // GUI_TRASH_FILE
ext_snprintf_P(npath, sizeof(npath), UFS_FORM_SDC_HREF, ppe, epe);
WSContentSend_P(UFS_FORM_SDC_DIRb, hiddable ? UFS_FORM_SDC_DIR_HIDDABLE : UFS_FORM_SDC_DIR_NORMAL, npath, epe,
HtmlEscape(name).c_str(), tstr.c_str(), entry.size(), delpath, editpath);
}
entry.close();
yield(); // trigger watchdog reset
}
}
dir.close();
}
}
#ifdef ESP32
// this actually does not work reliably, as the
// webserver can close the connection before the download task completes
//#define ESP32_DOWNLOAD_TASK
#endif // ESP32
uint8_t UfsDownloadFile(char *file) {
File download_file;
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "File '%s' download"), file);
if (!dfsp->exists(file)) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "File '%s' not found"), file);
return 0;
}
download_file = dfsp->open(file, UFS_FILE_READ);
if (!download_file) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "Could not open file '%s'"), file);
return 0;
}
if (download_file.isDirectory()) {
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "File '%s' to download is directory"), file);
download_file.close();
return 1;
}
#ifndef ESP32_DOWNLOAD_TASK
WiFiClient download_Client;
uint32_t flen = download_file.size();
download_Client = Webserver->client();
Webserver->setContentLength(flen);
char attachment[100];
char *cp = fileOnly(file);
snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"), cp);
Webserver->sendHeader(F("Content-Disposition"), attachment);
WSSend(200, CT_APP_STREAM, "");
uint8_t buff[512];
uint32_t bread;
// transfer is about 150kb/s
uint32_t cnt = 0;
while (download_file.available()) {
bread = download_file.read(buff, sizeof(buff));
uint32_t bw = download_Client.write((const char*)buff, bread);
if (!bw) { break; }
cnt++;
if (cnt > 7) {
cnt = 0;
//if (glob_script_mem.script_loglevel & 0x80) {
// this indeed multitasks, but is slower 50 kB/s
// loop();
//}
}
delay(0);
OsWatchLoop();
}
download_file.close();
download_Client.stop();
#endif // ESP32_DOWNLOAD_TASK
// to make this work I thing you wouold need to duplicate the client
// BEFORE starting the task, so that the webserver does not close it's
// copy of the client.
#ifdef ESP32_DOWNLOAD_TASK
download_file.close();
if (UfsData.download_busy == true) {
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "Download is busy"));
return 0;
}
UfsData.download_busy = true;
char *path = (char*)malloc(128);
strcpy(path,file);
BaseType_t ret = xTaskCreatePinnedToCore(download_task, "DT", 6000, (void*)path, 3, nullptr, 1);
if (ret != pdPASS)
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "Download task failed with %d"), ret);
yield();
#endif // ESP32_DOWNLOAD_TASK
return 0;
}
#ifdef ESP32_DOWNLOAD_TASK
#ifndef DOWNLOAD_SIZE
#define DOWNLOAD_SIZE 4096
#endif // DOWNLOAD_SIZE
void download_task(void *path) {
File download_file;
WiFiClient download_Client;
char *file = (char*) path;
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "ESP32 File '%s' to download"), file);
download_file = dfsp->open(file, UFS_FILE_READ);
uint32_t flen = download_file.size();
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "len %d to download"), flen);
download_Client = Webserver->client();
Webserver->setContentLength(flen);
char attachment[100];
char *cp = fileOnly(file);
//snprintf_P(attachment, sizeof(attachment), PSTR("download file '%s' as '%s'"), file, cp);
//Webserver->sendHeader(F("X-Tasmota-Debug"), attachment);
snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"), cp);
Webserver->sendHeader(F("Content-Disposition"), attachment);
WSSend(200, CT_APP_STREAM, "");
uint8_t *buff = (uint8_t*)malloc(DOWNLOAD_SIZE);
if (buff) {
uint32_t bread;
while (download_file.available()) {
bread = download_file.read(buff, DOWNLOAD_SIZE);
uint32_t bw = download_Client.write((const char*)buff, bread);
if (!bw) { break; }
}
free(buff);
}
download_file.close();
download_Client.stop();
UfsData.download_busy = false;
vTaskDelete( NULL );
free(path);
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "esp32 sent file"));
}
#endif // ESP32_DOWNLOAD_TASK
bool UfsUploadFileOpen(const char* upload_filename) {
char npath[UFS_FILENAME_SIZE];
snprintf_P(npath, sizeof(npath), PSTR("%s/%s"), ufs_path, upload_filename);
dfsp->remove(npath);
ufs_upload_file = dfsp->open(npath, UFS_FILE_WRITE);
return (ufs_upload_file);
}
bool UfsUploadFileWrite(uint8_t *upload_buf, size_t current_size) {
if (ufs_upload_file) {
ufs_upload_file.write(upload_buf, current_size);
} else {
return false;
}
return true;
}
void UfsUploadFileClose(void) {
ufs_upload_file.close();
}
//******************************************************************************************
// File Editor
//******************************************************************************************
#ifdef GUI_EDIT_FILE
void UfsEditor(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor GET"));
char fname_input[UFS_FILENAME_SIZE];
if (Webserver->hasArg(F("file"))) {
WebGetArg(PSTR("file"), fname_input, sizeof(fname_input));
} else {
snprintf_P(fname_input, sizeof(fname_input), PSTR(D_NEW_FILE));
}
char fname[UFS_FILENAME_SIZE];
UfsFilename(fname, fname_input); // Trim spaces and add slash
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: file=%s, ffs_type=%d, TfsFileExist=%d"), fname, ffs_type, dfsp->exists(fname));
WSContentStart_P(PSTR(D_EDIT_FILE));
WSContentSendStyle();
WSContentSend_P(HTTP_FIELDSET_LEGEND, PSTR(D_EDIT_FILE));
char *bfname = fname +1;
WSContentSend_P(HTTP_EDITOR_FORM_START, bfname); // Skip leading slash
if (ffs_type && dfsp->exists(fname)) {
File fp = dfsp->open(fname, "r");
if (!fp) {
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: file open failed"));
WSContentSend_P(D_NEW_FILE);
} else {
uint8_t *buf = (uint8_t*)malloc(FILE_BUFFER_SIZE+1);
size_t filelen = fp.size();
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: file len=%d"), filelen);
while (filelen > 0) {
size_t l = fp.read(buf, FILE_BUFFER_SIZE);
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_UFS "UfsEditor: read=%d"), l);
if (l < 0) { break; }
buf[l] = '\0';
WSContentSendRaw_P( HtmlEscape((char*)buf).c_str());
filelen -= l;
}
fp.close();
free(buf);
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: read done"));
}
} else {
WSContentSend_P(D_NEW_FILE);
}
WSContentSend_P(HTTP_EDITOR_FORM_END);
folderOnly(fname);
WSContentSend_P(UFS_WEB_DIR, fname, PSTR(D_MANAGE_FILE_SYSTEM));
WSContentStop();
}
void UfsEditorUpload(void) {
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: file upload"));
if (!HttpCheckPriviledgedAccess()) { return; }
if (!Webserver->hasArg("name")) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "UfsEditor: file upload - no filename"));
WSSend(400, CT_PLAIN, F("400: Bad request - no filename"));
return;
}
char fname_input[UFS_FILENAME_SIZE];
WebGetArg(PSTR("name"), fname_input, sizeof(fname_input));
char fname[UFS_FILENAME_SIZE];
UfsFilename(fname, fname_input); // Trim spaces and add slash
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UFS "UfsEditor: file '%s'"), fname);
if (!Webserver->hasArg("content")) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "UfsEditor: file upload - no content"));
WSSend(400, CT_PLAIN, F("400: Bad request - no content"));
return;
}
String content = Webserver->arg("content");
if (!dfsp) {
Web.upload_error = 1;
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "UfsEditor: 507: no storage available"));
WSSend(507, CT_PLAIN, F("507: no storage available"));
return;
}
// recursively create folder(s)
char tmp[UFS_FILENAME_SIZE];
char folder[UFS_FILENAME_SIZE] = "";
strcpy(tmp, fname);
// zap file name off the end
folderOnly(tmp);
char *tf = strtok(tmp, "/");
while(tf){
if (*tf){
strcat(folder, "/");
strcat(folder, tf);
}
// we don;t care if it fails - it may already exist.
dfsp->mkdir(folder);
tf = strtok(nullptr, "/");
}
File fp = dfsp->open(fname, "w");
if (!fp) {
Web.upload_error = 1;
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "UfsEditor: 400: invalid file name '%s'"), fname);
WSSend(400, CT_PLAIN, F("400: bad request - invalid filename"));
return;
}
if (*content.c_str()) {
content.replace("\r\n", "\n");
content.replace("\r", "\n");
}
if (!fp.print(content)) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_UFS "UfsEditor: write error on '%s'"), fname);
}
fp.close();
// zap file name off the end
folderOnly(fname);
char t[20+UFS_FILENAME_SIZE] = "/ufsu?download=";
strcat(t, fname);
Webserver->sendHeader(F("Location"), t);
Webserver->send(303);
}
#endif // GUI_EDIT_FILE
#endif // USE_WEBSERVER
#ifdef USE_FTP
#include
FtpServer *ftpSrv;
void FTP_Server(uint32_t mode) {
if (mode > 0) {
if (ftpSrv) {
delete ftpSrv;
}
ftpSrv = new FtpServer;
if (mode == 1) {
ftpSrv->begin(USER_FTP,PW_FTP, ufsp);
} else {
ftpSrv->begin(USER_FTP,PW_FTP, ffsp);
}
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UFS "FTP Server started in mode: '%d'"), mode);
} else {
if (ftpSrv) {
delete ftpSrv;
ftpSrv = nullptr;
}
}
}
void Switch_FTP(void) {
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) {
FTP_Server(XdrvMailbox.payload);
Settings->mbflag2.FTP_Mode = XdrvMailbox.payload;
}
}
ResponseCmndNumber(Settings->mbflag2.FTP_Mode);
}
#endif // USE_FTP
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv50(uint32_t function) {
bool result = false;
switch (function) {
case FUNC_LOOP:
UfsExecuteCommandFileLoop();
#ifdef USE_FTP
if (ftpSrv) {
ftpSrv->handleFTP();
}
#endif
break;
case FUNC_NETWORK_UP:
#ifdef USE_FTP
if (Settings->mbflag2.FTP_Mode && !ftpSrv) {
FTP_Server(Settings->mbflag2.FTP_Mode);
}
#endif
break;
/*
// Moved to support_tasmota.ino for earlier init to be used by scripter
#ifdef USE_SDCARD
case FUNC_PRE_INIT:
UfsCheckSDCardInit();
break;
#endif // USE_SDCARD
*/
case FUNC_MQTT_INIT:
if (!TasmotaGlobal.no_autoexec) {
UfsExecuteCommandFile(TASM_FILE_AUTOEXEC);
}
break;
case FUNC_COMMAND:
result = DecodeCommand(kUFSCommands, kUFSCommand);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_MANAGEMENT_BUTTON:
if (ufs_type) {
if (XdrvMailbox.index) {
XdrvMailbox.index++;
} else {
WSContentSend_PD(UFS_WEB_DIR, "/", PSTR(D_MANAGE_FILE_SYSTEM));
}
}
break;
case FUNC_WEB_ADD_HANDLER:
// Webserver->on(F("/ufsd"), UfsDirectory);
// Webserver->on(F("/ufsu"), HTTP_GET, UfsDirectory);
// Webserver->on(F("/ufsu"), HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop);
Webserver->on("/ufsd", UfsDirectory);
Webserver->on("/ufsu", HTTP_GET, UfsDirectory);
//Webserver->on("/ufsu", HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop);
Webserver->on("/ufsu", HTTP_POST, HandleUploadUFSDone, HandleUploadLoop);
#ifdef GUI_EDIT_FILE
Webserver->on("/ufse", HTTP_GET, UfsEditor);
Webserver->on("/ufse", HTTP_POST, UfsEditorUpload);
#endif
break;
#endif // USE_WEBSERVER
case FUNC_ACTIVE:
result = true;
break;
}
return result;
}
#endif // USE_UFILESYS