2020-12-31 13:19:50 +00:00
/*
2021-01-06 13:41:23 +00:00
xdrv_50_filesystem . ino - unified file system for Tasmota
2020-12-31 13:19:50 +00:00
Copyright ( C ) 2020 Gerhard Mutz and Theo Arends
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2021-01-04 14:52:32 +00:00
# ifdef USE_UFILESYS
2023-07-10 09:27:20 +01:00
// saves 80 bytes of flash, makes the UI cleaner for folders containing lots of files.
// disables recursive folder listing in file UI
//#define UFILESYS_NO_RECURSE_GUI
// Enables serving of static files on /fs/
// costs 1844 bytes of flash and 40 bytes of RAM
// probably not useful on esp8266, but useful on esp32
// You could serve a whole webapp from Tas itself.
//#define UFILESYS_STATIC_SERVING
2021-01-04 14:52:32 +00:00
/*********************************************************************************************\
2021-01-06 14:28:56 +00:00
This driver adds universal file system support for
- ESP8266 ( sd card or littlefs on > 1 M devices with special linker file e . g . eagle . flash .4 m2m . ld )
( makes no sense on 1 M devices without sd card )
- ESP32 ( sd card or littlefs or sfatfile system ) .
2021-01-04 14:52:32 +00:00
The sd card chip select is the standard SDCARD_CS or when not found SDCARD_CS_PIN and initializes
the FS System Pointer ufsp which can be used by all standard file system calls .
2021-01-08 14:00:40 +00:00
The only specific call is UfsInfo ( ) which gets the total size ( 0 ) and free size ( 1 ) .
2021-01-04 14:52:32 +00:00
A button is created in the setup section to show up the file directory to download and upload files
subdirectories are supported .
2020-12-31 13:19:50 +00:00
2021-01-08 14:00:40 +00:00
Supported commands :
2020-12-31 13:19:50 +00:00
ufs fs info
ufstype get filesytem type 0 = none 1 = SD 2 = Flashfile
ufssize total size in kB
ufsfree free size in kB
2024-01-08 15:44:49 +00:00
ufsdelete
ufsrename
ufsrun
ufsServe
ftp start stop ftp server : 0 = OFF , 1 = SDC , 2 = FlashFile
2021-01-04 14:52:32 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-12-31 13:19:50 +00:00
2021-01-06 13:41:23 +00:00
# define XDRV_50 50
2020-12-31 13:19:50 +00:00
2021-01-08 14:00:40 +00:00
# define UFS_TNONE 0
# define UFS_TSDC 1
# define UFS_TFAT 2
# define UFS_TLFS 3
2024-07-30 21:24:31 +01:00
# define UFS_SDC 0
# define UFS_SDMMC 1
2021-01-20 14:43:26 +00:00
/*
// In tasmota.ino
2020-12-31 13:19:50 +00:00
# ifdef ESP8266
# include <LittleFS.h>
# include <SPI.h>
2021-01-01 10:08:48 +00:00
# ifdef USE_SDCARD
2020-12-31 13:19:50 +00:00
# include <SD.h>
# include <SDFAT.h>
2021-01-04 14:52:32 +00:00
# endif // USE_SDCARD
# endif // ESP8266
# ifdef ESP32
2020-12-31 17:05:42 +00:00
# include <LITTLEFS.h>
2021-01-01 10:08:48 +00:00
# ifdef USE_SDCARD
2020-12-31 13:19:50 +00:00
# include <SD.h>
2021-01-04 14:52:32 +00:00
# endif // USE_SDCARD
2020-12-31 13:19:50 +00:00
# include "FFat.h"
# include "FS.h"
2021-01-04 14:52:32 +00:00
# endif // ESP32
2021-01-20 14:43:26 +00:00
*/
2020-12-31 13:19:50 +00:00
2021-01-15 15:17:25 +00:00
// Global file system pointer
2020-12-31 13:19:50 +00:00
FS * ufsp ;
2021-01-15 15:17:25 +00:00
// Flash file system pointer
2021-01-06 09:51:22 +00:00
FS * ffsp ;
2021-01-15 15:17:25 +00:00
// Local pointer for file managment
2021-01-07 09:57:24 +00:00
FS * dfsp ;
2020-12-31 13:19:50 +00:00
char ufs_path [ 48 ] ;
File ufs_upload_file ;
2021-01-07 09:57:24 +00:00
uint8_t ufs_dir ;
2021-01-15 15:17:25 +00:00
// 0 = None, 1 = SD, 2 = ffat, 3 = littlefs
2020-12-31 13:19:50 +00:00
uint8_t ufs_type ;
2021-01-07 09:57:24 +00:00
uint8_t ffs_type ;
2024-07-30 21:24:31 +01:00
// sd type 0 = SD spi interface, 1 = MMC interface
uint8_t sd_type ;
2021-02-15 15:37:09 +00:00
struct {
2021-02-16 11:19:40 +00:00
char run_file [ 48 ] ;
int run_file_pos = - 1 ;
bool run_file_mutex = 0 ;
2021-02-15 15:37:09 +00:00
bool download_busy ;
} UfsData ;
2021-01-11 16:44:54 +00:00
2021-01-08 14:00:40 +00:00
/*********************************************************************************************/
2020-12-31 13:19:50 +00:00
2021-01-15 15:17:25 +00:00
// Init flash file system
2021-01-08 15:22:06 +00:00
void UfsInitOnce ( void ) {
2020-12-31 13:19:50 +00:00
ufs_type = 0 ;
2021-01-06 09:51:22 +00:00
ffsp = 0 ;
2021-01-07 09:57:24 +00:00
ufs_dir = 0 ;
2021-01-01 10:38:01 +00:00
2020-12-31 13:19:50 +00:00
# ifdef ESP8266
2021-01-08 18:28:05 +00:00
ffsp = & LittleFS ;
2020-12-31 17:05:42 +00:00
if ( ! LittleFS . begin ( ) ) {
2021-11-14 17:23:08 +00:00
ffsp = nullptr ;
2020-12-31 13:19:50 +00:00
return ;
}
2021-01-04 14:52:32 +00:00
# endif // ESP8266
2021-01-08 18:28:05 +00:00
2021-01-04 14:52:32 +00:00
# ifdef ESP32
2020-12-31 19:22:54 +00:00
// try lfs first
2021-07-18 22:14:10 +01:00
ffsp = & LittleFS ;
2023-01-20 12:19:38 +00:00
if ( ! LittleFS . begin ( true , " " ) & & ! LittleFS . begin ( true , " " , 5 , " fs_1 " ) ) { // force empty mount point to make it the fallback FS
2020-12-31 19:22:54 +00:00
// ffat is second
2021-01-08 18:28:05 +00:00
ffsp = & FFat ;
2022-04-16 11:29:01 +01:00
if ( ! FFat . begin ( true , " " ) ) {
2021-11-14 17:23:08 +00:00
ffsp = nullptr ;
2020-12-31 15:41:58 +00:00
return ;
}
2021-01-08 18:28:05 +00:00
ffs_type = UFS_TFAT ;
ufs_type = ffs_type ;
ufsp = ffsp ;
dfsp = ffsp ;
2020-12-31 13:19:50 +00:00
return ;
}
2021-01-04 14:52:32 +00:00
# endif // ESP32
2021-01-08 18:28:05 +00:00
ffs_type = UFS_TLFS ;
ufs_type = ffs_type ;
ufsp = ffsp ;
dfsp = ffsp ;
2021-01-06 14:28:56 +00:00
}
2021-02-16 11:19:40 +00:00
// Called from tasmota.ino at restart. This inits flash file only
2021-01-08 15:22:06 +00:00
void UfsInit ( void ) {
2021-02-16 11:19:40 +00:00
UfsData . run_file_pos = - 1 ;
2021-01-08 15:22:06 +00:00
UfsInitOnce ( ) ;
if ( ufs_type ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: FlashFS mounted with %d kB free " ) , UfsInfo ( 1 , 0 ) ) ;
2021-01-08 18:28:05 +00:00
}
}
2023-07-10 09:27:20 +01:00
// simple put a zero at last '/'
// modifies input string
char * folderOnly ( char * fname ) {
for ( int i = strlen ( fname ) - 1 ; i > = 0 ; i - - ) {
if ( fname [ i ] = = ' / ' ) {
fname [ i ] = 0 ;
break ;
}
fname [ i ] = 0 ;
}
if ( ! fname [ 0 ] ) {
fname [ 0 ] = ' / ' ;
fname [ 1 ] = 0 ;
}
return fname ;
}
// returns everything after last '/' of whiole input if no '/'
char * fileOnly ( char * fname ) {
char * cp = fname ;
for ( uint32_t cnt = strlen ( fname ) ; cnt > = 0 ; cnt - - ) {
if ( fname [ cnt ] = = ' / ' ) {
cp = & fname [ cnt + 1 ] ;
break ;
}
}
return cp ;
}
2021-01-08 18:28:05 +00:00
# ifdef USE_SDCARD
void UfsCheckSDCardInit ( void ) {
2022-04-19 18:17:04 +01:00
// Try SPI mode first
// SPI mode requires SDCARD_CS to be configured
if ( TasmotaGlobal . spi_enabled & & PinUsed ( GPIO_SDCARD_CS ) ) {
int8_t cs = Pin ( GPIO_SDCARD_CS ) ;
2021-10-19 07:08:55 +01:00
2023-07-29 15:50:27 +01:00
# ifdef ESP8266
2021-01-09 07:51:27 +00:00
SPI . begin ( ) ;
2023-07-29 15:50:27 +01:00
# endif // ESP8266
2021-01-09 07:51:27 +00:00
# ifdef ESP32
SPI . begin ( Pin ( GPIO_SPI_CLK ) , Pin ( GPIO_SPI_MISO ) , Pin ( GPIO_SPI_MOSI ) , - 1 ) ;
# endif // ESP32
2021-01-08 18:28:05 +00:00
if ( SD . begin ( cs ) ) {
# ifdef ESP8266
ufsp = & SDFS ;
# endif // ESP8266
# ifdef ESP32
ufsp = & SD ;
# endif // ESP32
2022-04-19 18:17:04 +01:00
2021-01-08 18:28:05 +00:00
ufs_type = UFS_TSDC ;
2024-07-30 21:24:31 +01:00
sd_type = UFS_SDC ;
2021-01-08 18:28:05 +00:00
dfsp = ufsp ;
2021-01-09 07:51:27 +00:00
if ( ffsp ) { ufs_dir = 1 ; }
2021-01-08 18:28:05 +00:00
// make sd card the global filesystem
# ifdef ESP8266
// on esp8266 sdcard info takes several seconds !!!, so we ommit it here
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: SDCard mounted " ) ) ;
2021-01-08 18:28:05 +00:00
# endif // ESP8266
# ifdef ESP32
2022-04-19 18:17:04 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: SDCard mounted (SPI mode) with %d kB free " ) , UfsInfo ( 1 , 0 ) ) ;
2021-01-08 18:28:05 +00:00
# endif // ESP32
}
2021-01-08 15:22:06 +00:00
}
2022-04-19 19:03:14 +01:00
# if defined(ESP32) && defined(SOC_SDMMC_HOST_SUPPORTED) // ESP32 and SDMMC supported (not Esp32C3)
2022-04-19 18:17:04 +01:00
// check if SDIO is configured
else if ( PinUsed ( GPIO_SDIO_CLK ) & & PinUsed ( GPIO_SDIO_CMD ) & & PinUsed ( GPIO_SDIO_D0 ) ) {
int32_t sdio_cmd = Pin ( GPIO_SDIO_CMD ) ;
int32_t sdio_clk = Pin ( GPIO_SDIO_CLK ) ;
int32_t sdio_d0 = Pin ( GPIO_SDIO_D0 ) ;
int32_t sdio_d1 = Pin ( GPIO_SDIO_D1 ) ;
int32_t sdio_d2 = Pin ( GPIO_SDIO_D2 ) ;
int32_t sdio_d3 = Pin ( GPIO_SDIO_D3 ) ;
bool bit_4_mode = ( sdio_d1 > = 0 ) & & ( sdio_d2 > = 0 ) & & ( sdio_d3 > = 0 ) ; // enable 4-bit mode if possible
if ( bit_4_mode ) {
// AddLog(LOG_LEVEL_DEBUG, "UFS: trying SDIO 4-bit clk=%i cmd=%i d0=%i d1=%i d2=%i d3=%i", sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3);
SD_MMC . setPins ( sdio_clk , sdio_cmd , sdio_d0 , sdio_d1 , sdio_d2 , sdio_d3 ) ;
} else {
// AddLog(LOG_LEVEL_DEBUG, "UFS: trying SDIO 1-bit clk=%i cmd=%i d0=%i", sdio_clk, sdio_cmd, sdio_d0);
SD_MMC . setPins ( sdio_clk , sdio_cmd , sdio_d0 ) ;
}
if ( SD_MMC . begin ( " /sd " , ! bit_4_mode /*mode 1 bit*/ , false /*format_if_failed*/ ) ) { // mount under "/sd" to be consistent with SD SPI
ufsp = & SD_MMC ;
ufs_type = UFS_TSDC ;
2024-07-30 21:24:31 +01:00
sd_type = UFS_SDMMC ;
2022-04-19 18:17:04 +01:00
dfsp = ufsp ;
if ( ffsp ) { ufs_dir = 1 ; }
// make sd card the global filesystem
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: SDCard mounted (SDIO %i-bit) with %d kB free " ) , bit_4_mode ? 4 : 1 , UfsInfo ( 1 , 0 ) ) ;
}
}
# endif
2021-01-08 15:22:06 +00:00
}
2021-01-08 18:28:05 +00:00
# endif // USE_SDCARD
2021-01-08 15:22:06 +00:00
2021-01-08 14:00:40 +00:00
uint32_t UfsInfo ( uint32_t sel , uint32_t type ) {
2021-01-10 18:42:33 +00:00
uint64_t result = 0 ;
2021-01-07 09:57:24 +00:00
FS * ifsp = ufsp ;
uint8_t itype = ufs_type ;
if ( type ) {
ifsp = ffsp ;
itype = ffs_type ;
}
2021-01-04 14:52:32 +00:00
2021-01-01 15:48:52 +00:00
# ifdef ESP8266
2021-01-04 14:52:32 +00:00
FSInfo64 fsinfo ;
# endif // ESP8266
2020-12-31 13:19:50 +00:00
2021-01-07 09:57:24 +00:00
switch ( itype ) {
2020-12-31 13:19:50 +00:00
case UFS_TSDC :
2021-01-01 10:08:48 +00:00
# ifdef USE_SDCARD
2021-01-04 14:52:32 +00:00
# ifdef ESP8266
2021-01-07 09:57:24 +00:00
ifsp - > info64 ( fsinfo ) ;
2021-01-01 15:48:52 +00:00
if ( sel = = 0 ) {
result = fsinfo . totalBytes ;
} else {
result = ( fsinfo . totalBytes - fsinfo . usedBytes ) ;
}
2021-01-04 14:52:32 +00:00
# endif // ESP8266
# ifdef ESP32
2024-07-30 21:24:31 +01:00
# ifdef SOC_SDMMC_HOST_SUPPORTED
if ( sd_type = = UFS_SDC ) {
if ( sel = = 0 ) {
result = SD . totalBytes ( ) ;
} else {
result = ( SD . totalBytes ( ) - SD . usedBytes ( ) ) ;
}
} else if ( sd_type = = UFS_SDMMC ) {
if ( sel = = 0 ) {
result = SD_MMC . totalBytes ( ) ;
} else {
result = ( SD_MMC . totalBytes ( ) - SD_MMC . usedBytes ( ) ) ;
}
} else {
result = 0 ;
}
# else
2021-01-04 14:52:32 +00:00
if ( sel = = 0 ) {
result = SD . totalBytes ( ) ;
} else {
result = ( SD . totalBytes ( ) - SD . usedBytes ( ) ) ;
}
2024-07-30 21:24:31 +01:00
# endif // SOC_SDMMC_HOST_SUPPORTED
2021-01-08 14:00:40 +00:00
# endif // ESP32
# endif // USE_SDCARD
2020-12-31 13:19:50 +00:00
break ;
2020-12-31 17:05:42 +00:00
case UFS_TLFS :
2020-12-31 13:19:50 +00:00
# ifdef ESP8266
2021-01-07 09:57:24 +00:00
ifsp - > info64 ( fsinfo ) ;
2020-12-31 13:19:50 +00:00
if ( sel = = 0 ) {
result = fsinfo . totalBytes ;
} else {
result = ( fsinfo . totalBytes - fsinfo . usedBytes ) ;
}
2021-01-04 14:52:32 +00:00
# endif // ESP8266
# ifdef ESP32
2020-12-31 17:05:42 +00:00
if ( sel = = 0 ) {
2021-07-18 22:14:10 +01:00
result = LittleFS . totalBytes ( ) ;
2020-12-31 17:05:42 +00:00
} else {
2021-07-18 22:14:10 +01:00
result = LittleFS . totalBytes ( ) - LittleFS . usedBytes ( ) ;
2020-12-31 17:05:42 +00:00
}
2021-01-08 14:00:40 +00:00
# endif // ESP32
2020-12-31 17:05:42 +00:00
break ;
case UFS_TFAT :
# ifdef ESP32
2020-12-31 13:19:50 +00:00
if ( sel = = 0 ) {
result = FFat . totalBytes ( ) ;
} else {
result = FFat . freeBytes ( ) ;
}
2021-01-08 14:00:40 +00:00
# endif // ESP32
2020-12-31 13:19:50 +00:00
break ;
2020-12-31 17:05:42 +00:00
2020-12-31 13:19:50 +00:00
}
2021-01-08 14:00:40 +00:00
return result / 1024 ;
2020-12-31 13:19:50 +00:00
}
2023-05-07 16:25:18 +01:00
uint32_t UfsSize ( void ) {
return UfsInfo ( 0 , ufs_dir = = 2 ? 1 : 0 ) ;
}
uint32_t UfsFree ( void ) {
return UfsInfo ( 1 , ufs_dir = = 2 ? 1 : 0 ) ;
}
2020-12-31 13:19:50 +00:00
# if USE_LONG_FILE_NAMES>0
# undef REJCMPL
# define REJCMPL 6
# else
# undef REJCMPL
# define REJCMPL 8
# endif
2021-01-08 14:00:40 +00:00
uint8_t UfsReject ( char * name ) {
2020-12-31 13:19:50 +00:00
char * lcp = strrchr ( name , ' / ' ) ;
if ( lcp ) {
name = lcp + 1 ;
}
2021-01-04 14:52:32 +00:00
while ( * name = = ' / ' ) { name + + ; }
if ( * name = = ' . ' ) { return 1 ; }
2020-12-31 13:19:50 +00:00
2021-01-04 14:52:32 +00:00
if ( ! strncasecmp ( name , " SPOTLI~1 " , REJCMPL ) ) { return 1 ; }
if ( ! strncasecmp ( name , " TRASHE~1 " , REJCMPL ) ) { return 1 ; }
if ( ! strncasecmp ( name , " FSEVEN~1 " , REJCMPL ) ) { return 1 ; }
if ( ! strncasecmp ( name , " SYSTEM~1 " , REJCMPL ) ) { return 1 ; }
if ( ! strncasecmp ( name , " System Volume " , 13 ) ) { return 1 ; }
2020-12-31 13:19:50 +00:00
return 0 ;
}
2021-01-08 14:00:40 +00:00
/*********************************************************************************************\
* Tfs low level functions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool TfsFileExists ( const char * fname ) {
2021-01-15 15:17:25 +00:00
if ( ! ffs_type ) { return false ; }
2021-01-08 14:00:40 +00:00
bool yes = ffsp - > exists ( fname ) ;
if ( ! yes ) {
2024-03-04 10:58:46 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " TFS: File '%s' not found " ) , fname + 1 ) ; // Skip leading slash
2021-01-08 14:00:40 +00:00
}
return yes ;
}
2023-01-04 11:00:09 +00:00
size_t TfsFileSize ( const char * fname ) {
if ( ! ffs_type ) { return 0 ; }
File file = ffsp - > open ( fname , " r " ) ;
if ( ! file ) { return 0 ; }
size_t flen = file . size ( ) ;
file . close ( ) ;
return flen ;
}
2021-01-08 14:00:40 +00:00
bool TfsSaveFile ( const char * fname , const uint8_t * buf , uint32_t len ) {
2021-01-15 15:17:25 +00:00
if ( ! ffs_type ) { return false ; }
2021-12-17 17:01:27 +00:00
# ifdef USE_WEBCAM
WcInterrupt ( 0 ) ; // Stop stream if active to fix TG1WDT_SYS_RESET
# endif
bool result = false ;
2021-01-08 14:00:40 +00:00
File file = ffsp - > open ( fname , " w " ) ;
if ( ! file ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " TFS: Save failed " ) ) ;
2021-12-17 17:01:27 +00:00
} else {
// This will timeout on ESP32-webcam
// But now solved with WcInterrupt(0) in support_esp.ino
file . write ( buf , len ) ;
/*
// This will still timeout on ESP32-webcam when wcresolution 10
uint32_t count = len / 512 ;
uint32_t chunk = len / count ;
for ( uint32_t i = 0 ; i < count ; i + + ) {
file . write ( buf + ( i * chunk ) , chunk ) ;
// do actually wait a little to allow ESP32 tasks to tick
// fixes task timeout in ESP32Solo1 style unicore code and webcam.
delay ( 10 ) ;
OsWatchLoop ( ) ;
}
uint32_t left = len % count ;
if ( left ) {
file . write ( buf + ( count * chunk ) , left ) ;
}
*/
file . close ( ) ;
result = true ;
2021-12-12 17:04:46 +00:00
}
2021-12-17 17:01:27 +00:00
# ifdef USE_WEBCAM
WcInterrupt ( 1 ) ;
# endif
return result ;
2021-01-08 14:00:40 +00:00
}
bool TfsInitFile ( const char * fname , uint32_t len , uint8_t init_value ) {
2021-01-15 15:17:25 +00:00
if ( ! ffs_type ) { return false ; }
2021-01-08 14:00:40 +00:00
File file = ffsp - > open ( fname , " w " ) ;
if ( ! file ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " TFS: Erase failed " ) ) ;
2021-01-08 14:00:40 +00:00
return false ;
}
for ( uint32_t i = 0 ; i < len ; i + + ) {
file . write ( & init_value , 1 ) ;
}
file . close ( ) ;
return true ;
}
bool TfsLoadFile ( const char * fname , uint8_t * buf , uint32_t len ) {
2021-01-15 15:17:25 +00:00
if ( ! ffs_type ) { return false ; }
2021-01-08 14:00:40 +00:00
File file = ffsp - > open ( fname , " r " ) ;
if ( ! file ) {
2024-03-04 10:58:46 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " TFS: File '%s' not found " ) , fname + 1 ) ; // Skip leading slash
2021-01-08 14:00:40 +00:00
return false ;
}
2022-04-03 17:16:08 +01:00
2022-01-09 17:24:39 +00:00
size_t flen = file . size ( ) ;
2023-01-04 11:00:09 +00:00
if ( len > flen ) { len = flen ; } // Adjust requested length to smaller file length
2021-01-08 14:00:40 +00:00
file . read ( buf , len ) ;
file . close ( ) ;
return true ;
}
2023-01-04 11:00:09 +00:00
String TfsLoadString ( const char * fname ) {
// Use a reasonable amount of stack space considering 4k/8k available on ESP8266/ESP32 and manageable string length
char buf [ 2048 ] = { 0 } ; // Prepare empty string of max 2047 characters on stack
TfsLoadFile ( fname , ( uint8_t * ) buf , 2047 ) ; // Leave last position as end of string ('\0')
return String ( buf ) ; // Received string or empty on error
}
2021-01-08 15:22:06 +00:00
bool TfsDeleteFile ( const char * fname ) {
2021-01-15 15:17:25 +00:00
if ( ! ffs_type ) { return false ; }
2021-01-08 15:22:06 +00:00
if ( ! ffsp - > remove ( fname ) ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " TFS: Delete failed " ) ) ;
2021-01-08 15:22:06 +00:00
return false ;
}
return true ;
}
2021-02-15 16:29:31 +00:00
bool TfsRenameFile ( const char * fname1 , const char * fname2 ) {
if ( ! ffs_type ) { return false ; }
if ( ! ffsp - > rename ( fname1 , fname2 ) ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( " TFS: Rename failed " ) ) ;
return false ;
}
return true ;
}
2021-02-15 15:37:09 +00:00
/*********************************************************************************************\
2021-02-16 12:00:10 +00:00
* File command execute support
2021-02-15 15:37:09 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-02-16 15:21:46 +00:00
bool UfsExecuteCommandFileReady ( void ) {
2021-02-16 14:54:53 +00:00
return ( UfsData . run_file_pos < 0 ) ; // Check file ready to disable concurrency
2021-02-16 12:00:10 +00:00
}
2021-02-16 11:19:40 +00:00
2021-02-16 15:21:46 +00:00
void UfsExecuteCommandFileLoop ( void ) {
if ( UfsExecuteCommandFileReady ( ) | | ! ffs_type ) { return ; }
2021-02-15 15:37:09 +00:00
2021-04-14 15:09:34 +01:00
if ( BACKLOG_EMPTY & & strlen ( UfsData . run_file ) & & ! UfsData . run_file_mutex ) {
2021-02-16 11:19:40 +00:00
File file = ffsp - > open ( UfsData . run_file , " r " ) ;
2021-02-16 14:25:16 +00:00
if ( ! file | | ! file . seek ( UfsData . run_file_pos ) ) {
2021-02-16 14:54:53 +00:00
UfsData . run_file_pos = - 1 ; // Signal file ready
2021-02-16 14:25:16 +00:00
return ;
}
2021-02-16 11:19:40 +00:00
UfsData . run_file_mutex = true ;
char cmd_line [ 512 ] ;
2021-02-16 14:25:16 +00:00
cmd_line [ 0 ] = ' \0 ' ; // Clear in case of re-entry
2021-02-15 15:37:09 +00:00
while ( file . available ( ) ) {
2021-02-16 11:19:40 +00:00
uint16_t index = 0 ;
2021-02-16 14:25:16 +00:00
bool comment = false ;
2021-02-16 11:19:40 +00:00
while ( file . available ( ) ) {
uint8_t buf [ 1 ] ;
file . read ( buf , 1 ) ;
if ( ( buf [ 0 ] = = ' \n ' ) | | ( buf [ 0 ] = = ' \r ' ) ) {
2021-02-16 14:25:16 +00:00
break ; // End of command with linefeed or carriage return
2021-02-16 11:19:40 +00:00
}
2021-04-14 15:09:34 +01:00
// else if (index && !comment && (buf[0] == ';')) {
// break; // End of command on multi command line
// }
2021-02-16 11:19:40 +00:00
else if ( ( 0 = = index ) & & isspace ( buf [ 0 ] ) ) {
2021-02-16 14:25:16 +00:00
// Skip leading spaces (' ','\t','\n','\v','\f','\r')
}
else if ( ( 0 = = index ) & & ( buf [ 0 ] = = ' ; ' ) ) {
comment = true ; // Ignore comment lines until linefeed or carriage return
2021-02-16 11:19:40 +00:00
}
2021-02-16 14:25:16 +00:00
else if ( ! comment & & ( index < sizeof ( cmd_line ) - 2 ) ) {
cmd_line [ index + + ] = buf [ 0 ] ; // Build command
2021-02-16 11:19:40 +00:00
}
2021-02-15 17:01:02 +00:00
}
2021-02-16 14:25:16 +00:00
if ( ( index > 0 ) & & ( index < sizeof ( cmd_line ) - 1 ) ) {
cmd_line [ index ] = ' \0 ' ; // Valid command received
2021-02-16 11:19:40 +00:00
break ;
2021-02-15 15:37:09 +00:00
}
}
2021-02-16 11:19:40 +00:00
UfsData . run_file_pos = ( file . available ( ) ) ? file . position ( ) : - 1 ;
file . close ( ) ;
if ( strlen ( cmd_line ) ) {
ExecuteCommand ( cmd_line , SRC_FILE ) ;
2021-02-15 15:37:09 +00:00
}
2021-02-16 11:19:40 +00:00
UfsData . run_file_mutex = false ;
2021-02-15 15:37:09 +00:00
}
2021-02-16 11:19:40 +00:00
}
2021-02-15 15:37:09 +00:00
2021-02-16 14:54:53 +00:00
bool UfsExecuteCommandFile ( const char * fname ) {
// Check for non-concurrency and file existance
2021-02-16 15:21:46 +00:00
if ( UfsExecuteCommandFileReady ( ) & & TfsFileExists ( fname ) ) {
2021-02-16 14:54:53 +00:00
snprintf ( UfsData . run_file , sizeof ( UfsData . run_file ) , fname ) ;
UfsData . run_file_pos = 0 ; // Signal start of file
return true ;
2021-02-16 11:19:40 +00:00
}
2021-02-16 14:54:53 +00:00
return false ;
2021-02-15 15:37:09 +00:00
}
2024-02-07 21:55:39 +00:00
/*********************************************************************************************\
* File JSON settings support using file / . drvset000
*
* { " UserSet1 " : { " Param1 " : 123 , " Param2 " : " Text1 " } , " UserSet2 " : { " Param1 " : 123 , " Param2 " : " Text2 " } , " UserSet3 " : { " Param1 " : 123 , " Param2 " : " Text3 " } }
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool _UfsJsonSettingsUpdate ( const char * data ) {
// Delete: Input UserSet2
// Append: Input {"UserSet2":{"Param1":123,"Param2":"Text2"}}
char filename [ 14 ] ;
snprintf_P ( filename , sizeof ( filename ) , PSTR ( TASM_FILE_DRIVER ) , 0 ) ; // /.drvset000
if ( ! TfsFileExists ( filename ) ) { return false ; } // Error - File not found
char bfname [ 14 ] ;
strcpy_P ( bfname , PSTR ( " /settmp " ) ) ;
File ofile = ffsp - > open ( bfname , " w " ) ;
if ( ! ofile ) { return false ; } // Error - unable to open temporary file
File ifile = ffsp - > open ( filename , " r " ) ;
if ( ! ifile ) {
ofile . close ( ) ;
ffsp - > remove ( bfname ) ;
return false ; // Error - unable to open settings file
}
bool append = false ;
char * key = ( char * ) data ;
char key_pos [ 32 ] ; // Max key length
char * p = strchr ( data , ' " ' ) ;
if ( p ) {
append = true ;
char * q = strchr ( + + p , ' " ' ) ;
if ( ! q ) { return false ; } // Error - No valid key provided in data to append
uint32_t len = ( uint32_t ) q - ( uint32_t ) p ;
memcpy ( key_pos , p , len ) ;
key_pos [ len ] = ' \0 ' ; // key = UserSet2
key = key_pos ;
}
char buffer [ 32 ] ; // Max key length
uint8_t buf [ 1 ] ;
uint32_t index = 0 ;
uint32_t bracket_count = 0 ;
int entries = 0 ;
bool quote = false ;
bool mine = false ;
bool deleted = false ;
while ( ifile . available ( ) ) { // Process file
ifile . read ( buf , 1 ) ;
if ( bracket_count > 1 ) { // Copy or skip old data
if ( ! mine ) {
ofile . write ( buf , 1 ) ; // Copy data
}
if ( buf [ 0 ] = = ' } ' ) {
bracket_count - - ;
}
2024-03-10 11:04:57 +00:00
else if ( buf [ 0 ] = = ' { ' ) { // Next bracket
bracket_count + + ;
}
2024-02-07 21:55:39 +00:00
} else {
if ( buf [ 0 ] = = ' } ' ) { // Last bracket
break ; // End of file
}
else if ( buf [ 0 ] = = ' { ' ) {
bracket_count + + ;
if ( bracket_count > 1 ) { // Skip first bracket
entries + + ;
}
}
else if ( buf [ 0 ] = = ' " ' ) {
quote ^ = 1 ;
if ( quote ) {
index = 0 ;
} else {
buffer [ index ] = ' \0 ' ; // End of key name
mine = ( ! strcasecmp ( buffer , key ) ) ;
if ( mine ) {
entries - - ; // Skip old data
deleted = true ;
} else {
ofile . write ( ( entries ) ? ( uint8_t * ) " , \" " : ( uint8_t * ) " { \" " , 2 ) ;
ofile . write ( ( uint8_t * ) buffer , strlen ( buffer ) ) ;
ofile . write ( ( uint8_t * ) " \" :{ " , 3 ) ;
}
}
}
else {
buffer [ index + + ] = buf [ 0 ] ; // Add key name
if ( index > sizeof ( buffer ) - 2 ) {
break ; // Key name too long
}
}
}
}
ifile . close ( ) ;
if ( append ) {
// Append new data
ofile . write ( ( entries ) ? ( uint8_t * ) " , " : ( uint8_t * ) " { " , 1 ) ;
ofile . write ( ( uint8_t * ) data + 1 , strlen ( data ) - 1 ) ;
} else {
// Delete data
if ( entries ) {
ofile . write ( ( uint8_t * ) " } " , 1 ) ;
}
}
ofile . close ( ) ;
if ( index > sizeof ( buffer ) - 2 ) {
// No changes
ffsp - > remove ( bfname ) ;
return false ; // Error - Key name too long
}
ffsp - > remove ( filename ) ;
ffsp - > rename ( bfname , filename ) ;
if ( ! append ) {
// Delete data
if ( ! entries ) {
ffsp - > remove ( filename ) ;
}
return deleted ; // State - 0 = Not found, 1 = Deleted
}
return true ; // State - Append success
}
bool UfsJsonSettingsDelete ( const char * key ) {
// Delete: Input UserSet2
// Output 0 = Not found, 1 = Deleted
return _UfsJsonSettingsUpdate ( key ) ; // State - 0 = Not found, 1 = Deleted
}
bool UfsJsonSettingsWrite ( const char * data ) {
// Add new UserSet replacing present UserSet
// Input {"UserSet2":{"Param1":123,"Param2":"Text2"}}
// Output 0 = Error, 1 = Append success
2024-02-08 13:42:58 +00:00
String json = data ;
JsonParser parser ( ( char * ) json . c_str ( ) ) ;
JsonParserObject root = parser . getRootObject ( ) ;
if ( ! root ) { return false ; } // Error - invalid JSON
2024-02-07 21:55:39 +00:00
char filename [ 14 ] ;
snprintf_P ( filename , sizeof ( filename ) , PSTR ( TASM_FILE_DRIVER ) , 0 ) ; // /.drvset000
if ( ! TfsFileExists ( filename ) ) {
2024-03-10 11:04:57 +00:00
return TfsSaveFile ( filename , ( uint8_t * ) data , strlen ( data ) ) ;
2024-02-07 21:55:39 +00:00
}
return _UfsJsonSettingsUpdate ( data ) ; // State - 0 = Error, 1 = Append success
}
String UfsJsonSettingsRead ( const char * key ) {
// Read: Input UserSet2
2024-03-10 11:04:57 +00:00
// Output "" = Error, {"Param1":123,"Param2":"Text2","Param3":[{"Param3a":1},{"Param3b":1}]} = Data
2024-02-07 21:55:39 +00:00
String data = " " ;
char filename [ 14 ] ;
snprintf_P ( filename , sizeof ( filename ) , PSTR ( TASM_FILE_DRIVER ) , 0 ) ; // /.drvset000
if ( ! TfsFileExists ( filename ) ) { return data ; } // Error - File not found
File file = ffsp - > open ( filename , " r " ) ;
if ( ! file ) { return data ; } // Error - unable to open settings file
Trim ( ( char * ) key ) ;
char buffer [ 128 ] ;
uint8_t buf [ 1 ] = { 0 } ;
uint32_t index = 0 ;
uint32_t bracket_count = 0 ;
bool quote = false ;
bool mine = false ;
while ( file . available ( ) ) { // Process file
file . read ( buf , 1 ) ;
if ( bracket_count > 1 ) { // Build JSON
if ( mine ) {
buffer [ index + + ] = buf [ 0 ] ; // Add key data
if ( index > sizeof ( buffer ) - 2 ) {
buffer [ index ] = ' \0 ' ;
data + = buffer ; // Add buffer to result
index = 0 ;
}
}
if ( buf [ 0 ] = = ' } ' ) {
bracket_count - - ;
if ( 1 = = bracket_count ) {
if ( mine ) {
break ; // End of key data
} else {
index = 0 ; // End of data which is not mine
}
}
}
2024-03-10 11:04:57 +00:00
else if ( buf [ 0 ] = = ' { ' ) { // Next bracket
bracket_count + + ;
}
2024-02-07 21:55:39 +00:00
} else {
if ( buf [ 0 ] = = ' } ' ) { // Last bracket
index = 0 ;
break ; // End of file - key not found
}
else if ( buf [ 0 ] = = ' { ' ) {
bracket_count + + ;
if ( bracket_count > 1 ) { // Skip first bracket
index = 0 ;
buffer [ index + + ] = buf [ 0 ] ; // Start of key data
}
}
else if ( buf [ 0 ] = = ' " ' ) {
quote ^ = 1 ;
if ( quote ) {
index = 0 ;
} else {
buffer [ index ] = ' \0 ' ; // End of key name
mine = ( ! strcasecmp ( buffer , key ) ) ;
}
}
else {
buffer [ index + + ] = buf [ 0 ] ; // Add key name
if ( index > sizeof ( buffer ) - 2 ) {
index = 0 ;
break ; // Key name too long
}
}
}
}
file . close ( ) ;
buffer [ index ] = ' \0 ' ;
data + = buffer ;
return data ; // State - "" = Error, {"Param1":123,"Param2":"Text2"} = Data
}
2021-01-08 14:00:40 +00:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-02-19 17:35:47 +00:00
const int UFS_FILENAME_SIZE = 48 ;
char * UfsFilename ( char * fname , char * fname_in ) {
fname_in = Trim ( fname_in ) ; // Remove possible leading spaces
snprintf_P ( fname , UFS_FILENAME_SIZE , PSTR ( " %s%s " ) , ( ' / ' = = fname_in [ 0 ] ) ? " " : " / " , fname_in ) ;
return fname ;
}
2021-01-08 15:22:06 +00:00
const char kUFSCommands [ ] PROGMEM = " Ufs| " // Prefix
2023-07-10 09:27:20 +01:00
" |Type|Size|Free|Delete|Rename|Run "
# ifdef UFILESYS_STATIC_SERVING
" |Serve "
2024-01-08 15:44:49 +00:00
# endif
# ifdef USE_FTP
" |FTP "
2023-07-10 09:27:20 +01:00
# endif
;
2020-12-31 13:19:50 +00:00
void ( * const kUFSCommand [ ] ) ( void ) PROGMEM = {
2023-07-10 09:27:20 +01:00
& UFSInfo , & UFSType , & UFSSize , & UFSFree , & UFSDelete , & UFSRename , & UFSRun
# ifdef UFILESYS_STATIC_SERVING
, & UFSServe
2024-01-08 15:44:49 +00:00
# endif
# ifdef USE_FTP
, & Switch_FTP
# endif
2023-07-10 09:27:20 +01:00
} ;
2020-12-31 13:19:50 +00:00
2021-01-08 14:00:40 +00:00
void UFSInfo ( void ) {
2021-01-15 15:17:25 +00:00
Response_P ( PSTR ( " { \" Ufs \" :{ \" Type \" :%d, \" Size \" :%d, \" Free \" :%d} " ) , ufs_type , UfsInfo ( 0 , 0 ) , UfsInfo ( 1 , 0 ) ) ;
if ( ffs_type & & ( ffs_type ! = ufs_type ) ) {
ResponseAppend_P ( PSTR ( " ,{ \" Type \" :%d, \" Size \" :%d, \" Free \" :%d} " ) , ffs_type , UfsInfo ( 0 , 1 ) , UfsInfo ( 1 , 1 ) ) ;
}
ResponseJsonEnd ( ) ;
2020-12-31 13:19:50 +00:00
}
2021-01-04 14:52:32 +00:00
2021-01-08 14:00:40 +00:00
void UFSType ( void ) {
2021-01-15 15:17:25 +00:00
if ( ffs_type & & ( ffs_type ! = ufs_type ) ) {
Response_P ( PSTR ( " { \" %s \" :[%d,%d]} " ) , XdrvMailbox . command , ufs_type , ffs_type ) ;
} else {
ResponseCmndNumber ( ufs_type ) ;
}
2020-12-31 13:19:50 +00:00
}
2021-01-08 15:22:06 +00:00
2021-01-08 14:00:40 +00:00
void UFSSize ( void ) {
2021-01-15 15:17:25 +00:00
if ( ffs_type & & ( ffs_type ! = ufs_type ) ) {
Response_P ( PSTR ( " { \" %s \" :[%d,%d]} " ) , XdrvMailbox . command , UfsInfo ( 0 , 0 ) , UfsInfo ( 0 , 1 ) ) ;
} else {
ResponseCmndNumber ( UfsInfo ( 0 , 0 ) ) ;
}
2020-12-31 13:19:50 +00:00
}
2021-01-08 15:22:06 +00:00
2021-01-08 14:00:40 +00:00
void UFSFree ( void ) {
2021-01-15 15:17:25 +00:00
if ( ffs_type & & ( ffs_type ! = ufs_type ) ) {
Response_P ( PSTR ( " { \" %s \" :[%d,%d]} " ) , XdrvMailbox . command , UfsInfo ( 1 , 0 ) , UfsInfo ( 1 , 1 ) ) ;
} else {
ResponseCmndNumber ( UfsInfo ( 1 , 0 ) ) ;
}
2020-12-31 13:19:50 +00:00
}
2021-01-08 15:22:06 +00:00
void UFSDelete ( void ) {
2021-01-15 15:17:25 +00:00
// UfsDelete sdcard or flashfs file if only one of them available
// UfsDelete2 flashfs file if available
2021-01-08 15:22:06 +00:00
if ( XdrvMailbox . data_len > 0 ) {
2021-02-19 17:35:47 +00:00
char fname [ UFS_FILENAME_SIZE ] ;
UfsFilename ( fname , XdrvMailbox . data ) ;
2021-01-15 15:17:25 +00:00
bool result = false ;
if ( ffs_type & & ( ffs_type ! = ufs_type ) & & ( 2 = = XdrvMailbox . index ) ) {
2021-02-19 17:35:47 +00:00
result = TfsDeleteFile ( fname ) ;
2021-01-15 15:17:25 +00:00
} else {
2021-02-19 17:35:47 +00:00
result = ( ufs_type & & ufsp - > remove ( fname ) ) ;
2021-01-15 15:17:25 +00:00
}
if ( ! result ) {
2021-02-16 15:21:46 +00:00
ResponseCmndFailed ( ) ;
2021-01-08 15:22:06 +00:00
} else {
ResponseCmndDone ( ) ;
2021-02-15 16:29:31 +00:00
}
}
}
void UFSRename ( void ) {
// UfsRename sdcard or flashfs file if only one of them available
// UfsRename2 flashfs file if available
if ( XdrvMailbox . data_len > 0 ) {
bool result = false ;
2021-02-19 17:35:47 +00:00
char * fname1 = strtok ( XdrvMailbox . data , " , " ) ;
char * fname2 = strtok ( nullptr , " , " ) ;
2021-02-15 16:29:31 +00:00
if ( fname1 & & fname2 ) {
2021-02-19 17:35:47 +00:00
char fname_old [ UFS_FILENAME_SIZE ] ;
UfsFilename ( fname_old , fname1 ) ;
char fname_new [ UFS_FILENAME_SIZE ] ;
UfsFilename ( fname_new , fname2 ) ;
2021-02-15 16:29:31 +00:00
if ( ffs_type & & ( ffs_type ! = ufs_type ) & & ( 2 = = XdrvMailbox . index ) ) {
2021-02-19 17:35:47 +00:00
result = TfsRenameFile ( fname_old , fname_new ) ;
2021-02-15 16:29:31 +00:00
} else {
2021-02-19 17:35:47 +00:00
result = ( ufs_type & & ufsp - > rename ( fname_old , fname_new ) ) ;
2021-02-15 16:29:31 +00:00
}
}
if ( ! result ) {
2021-02-16 15:21:46 +00:00
ResponseCmndFailed ( ) ;
2021-02-15 16:29:31 +00:00
} else {
ResponseCmndDone ( ) ;
2021-01-08 15:22:06 +00:00
}
}
}
2023-07-10 09:27:20 +01:00
# ifdef UFILESYS_STATIC_SERVING
/*
* Serves a filesystem folder at a web url .
* NOTE - this is expensive on flash - > + 2.5 kbytes .
* like " UFSServe <fs path>,<url>[,<noauth>] "
* e . g . " UFSServe /sd/,/mysdcard/,1 " - will serve the / sd / fs folder as https : //<ip>/mysdcard/ with no auth required
* e . g . " UFSServe /www/,/ " - will serve the / www / fs folder as https : //<ip>/ with auth required if TAS has a password setup
* < noauth > 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"
2024-05-24 21:02:09 +01:00
//#define SERVING_DEBUG
2023-07-10 09:27:20 +01:00
// class to allow us to request auth when required.
// StaticRequestHandler is in the above header
class StaticRequestHandlerAuth : public StaticRequestHandler {
public :
2024-05-24 21:02:09 +01:00
StaticRequestHandlerAuth ( FS & fs , const char * path , const char * uri , const char * cache_header , bool requireAuth ) :
2023-07-10 09:27:20 +01:00
StaticRequestHandler ( fs , path , uri , cache_header )
{
2024-05-24 21:02:09 +01:00
_requireAuth = requireAuth ;
2023-07-10 09:27:20 +01:00
}
2024-05-24 21:02:09 +01:00
bool _requireAuth ;
2023-07-10 09:27:20 +01:00
// 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 ;
2024-05-24 21:02:09 +01:00
//log_v("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
# ifdef SERVING_DEBUG
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: ::handle: request=%s _uri=%s " ) , requestUri . c_str ( ) , _uri . c_str ( ) ) ;
# endif
2023-07-10 09:27:20 +01:00
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 ) ;
}
2024-05-24 21:02:09 +01:00
# ifdef SERVING_DEBUG
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: ::handle: path=%s, isFile=%d " ) , path . c_str ( ) , _isFile ) ;
# endif
2023-07-10 09:27:20 +01:00
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 " ) ;
2024-05-24 21:02:09 +01:00
if ( ! f | | ! f . available ( ) ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: ::handler missing file? " ) ) ;
2023-07-10 09:27:20 +01:00
return false ;
2024-05-24 21:02:09 +01:00
}
# ifdef SERVING_DEBUG
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: ::handler file open %d " ) , f . available ( ) ) ;
# endif
if ( _requireAuth & & ! WebAuthenticate ( ) ) {
# ifdef SERVING_DEBUG
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: serv of %s denied " ) , requestUri . c_str ( ) ) ;
2024-05-24 21:02:09 +01:00
# endif
2023-07-10 09:27:20 +01:00
server . requestAuthentication ( ) ;
return true ;
}
if ( _cache_header . length ( ) ! = 0 )
server . sendHeader ( " Cache-Control " , _cache_header ) ;
2024-05-24 21:02:09 +01:00
# ifdef SERVING_DEBUG
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " 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("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 ( " UFS: ::handler sent %d/%d " ) , cnt , flen ) ;
# endif
if ( cnt ! = flen ) {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " 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 ( " UFS: ::handler done " ) ) ;
# endif
2023-07-10 09:27:20 +01:00
return true ;
}
} ;
void UFSServe ( void ) {
2024-05-22 15:26:26 +01:00
bool result = false ;
2023-07-10 09:27:20 +01:00
if ( XdrvMailbox . data_len > 0 ) {
char * fpath = strtok ( XdrvMailbox . data , " , " ) ;
char * url = strtok ( nullptr , " , " ) ;
char * noauth = strtok ( nullptr , " , " ) ;
if ( fpath & & url ) {
2024-05-22 15:26:26 +01:00
if ( Webserver ) { // fail if no Webserver yet.
StaticRequestHandlerAuth * staticHandler ;
if ( noauth & & * noauth = = ' 1 ' ) {
2024-05-24 21:02:09 +01:00
staticHandler = new StaticRequestHandlerAuth ( * ffsp , fpath , url , ( char * ) nullptr , false ) ;
2024-05-22 15:26:26 +01:00
} else {
2024-05-24 21:02:09 +01:00
staticHandler = new StaticRequestHandlerAuth ( * ffsp , fpath , url , ( char * ) nullptr , true ) ;
2024-05-22 15:26:26 +01:00
}
if ( staticHandler ) {
//Webserver->serveStatic(url, *ffsp, fpath);
Webserver - > addHandler ( staticHandler ) ;
Webserver - > enableCORS ( true ) ;
result = true ;
}
2023-07-10 09:27:20 +01:00
}
}
2024-05-22 15:26:26 +01:00
}
if ( ! result ) {
ResponseCmndFailed ( ) ;
} else {
ResponseCmndDone ( ) ;
2023-07-10 09:27:20 +01:00
}
}
# endif // UFILESYS_STATIC_SERVING
2021-02-16 11:19:40 +00:00
void UFSRun ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
2021-02-19 17:35:47 +00:00
char fname [ UFS_FILENAME_SIZE ] ;
if ( UfsExecuteCommandFile ( UfsFilename ( fname , XdrvMailbox . data ) ) ) {
2021-02-16 11:19:40 +00:00
ResponseClear ( ) ;
} else {
2021-02-16 15:21:46 +00:00
ResponseCmndFailed ( ) ;
2021-02-16 11:19:40 +00:00
}
2021-02-17 10:06:48 +00:00
} else {
bool not_active = UfsExecuteCommandFileReady ( ) ;
UfsData . run_file_pos = - 1 ;
ResponseCmndChar ( not_active ? PSTR ( D_JSON_DONE ) : PSTR ( D_JSON_ABORTED ) ) ;
2021-02-16 11:19:40 +00:00
}
}
2023-07-10 09:27:20 +01:00
2021-01-08 14:00:40 +00:00
/*********************************************************************************************\
* Web support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-01-08 16:31:16 +00:00
# ifdef USE_WEBSERVER
2020-12-31 13:19:50 +00:00
const char UFS_WEB_DIR [ ] PROGMEM =
2023-07-10 09:27:20 +01:00
" <p><form action='ufsd' method='get'><input type='hidden' name='download' value='%s' /> <button>%s</button></form></p> " ;
const char UFS_CURRDIR [ ] PROGMEM =
" <p>%s: %s</p> " ;
# ifndef D_CURR_DIR
# define D_CURR_DIR "Folder"
# endif
2021-01-06 16:54:03 +00:00
2020-12-31 13:19:50 +00:00
const char UFS_FORM_FILE_UPLOAD [ ] PROGMEM =
2021-01-04 14:52:32 +00:00
" <div id='f1' name='f1' style='display:block;'> "
2021-01-06 16:54:03 +00:00
" <fieldset><legend><b> " D_MANAGE_FILE_SYSTEM " </b></legend> " ;
const char UFS_FORM_FILE_UPGc [ ] PROGMEM =
2021-01-13 11:12:14 +00:00
" <div style='text-align:left;color:#%06x;'> " D_FS_SIZE " %s MB - " D_FS_FREE " %s MB " ;
2021-01-07 09:57:24 +00:00
const char UFS_FORM_FILE_UPGc1 [ ] PROGMEM =
2023-03-10 14:04:13 +00:00
" <a href='/ufsd?dir=%d'>%s</a> " ;
2021-01-07 09:57:24 +00:00
const char UFS_FORM_FILE_UPGc2 [ ] PROGMEM =
2021-01-09 17:19:15 +00:00
" </div> " ;
2021-01-07 09:57:24 +00:00
2020-12-31 13:19:50 +00:00
const char UFS_FORM_FILE_UPG [ ] PROGMEM =
2024-01-12 09:08:05 +00:00
" <form method='post' action='ufsu?fsz=' enctype='multipart/form-data'> "
2021-01-06 16:54:03 +00:00
" <br><input type='file' name='ufsu'><br> "
2024-01-12 09:08:05 +00:00
" <br><button type='submit' "
" onclick='eb( \" f1 \" ).style.display= \" none \" ;eb( \" but6 \" ).style.display= \" none \" ;eb( \" f2 \" ).style.display= \" block \" ;this.form.action+=this.form[ \" ufsu \" ].files[0].size;this.form.submit();' "
" > " D_UPLOAD " </button></form> "
2023-11-20 08:05:40 +00:00
" <br><hr> " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_SDC_DIRa [ ] PROGMEM =
2021-01-06 16:54:03 +00:00
" <div style='text-align:left;overflow:auto;height:250px;'> " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_SDC_DIRc [ ] PROGMEM =
2021-01-04 14:52:32 +00:00
" </div> " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_FILE_UPGb [ ] PROGMEM =
2023-07-10 09:27:20 +01:00
" <form method='get' action='ufse'><input type='hidden' name='file' value='%s/ " D_NEW_FILE " '> "
2021-10-21 17:35:25 +01:00
" <button type='submit'> " D_CREATE_NEW_FILE " </button></form> " ;
2021-10-19 07:08:55 +01:00
const char UFS_FORM_FILE_UPGb1 [ ] PROGMEM =
2024-11-15 14:23:57 +00:00
" <label><input type='checkbox' id='shf' onclick='sf(eb( \" shf \" ).checked);' name='shf'> " D_SHOW_HIDDEN_FILES " </label> " ;
2021-10-19 07:08:55 +01:00
const char UFS_FORM_FILE_UPGb2 [ ] PROGMEM =
2021-01-04 14:52:32 +00:00
" </fieldset> "
" </div> "
" <div id='f2' name='f2' style='display:none;text-align:center;'><b> " D_UPLOAD_STARTED " ...</b></div> " ;
2021-10-03 19:11:14 +01:00
const char UFS_FORM_SDC_DIR_NORMAL [ ] PROGMEM =
" " ;
const char UFS_FORM_SDC_DIR_HIDDABLE [ ] PROGMEM =
" class='hf' " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_SDC_DIRd [ ] PROGMEM =
2021-01-04 14:52:32 +00:00
" <pre><a href='%s' file='%s'>%s</a></pre> " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_SDC_DIRb [ ] PROGMEM =
2023-07-10 09:27:20 +01:00
" <pre%s><a href='%s' file='%s'>%s</a> %19s %8d %s %s</pre> " ;
2020-12-31 13:19:50 +00:00
const char UFS_FORM_SDC_HREF [ ] PROGMEM =
2021-04-11 14:22:57 +01:00
" ufsd?download=%s/%s " ;
2021-01-09 15:33:23 +00:00
# ifdef GUI_TRASH_FILE
const char UFS_FORM_SDC_HREFdel [ ] PROGMEM =
2021-04-11 14:22:57 +01:00
//"<a href=ufsd?delete=%s/%s>🗑</a>"; // 🗑️
2023-07-10 09:27:20 +01:00
" <a href='ufsd?delete=%s/%s&download=%s' onclick= \" return confirm(' " D_CONFIRM_FILE_DEL " ') \" >🔥</a> " ; // 🔥
2021-01-09 15:33:23 +00:00
# endif // GUI_TRASH_FILE
2021-04-11 14:22:57 +01:00
# ifdef GUI_EDIT_FILE
# define FILE_BUFFER_SIZE 1024
const char UFS_FORM_SDC_HREFedit [ ] PROGMEM =
2021-10-03 19:11:14 +01:00
" <a href='ufse?file=%s/%s'>📝</a> " ; // 📝
2021-04-11 14:22:57 +01:00
const char HTTP_EDITOR_FORM_START [ ] PROGMEM =
2021-04-16 16:24:49 +01:00
" <fieldset><legend><b> " D_EDIT_FILE " </b></legend> "
" <form> "
" <label for='name'> " D_FILE " :</label><input type='text' id='name' name='name' value='%s'><br><hr width='98%%'> "
2021-10-20 21:49:50 +01:00
" <textarea id='content' name='content' wrap='off' rows='8' cols='80' style='font-size: 12pt'> " ;
2021-04-11 14:22:57 +01:00
const char HTTP_EDITOR_FORM_END [ ] PROGMEM =
2021-04-16 16:24:49 +01:00
" </textarea> "
" <button name='save' type='submit' formmethod='post' formenctype='multipart/form-data' formaction='/ufse' class='button bgrn'> " D_SAVE " </button> "
" </form></fieldset> " ;
2021-04-11 14:22:57 +01:00
# endif // #ifdef GUI_EDIT_FILE
2024-01-12 09:08:05 +00:00
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 ( " <div style='text-align:center;'><b> " D_UPLOAD " <font color='# " ) ) ;
if ( Web . upload_error ) {
WSContentSend_P ( PSTR ( " %06x'> " D_FAILED " </font></b><br><br> " ) , 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 " </font></b><br> " ) , WebColor ( COL_TEXT_SUCCESS ) ) ;
}
WSContentSend_P ( PSTR ( " </div><br> " ) ) ;
XdrvCall ( FUNC_WEB_ADD_MANAGEMENT_BUTTON ) ;
WSContentStop ( ) ;
}
2021-01-08 14:00:40 +00:00
void UfsDirectory ( void ) {
2021-01-09 17:19:15 +00:00
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_HTTP D_MANAGE_FILE_SYSTEM ) ) ;
2021-01-09 17:19:15 +00:00
2020-12-31 13:19:50 +00:00
uint8_t depth = 0 ;
2023-07-10 09:27:20 +01:00
uint8_t isdir = 0 ;
2020-12-31 13:19:50 +00:00
strcpy ( ufs_path , " / " ) ;
2021-01-18 20:48:04 +00:00
if ( Webserver - > hasArg ( F ( " dir " ) ) ) {
String stmp = Webserver - > arg ( F ( " dir " ) ) ;
2021-01-07 09:57:24 +00:00
ufs_dir = atoi ( stmp . c_str ( ) ) ;
2023-07-10 09:27:20 +01:00
}
if ( ufs_dir = = 1 ) {
dfsp = ufsp ;
} else {
if ( ffsp ) {
dfsp = ffsp ;
2021-01-07 09:57:24 +00:00
}
}
2021-01-18 20:48:04 +00:00
if ( Webserver - > hasArg ( F ( " delete " ) ) ) {
String stmp = Webserver - > arg ( F ( " delete " ) ) ;
2021-01-09 15:33:23 +00:00
char * cp = ( char * ) stmp . c_str ( ) ;
2023-07-10 09:27:20 +01:00
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 ;
}
2021-01-09 15:33:23 +00:00
}
2021-01-06 16:54:03 +00:00
WSContentStart_P ( PSTR ( D_MANAGE_FILE_SYSTEM ) ) ;
2020-12-31 13:19:50 +00:00
WSContentSendStyle ( ) ;
2021-01-06 16:54:03 +00:00
WSContentSend_P ( UFS_FORM_FILE_UPLOAD ) ;
2021-01-13 11:12:14 +00:00
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 ) ;
2021-01-06 16:54:03 +00:00
2021-01-07 09:57:24 +00:00
if ( ufs_dir ) {
2023-03-10 14:04:13 +00:00
WSContentSend_P ( UFS_FORM_FILE_UPGc1 , ( ufs_dir = = 1 ) ? 2 : 1 , ( ufs_dir = = 1 ) ? PSTR ( " SDCard " ) : PSTR ( " FlashFS " ) ) ;
2021-01-07 09:57:24 +00:00
}
WSContentSend_P ( UFS_FORM_FILE_UPGc2 ) ;
2023-11-20 08:05:40 +00:00
WSContentSend_P ( UFS_FORM_FILE_UPG ) ;
2021-01-06 16:54:03 +00:00
2023-07-10 09:27:20 +01:00
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 ) ;
}
}
2020-12-31 13:19:50 +00:00
WSContentSend_P ( UFS_FORM_SDC_DIRa ) ;
if ( ufs_type ) {
2021-01-08 14:00:40 +00:00
UfsListDir ( ufs_path , depth ) ;
2020-12-31 13:19:50 +00:00
}
WSContentSend_P ( UFS_FORM_SDC_DIRc ) ;
2021-10-21 17:35:25 +01:00
# ifdef GUI_EDIT_FILE
2023-07-10 09:27:20 +01:00
WSContentSend_P ( UFS_FORM_FILE_UPGb , ufs_path ) ;
2021-10-21 17:35:25 +01:00
# endif
2021-10-19 07:08:55 +01:00
if ( ! isSDC ( ) ) {
WSContentSend_P ( UFS_FORM_FILE_UPGb1 ) ;
}
WSContentSend_P ( UFS_FORM_FILE_UPGb2 ) ;
2021-04-16 10:40:38 +01:00
WSContentSpaceButton ( BUTTON_MANAGEMENT ) ;
2020-12-31 13:19:50 +00:00
WSContentStop ( ) ;
2021-01-09 17:19:15 +00:00
Web . upload_file_type = UPL_UFSFILE ;
2020-12-31 13:19:50 +00:00
}
2021-10-19 07:08:55 +01:00
// return true if SDC
bool isSDC ( void ) {
# ifndef SDC_HIDE_INVISIBLES
return false ;
# else
if ( ( ( uint32_t ) ufsp ! = ( uint32_t ) ffsp ) & & ( ( uint32_t ) ffsp = = ( uint32_t ) dfsp ) ) return false ;
if ( ( ( uint32_t ) ufsp = = ( uint32_t ) ffsp ) & & ( ufs_type ! = UFS_TSDC ) ) return false ;
return true ;
# endif
}
2021-01-08 14:00:40 +00:00
void UfsListDir ( char * path , uint8_t depth ) {
2021-10-19 07:08:55 +01:00
char name [ 48 ] ;
2020-12-31 13:19:50 +00:00
char npath [ 128 ] ;
char format [ 12 ] ;
2021-01-18 20:48:04 +00:00
sprintf ( format , PSTR ( " %%-%ds " ) , 24 - depth ) ;
2020-12-31 13:19:50 +00:00
2021-01-07 09:57:24 +00:00
File dir = dfsp - > open ( path , UFS_FILE_READ ) ;
2020-12-31 13:19:50 +00:00
if ( dir ) {
dir . rewindDirectory ( ) ;
if ( strlen ( path ) > 1 ) {
2021-04-11 14:22:57 +01:00
ext_snprintf_P ( npath , sizeof ( npath ) , PSTR ( " ufsd?download=%s " ) , path ) ;
2021-01-04 14:52:32 +00:00
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 ;
}
2020-12-31 13:19:50 +00:00
break ;
}
}
2021-01-18 20:48:04 +00:00
WSContentSend_P ( UFS_FORM_SDC_DIRd , npath , path , PSTR ( " .. " ) ) ;
2020-12-31 13:19:50 +00:00
}
char * ep ;
while ( true ) {
2023-07-10 09:27:20 +01:00
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 ;
}
2020-12-31 13:19:50 +00:00
File entry = dir . openNextFile ( ) ;
if ( ! entry ) {
break ;
}
// esp32 returns path here, shorten to filename
ep = ( char * ) entry . name ( ) ;
2021-01-04 14:52:32 +00:00
if ( * ep = = ' / ' ) { ep + + ; }
2020-12-31 13:19:50 +00:00
char * lcp = strrchr ( ep , ' / ' ) ;
if ( lcp ) {
ep = lcp + 1 ;
}
2021-01-06 16:54:03 +00:00
uint32_t tm = entry . getLastWrite ( ) ;
String tstr = GetDT ( tm ) ;
2020-12-31 13:19:50 +00:00
char * pp = path ;
2021-01-04 14:52:32 +00:00
if ( ! * ( pp + 1 ) ) { pp + + ; }
2020-12-31 13:19:50 +00:00
char * cp = name ;
// osx formatted disks contain a lot of stuff we dont want
2021-10-03 19:11:14 +01:00
bool hiddable = UfsReject ( ( char * ) ep ) ;
2020-12-31 13:19:50 +00:00
2021-10-19 07:08:55 +01:00
if ( ! hiddable | | ! isSDC ( ) ) {
2022-06-27 18:30:04 +01:00
for ( uint8_t cnt = 0 ; cnt < depth ; cnt + + ) {
* cp + + = ' - ' ;
}
2021-01-01 07:41:36 +00:00
2022-06-27 18:30:04 +01:00
String pp_escaped_string = UrlEscape ( pp ) ;
String ep_escaped_string = UrlEscape ( ep ) ;
const char * ppe = pp_escaped_string . c_str ( ) ; // this can't be merged on a single line otherwise the String object can be freed
const char * epe = ep_escaped_string . c_str ( ) ;
sprintf ( cp , format , ep ) ;
2023-07-10 09:27:20 +01:00
# ifdef GUI_TRASH_FILE
char delpath [ 128 + UFS_FILENAME_SIZE ] ;
ext_snprintf_P ( delpath , sizeof ( delpath ) , UFS_FORM_SDC_HREFdel , ppe , epe , ppe [ 0 ] ? ppe : " / " ) ;
# else
char delpath [ 2 ] = " " ;
# endif // GUI_TRASH_FILE
2022-06-27 18:30:04 +01:00
if ( entry . isDirectory ( ) ) {
ext_snprintf_P ( npath , sizeof ( npath ) , UFS_FORM_SDC_HREF , ppe , epe ) ;
2023-07-10 09:27:20 +01:00
WSContentSend_P ( UFS_FORM_SDC_DIRb , hiddable ? UFS_FORM_SDC_DIR_HIDDABLE : UFS_FORM_SDC_DIR_NORMAL , npath , epe ,
HtmlEscape ( name ) . c_str ( ) , " " , 0 , delpath , " " ) ;
//WSContentSend_P(UFS_FORM_SDC_DIRd, npath, ep, name);
# ifdef UFILESYS_RECURSEFOLDERS_GUI
2022-06-27 18:30:04 +01:00
uint8_t plen = strlen ( path ) ;
if ( plen > 1 ) {
strcat ( path , " / " ) ;
}
strcat ( path , ep ) ;
UfsListDir ( path , depth + 4 ) ;
path [ plen ] = 0 ;
2023-07-10 09:27:20 +01:00
# endif
2022-06-27 18:30:04 +01:00
} 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 ) ;
2023-01-04 11:00:09 +00:00
WSContentSend_P ( UFS_FORM_SDC_DIRb , hiddable ? UFS_FORM_SDC_DIR_HIDDABLE : UFS_FORM_SDC_DIR_NORMAL , npath , epe ,
2022-06-27 18:30:04 +01:00
HtmlEscape ( name ) . c_str ( ) , tstr . c_str ( ) , entry . size ( ) , delpath , editpath ) ;
2021-10-03 19:11:14 +01:00
}
2022-06-27 18:30:04 +01:00
entry . close ( ) ;
2024-05-24 21:02:09 +01:00
yield ( ) ; // trigger watchdog reset
2020-12-31 13:19:50 +00:00
}
}
dir . close ( ) ;
}
}
2021-01-17 11:30:20 +00:00
# ifdef ESP32
2023-07-10 09:27:20 +01:00
// this actually does not work reliably, as the
// webserver can close the connection before the download task completes
//#define ESP32_DOWNLOAD_TASK
2021-01-17 11:30:20 +00:00
# endif // ESP32
2021-01-11 16:44:54 +00:00
2021-01-08 14:00:40 +00:00
uint8_t UfsDownloadFile ( char * file ) {
2020-12-31 13:19:50 +00:00
File download_file ;
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: File '%s' download " ) , file ) ;
2021-01-07 09:57:24 +00:00
if ( ! dfsp - > exists ( file ) ) {
2021-04-11 14:22:57 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: File '%s' not found " ) , file ) ;
2021-01-04 14:52:32 +00:00
return 0 ;
}
2020-12-31 13:19:50 +00:00
2021-01-07 09:57:24 +00:00
download_file = dfsp - > open ( file , UFS_FILE_READ ) ;
2021-01-04 14:52:32 +00:00
if ( ! download_file ) {
2021-04-11 14:22:57 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: Could not open file '%s' " ) , file ) ;
2021-01-04 14:52:32 +00:00
return 0 ;
}
2020-12-31 13:19:50 +00:00
2021-01-04 14:52:32 +00:00
if ( download_file . isDirectory ( ) ) {
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: File '%s' to download is directory " ) , file ) ;
2021-01-04 14:52:32 +00:00
download_file . close ( ) ;
return 1 ;
}
2020-12-31 13:19:50 +00:00
2021-01-17 11:30:20 +00:00
# ifndef ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
WiFiClient download_Client ;
2021-01-04 14:52:32 +00:00
uint32_t flen = download_file . size ( ) ;
2020-12-31 13:19:50 +00:00
2021-01-04 14:52:32 +00:00
download_Client = Webserver - > client ( ) ;
Webserver - > setContentLength ( flen ) ;
2020-12-31 13:19:50 +00:00
2021-01-04 14:52:32 +00:00
char attachment [ 100 ] ;
2023-07-10 09:27:20 +01:00
char * cp = fileOnly ( file ) ;
2021-01-04 14:52:32 +00:00
snprintf_P ( attachment , sizeof ( attachment ) , PSTR ( " attachment; filename=%s " ) , cp ) ;
Webserver - > sendHeader ( F ( " Content-Disposition " ) , attachment ) ;
2021-02-02 13:57:53 +00:00
WSSend ( 200 , CT_APP_STREAM , " " ) ;
2021-01-04 14:52:32 +00:00
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();
//}
2020-12-31 13:19:50 +00:00
}
2021-01-04 14:52:32 +00:00
delay ( 0 ) ;
2021-01-11 16:44:54 +00:00
OsWatchLoop ( ) ;
2021-01-04 14:52:32 +00:00
}
download_file . close ( ) ;
download_Client . stop ( ) ;
2021-01-17 11:30:20 +00:00
# endif // ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
2023-07-10 09:27:20 +01:00
// 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.
2021-01-17 11:30:20 +00:00
# ifdef ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
download_file . close ( ) ;
2021-02-15 15:37:09 +00:00
if ( UfsData . download_busy = = true ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: Download is busy " ) ) ;
2021-01-11 16:44:54 +00:00
return 0 ;
}
2021-02-15 15:37:09 +00:00
UfsData . download_busy = true ;
2021-01-11 16:44:54 +00:00
char * path = ( char * ) malloc ( 128 ) ;
strcpy ( path , file ) ;
2023-01-15 15:20:35 +00:00
BaseType_t ret = xTaskCreatePinnedToCore ( download_task , " DT " , 6000 , ( void * ) path , 3 , nullptr , 1 ) ;
2022-04-03 17:16:08 +01:00
if ( ret ! = pdPASS )
AddLog ( LOG_LEVEL_INFO , PSTR ( " UFS: Download task failed with %d " ) , ret ) ;
yield ( ) ;
2021-01-17 11:30:20 +00:00
# endif // ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
2021-01-04 14:52:32 +00:00
return 0 ;
2020-12-31 13:19:50 +00:00
}
2021-01-11 16:44:54 +00:00
2021-01-17 11:30:20 +00:00
# ifdef ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
# ifndef DOWNLOAD_SIZE
# define DOWNLOAD_SIZE 4096
2021-01-17 11:30:20 +00:00
# endif // DOWNLOAD_SIZE
2023-01-15 15:20:35 +00:00
void download_task ( void * path ) {
2021-01-11 16:44:54 +00:00
File download_file ;
WiFiClient download_Client ;
char * file = ( char * ) path ;
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: ESP32 File '%s' to download " ) , file ) ;
2021-01-11 16:44:54 +00:00
download_file = dfsp - > open ( file , UFS_FILE_READ ) ;
uint32_t flen = download_file . size ( ) ;
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: len %d to download " ) , flen ) ;
2021-01-11 16:44:54 +00:00
download_Client = Webserver - > client ( ) ;
Webserver - > setContentLength ( flen ) ;
char attachment [ 100 ] ;
2023-07-10 09:27:20 +01:00
char * cp = fileOnly ( file ) ;
2023-01-15 15:20:35 +00:00
//snprintf_P(attachment, sizeof(attachment), PSTR("download file '%s' as '%s'"), file, cp);
//Webserver->sendHeader(F("X-Tasmota-Debug"), attachment);
2021-01-11 16:44:54 +00:00
snprintf_P ( attachment , sizeof ( attachment ) , PSTR ( " attachment; filename=%s " ) , cp ) ;
Webserver - > sendHeader ( F ( " Content-Disposition " ) , attachment ) ;
2021-02-02 13:57:53 +00:00
WSSend ( 200 , CT_APP_STREAM , " " ) ;
2021-01-11 16:44:54 +00:00
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 ( ) ;
2021-02-15 15:37:09 +00:00
UfsData . download_busy = false ;
2021-01-11 16:44:54 +00:00
vTaskDelete ( NULL ) ;
2023-01-15 15:20:35 +00:00
free ( path ) ;
2023-07-10 09:27:20 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: esp32 sent file " ) ) ;
2021-01-11 16:44:54 +00:00
}
2021-01-17 11:30:20 +00:00
# endif // ESP32_DOWNLOAD_TASK
2021-01-11 16:44:54 +00:00
2021-01-09 17:19:15 +00:00
bool UfsUploadFileOpen ( const char * upload_filename ) {
char npath [ 48 ] ;
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 ) ;
2020-12-31 13:19:50 +00:00
} else {
2021-01-09 17:19:15 +00:00
return false ;
2020-12-31 13:19:50 +00:00
}
2021-01-09 17:19:15 +00:00
return true ;
}
void UfsUploadFileClose ( void ) {
ufs_upload_file . close ( ) ;
2020-12-31 13:19:50 +00:00
}
2021-04-11 14:22:57 +01:00
//******************************************************************************************
// File Editor
//******************************************************************************************
# ifdef GUI_EDIT_FILE
void UfsEditor ( void ) {
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor GET " ) ) ;
2021-04-16 16:24:49 +01:00
char fname_input [ UFS_FILENAME_SIZE ] ;
2021-04-11 14:22:57 +01:00
if ( Webserver - > hasArg ( F ( " file " ) ) ) {
2021-04-16 16:24:49 +01:00
WebGetArg ( PSTR ( " file " ) , fname_input , sizeof ( fname_input ) ) ;
} else {
snprintf_P ( fname_input , sizeof ( fname_input ) , PSTR ( D_NEW_FILE ) ) ;
2021-04-11 14:22:57 +01:00
}
2021-04-16 16:24:49 +01:00
char fname [ UFS_FILENAME_SIZE ] ;
UfsFilename ( fname , fname_input ) ; // Trim spaces and add slash
2021-04-11 14:22:57 +01:00
2022-01-01 14:06:33 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor: file=%s, ffs_type=%d, TfsFileExist=%d " ) , fname , ffs_type , dfsp - > exists ( fname ) ) ;
2021-04-11 14:22:57 +01:00
WSContentStart_P ( PSTR ( D_EDIT_FILE ) ) ;
WSContentSendStyle ( ) ;
2021-04-16 16:24:49 +01:00
char * bfname = fname + 1 ;
WSContentSend_P ( HTTP_EDITOR_FORM_START , bfname ) ; // Skip leading slash
2021-04-11 14:22:57 +01:00
2022-01-01 14:06:33 +00:00
if ( ffs_type & & dfsp - > exists ( fname ) ) {
File fp = dfsp - > open ( fname , " r " ) ;
2021-04-11 14:22:57 +01:00
if ( ! fp ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor: file open failed " ) ) ;
WSContentSend_P ( D_NEW_FILE ) ;
2021-04-16 16:24:49 +01:00
} else {
uint8_t * buf = ( uint8_t * ) malloc ( FILE_BUFFER_SIZE + 1 ) ;
size_t filelen = fp . size ( ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor: file len=%d " ) , filelen ) ;
while ( filelen > 0 ) {
size_t l = fp . read ( buf , FILE_BUFFER_SIZE ) ;
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " UFS: UfsEditor: read=%d " ) , l ) ;
if ( l < 0 ) { break ; }
buf [ l ] = ' \0 ' ;
2024-11-21 15:58:45 +00:00
WSContentSend_P ( PSTR ( " %s " ) , HtmlEscape ( ( char * ) buf ) . c_str ( ) ) ;
2021-04-16 16:24:49 +01:00
filelen - = l ;
}
fp . close ( ) ;
free ( buf ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor: read done " ) ) ;
2021-04-11 14:22:57 +01:00
}
2021-04-16 16:24:49 +01:00
} else {
2021-04-11 14:22:57 +01:00
WSContentSend_P ( D_NEW_FILE ) ;
}
WSContentSend_P ( HTTP_EDITOR_FORM_END ) ;
2023-07-10 09:27:20 +01:00
folderOnly ( fname ) ;
WSContentSend_P ( UFS_WEB_DIR , fname , PSTR ( D_MANAGE_FILE_SYSTEM ) ) ;
2021-04-11 14:22:57 +01:00
WSContentStop ( ) ;
}
void UfsEditorUpload ( void ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " UFS: UfsEditor: file upload " ) ) ;
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
if ( ! Webserver - > hasArg ( " name " ) ) {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: UfsEditor: file upload - no filename " ) ) ;
WSSend ( 400 , CT_PLAIN , F ( " 400: Bad request - no filename " ) ) ;
return ;
}
2021-04-16 16:24:49 +01:00
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 ( " UFS: UfsEditor: file '%s' " ) , fname ) ;
2021-04-11 14:22:57 +01:00
if ( ! Webserver - > hasArg ( " content " ) ) {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: UfsEditor: file upload - no content " ) ) ;
WSSend ( 400 , CT_PLAIN , F ( " 400: Bad request - no content " ) ) ;
return ;
}
String content = Webserver - > arg ( " content " ) ;
2022-01-01 14:06:33 +00:00
if ( ! dfsp ) {
2021-04-11 14:22:57 +01:00
Web . upload_error = 1 ;
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: UfsEditor: 507: no storage available " ) ) ;
WSSend ( 507 , CT_PLAIN , F ( " 507: no storage available " ) ) ;
return ;
}
2023-07-10 09:27:20 +01:00
// 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 , " / " ) ;
}
2022-01-01 14:06:33 +00:00
File fp = dfsp - > open ( fname , " w " ) ;
2021-04-16 16:24:49 +01:00
if ( ! fp ) {
2021-04-11 14:22:57 +01:00
Web . upload_error = 1 ;
2021-04-16 16:24:49 +01:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: UfsEditor: 400: invalid file name '%s' " ) , fname ) ;
2021-04-11 14:22:57 +01:00
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 ) ) {
2021-04-16 16:24:49 +01:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " UFS: UfsEditor: write error on '%s' " ) , fname ) ;
2021-04-11 14:22:57 +01:00
}
fp . close ( ) ;
2023-07-10 09:27:20 +01:00
// zap file name off the end
folderOnly ( fname ) ;
char t [ 20 + UFS_FILENAME_SIZE ] = " /ufsu?download= " ;
strcat ( t , fname ) ;
Webserver - > sendHeader ( F ( " Location " ) , t ) ;
2021-04-11 14:22:57 +01:00
Webserver - > send ( 303 ) ;
}
2021-04-16 16:24:49 +01:00
# endif // GUI_EDIT_FILE
2021-04-11 14:22:57 +01:00
2021-01-08 16:31:16 +00:00
# endif // USE_WEBSERVER
2024-01-08 15:44:49 +00:00
# ifdef USE_FTP
# include <ESPFtpServer.h>
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 ( " 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
2020-12-31 13:19:50 +00:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-01-08 15:44:49 +00:00
2022-11-11 09:44:56 +00:00
bool Xdrv50 ( uint32_t function ) {
2020-12-31 13:19:50 +00:00
bool result = false ;
switch ( function ) {
2021-02-16 15:21:46 +00:00
case FUNC_LOOP :
UfsExecuteCommandFileLoop ( ) ;
2024-01-08 15:44:49 +00:00
# ifdef USE_FTP
if ( ftpSrv ) {
ftpSrv - > handleFTP ( ) ;
}
# endif
2021-02-16 15:21:46 +00:00
break ;
2024-01-08 15:44:49 +00:00
case FUNC_NETWORK_UP :
# ifdef USE_FTP
if ( Settings - > mbflag2 . FTP_Mode & & ! ftpSrv ) {
FTP_Server ( Settings - > mbflag2 . FTP_Mode ) ;
}
# endif
break ;
2023-07-01 11:48:33 +01:00
/*
// Moved to support_tasmota.ino for earlier init to be used by scripter
2021-01-08 18:28:05 +00:00
# ifdef USE_SDCARD
case FUNC_PRE_INIT :
UfsCheckSDCardInit ( ) ;
break ;
# endif // USE_SDCARD
2023-07-01 11:48:33 +01:00
*/
2021-02-15 15:37:09 +00:00
case FUNC_MQTT_INIT :
2021-02-16 14:54:53 +00:00
if ( ! TasmotaGlobal . no_autoexec ) {
UfsExecuteCommandFile ( TASM_FILE_AUTOEXEC ) ;
}
2021-02-15 15:37:09 +00:00
break ;
2020-12-31 13:19:50 +00:00
case FUNC_COMMAND :
result = DecodeCommand ( kUFSCommands , kUFSCommand ) ;
break ;
# ifdef USE_WEBSERVER
2021-01-06 16:54:03 +00:00
case FUNC_WEB_ADD_MANAGEMENT_BUTTON :
2020-12-31 13:19:50 +00:00
if ( ufs_type ) {
2021-04-16 10:40:38 +01:00
if ( XdrvMailbox . index ) {
XdrvMailbox . index + + ;
} else {
2023-07-10 09:27:20 +01:00
WSContentSend_PD ( UFS_WEB_DIR , " / " , PSTR ( D_MANAGE_FILE_SYSTEM ) ) ;
2021-04-16 10:40:38 +01:00
}
2020-12-31 13:19:50 +00:00
}
break ;
case FUNC_WEB_ADD_HANDLER :
2021-02-03 11:22:17 +00:00
// 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 ) ;
2024-01-12 09:08:05 +00:00
//Webserver->on("/ufsu", HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop);
Webserver - > on ( " /ufsu " , HTTP_POST , HandleUploadUFSDone , HandleUploadLoop ) ;
2021-04-11 14:22:57 +01:00
# ifdef GUI_EDIT_FILE
Webserver - > on ( " /ufse " , HTTP_GET , UfsEditor ) ;
Webserver - > on ( " /ufse " , HTTP_POST , UfsEditorUpload ) ;
# endif
2020-12-31 13:19:50 +00:00
break ;
# endif // USE_WEBSERVER
2023-12-27 21:03:56 +00:00
case FUNC_ACTIVE :
result = true ;
break ;
2020-12-31 13:19:50 +00:00
}
return result ;
}
# endif // USE_UFILESYS