diff options
-rw-r--r-- | espFtpCommands.h | 63 | ||||
-rw-r--r-- | espFtpServer.cpp | 1307 | ||||
-rw-r--r-- | espFtpServer.h | 147 |
3 files changed, 0 insertions, 1517 deletions
diff --git a/espFtpCommands.h b/espFtpCommands.h deleted file mode 100644 index 4cf8c21..0000000 --- a/espFtpCommands.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef ESP_FTP_COMMANDS_H -#define ESP_FTP_COMMANDS_H - -#define FTP_CMD(CMD) (FTP_CMD_LE_##CMD) - -#define FTP_CMD_LE_USER 0x52455355 // "USER" as uint32_t (little endian) -#define FTP_CMD_BE_USER 0x55534552 // "USER" as uint32_t (big endian) -#define FTP_CMD_LE_PASS 0x53534150 // "PASS" as uint32_t (little endian) -#define FTP_CMD_BE_PASS 0x50415353 // "PASS" as uint32_t (big endian) -#define FTP_CMD_LE_QUIT 0x54495551 // "QUIT" as uint32_t (little endian) -#define FTP_CMD_BE_QUIT 0x51554954 // "QUIT" as uint32_t (big endian) -#define FTP_CMD_LE_CDUP 0x50554443 // "CDUP" as uint32_t (little endian) -#define FTP_CMD_BE_CDUP 0x43445550 // "CDUP" as uint32_t (big endian) -#define FTP_CMD_LE_CWD 0x00445743 // "CWD" as uint32_t (little endian) -#define FTP_CMD_BE_CWD 0x43574400 // "CWD" as uint32_t (big endian) -#define FTP_CMD_LE_PWD 0x00445750 // "PWD" as uint32_t (little endian) -#define FTP_CMD_BE_PWD 0x50574400 // "PWD" as uint32_t (big endian) -#define FTP_CMD_LE_MODE 0x45444f4d // "MODE" as uint32_t (little endian) -#define FTP_CMD_BE_MODE 0x4d4f4445 // "MODE" as uint32_t (big endian) -#define FTP_CMD_LE_PASV 0x56534150 // "PASV" as uint32_t (little endian) -#define FTP_CMD_BE_PASV 0x50415356 // "PASV" as uint32_t (big endian) -#define FTP_CMD_LE_PORT 0x54524f50 // "PORT" as uint32_t (little endian) -#define FTP_CMD_BE_PORT 0x504f5254 // "PORT" as uint32_t (big endian) -#define FTP_CMD_LE_STRU 0x55525453 // "STRU" as uint32_t (little endian) -#define FTP_CMD_BE_STRU 0x53545255 // "STRU" as uint32_t (big endian) -#define FTP_CMD_LE_TYPE 0x45505954 // "TYPE" as uint32_t (little endian) -#define FTP_CMD_BE_TYPE 0x54595045 // "TYPE" as uint32_t (big endian) -#define FTP_CMD_LE_ABOR 0x524f4241 // "ABOR" as uint32_t (little endian) -#define FTP_CMD_BE_ABOR 0x41424f52 // "ABOR" as uint32_t (big endian) -#define FTP_CMD_LE_DELE 0x454c4544 // "DELE" as uint32_t (little endian) -#define FTP_CMD_BE_DELE 0x44454c45 // "DELE" as uint32_t (big endian) -#define FTP_CMD_LE_LIST 0x5453494c // "LIST" as uint32_t (little endian) -#define FTP_CMD_BE_LIST 0x4c495354 // "LIST" as uint32_t (big endian) -#define FTP_CMD_LE_MLSD 0x44534c4d // "MLSD" as uint32_t (little endian) -#define FTP_CMD_BE_MLSD 0x4d4c5344 // "MLSD" as uint32_t (big endian) -#define FTP_CMD_LE_NLST 0x54534c4e // "NLST" as uint32_t (little endian) -#define FTP_CMD_BE_NLST 0x4e4c5354 // "NLST" as uint32_t (big endian) -#define FTP_CMD_LE_NOOP 0x504f4f4e // "NOOP" as uint32_t (little endian) -#define FTP_CMD_BE_NOOP 0x4e4f4f50 // "NOOP" as uint32_t (big endian) -#define FTP_CMD_LE_RETR 0x52544552 // "RETR" as uint32_t (little endian) -#define FTP_CMD_BE_RETR 0x52455452 // "RETR" as uint32_t (big endian) -#define FTP_CMD_LE_STOR 0x524f5453 // "STOR" as uint32_t (little endian) -#define FTP_CMD_BE_STOR 0x53544f52 // "STOR" as uint32_t (big endian) -#define FTP_CMD_LE_MKD 0x00444b4d // "MKD" as uint32_t (little endian) -#define FTP_CMD_BE_MKD 0x4d4b4400 // "MKD" as uint32_t (big endian) -#define FTP_CMD_LE_RMD 0x00444d52 // "RMD" as uint32_t (little endian) -#define FTP_CMD_BE_RMD 0x524d4400 // "RMD" as uint32_t (big endian) -#define FTP_CMD_LE_RNFR 0x52464e52 // "RNFR" as uint32_t (little endian) -#define FTP_CMD_BE_RNFR 0x524e4652 // "RNFR" as uint32_t (big endian) -#define FTP_CMD_LE_RNTO 0x4f544e52 // "RNTO" as uint32_t (little endian) -#define FTP_CMD_BE_RNTO 0x524e544f // "RNTO" as uint32_t (big endian) -#define FTP_CMD_LE_FEAT 0x54414546 // "FEAT" as uint32_t (little endian) -#define FTP_CMD_BE_FEAT 0x46454154 // "FEAT" as uint32_t (big endian) -#define FTP_CMD_LE_MDTM 0x4d54444d // "MDTM" as uint32_t (little endian) -#define FTP_CMD_BE_MDTM 0x4d44544d // "MDTM" as uint32_t (big endian) -#define FTP_CMD_LE_SIZE 0x455a4953 // "SIZE" as uint32_t (little endian) -#define FTP_CMD_BE_SIZE 0x53495a45 // "SIZE" as uint32_t (big endian) -#define FTP_CMD_LE_SITE 0x45544953 // "SITE" as uint32_t (little endian) -#define FTP_CMD_BE_SITE 0x53495445 // "SITE" as uint32_t (big endian) -#define FTP_CMD_LE_SYST 0x54535953 // "SYST" as uint32_t (little endian) -#define FTP_CMD_BE_SYST 0x53595354 // "SYST" as uint32_t (big endian) - -#endif // ESP_FTP_COMMANDS_H diff --git a/espFtpServer.cpp b/espFtpServer.cpp deleted file mode 100644 index 96f7f3e..0000000 --- a/espFtpServer.cpp +++ /dev/null @@ -1,1307 +0,0 @@ -/* - * FTP Server for ESP8266/ESP32 - * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) - * based on Jean-Michel Gallego's work - * modified to work with esp8266 SPIFFS by David Paiva david@nailbuster.com - * modified to work with esp8266 LitteFS by Daniel Plasa dplasa@gmail.com - * Also done some code reworks and all string contants are now in flash memory - * by using F(), PSTR() ... on the string literals. - * - * 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/>. - */ - -#include "espFtpServer.h" - -#ifdef ESP8266 -#include <ESP8266WiFi.h> -#elif defined ESP32 -#include <WiFi.h> -#endif - -#include "espFtpCommands.h" - -WiFiServer controlServer(FTP_CTRL_PORT); -WiFiServer dataServer(FTP_DATA_PORT_PASV); - -// helper macros -#define FTP_STR(s) FTP_STR2(s) -#define FTP_STR2(s) #s -#define FTP_SEND_MSG(code, fmt, ...) \ - do \ - { \ - FTP_DEBUG_MSG(">>> " FTP_STR(code) " " fmt, ##__VA_ARGS__); \ - control.printf_P(PSTR(FTP_STR(code) " " fmt "\r\n"), ##__VA_ARGS__); \ - } while (0) - -#define FTP_SEND_DASHMSG(code, fmt, ...) \ - do \ - { \ - FTP_DEBUG_MSG(">>> " FTP_STR(code) "-" fmt, ##__VA_ARGS__); \ - control.printf_P(PSTR(FTP_STR(code) "-" fmt "\r\n"), ##__VA_ARGS__); \ - } while (0) - -// some constants -static const char aSpace[] PROGMEM = " "; -static const char aSlash[] PROGMEM = "/"; - -// constructor -FtpServer::FtpServer(FS &_FSImplementation) : THEFS(_FSImplementation) {} - -void FtpServer::begin(const String &uname, const String &pword) -{ - _FTP_USER = uname; - _FTP_PASS = pword; - - iniVariables(); - - // Tells the ftp server to begin listening for incoming connections - controlServer.begin(); - dataServer.begin(); -} - -void FtpServer::stop() -{ - abortTransfer(); - disconnectClient(false); - controlServer.stop(); - dataServer.stop(); -} - -void FtpServer::iniVariables() -{ - // Default Data connection is Active - dataPassiveConn = true; - - // Set the root directory - cwd = FPSTR(aSlash); - - // init internal status vars - cmdState = cInit; - transferState = tIdle; - rnFrom.clear(); - - // reset control connection input buffer, clear previous command - cmdLine.clear(); - cmdString.clear(); - parameters.clear(); - command = 0; - - // free any used fileBuffer - freeBuffer(); -} - -void FtpServer::handleFTP() -{ - // - // control connection state sequence is - // cInit - // | - // V - // cWait - // | - // V - // cCheck -----------+ - // | | (no username but password set) - // V | - // cUserId ----------+---+ - // | | | - // +<--------------+ | - // V | (no password set) - // cPassword | - // | | - // +<------------------+ - // V - // cLoginOk - // | - // V - // cProcess - // - - // if ((int32_t)(millisDelay - millis()) > 0) - // return; - - if (cmdState == cInit) - { - if (control.connected()) - { - abortTransfer(); - disconnectClient(false); - } - iniVariables(); - cmdState = cWait; - } - - else if (cmdState == cWait) // FTP control server waiting for connection - { - if (controlServer.hasClient()) - { - control = controlServer.available(); - - // wait 10s for login command - updateTimeout(10); - cmdState = cCheck; - } - } - - else if (cmdState == cCheck) // FTP control server check/setup control connection - { - if (control.connected()) // A client connected, say "220 Hello client!" - { - FTP_DEBUG_MSG("control server got connection from %s:%d", - control.remoteIP().toString().c_str(), control.remotePort()); - - FTP_SEND_MSG(220, "(espFTP " FTP_SERVER_VERSION ")"); - - if (_FTP_USER.length()) - { - // FTP_SEND_MSG(332, "Need account for login."); - cmdState = cUserId; - } - else if (_FTP_PASS.length()) - { - // FTP_SEND_MSG(331, "Please specify the password."); - cmdState = cPassword; - } - else - { - cmdState = cLoginOk; - } - } - } - - else if (cmdState == cLoginOk) // tell client "Login ok!" - { - FTP_SEND_MSG(230, "Login successful."); - updateTimeout(sTimeOut); - cmdState = cProcess; - } - - // - // all other command states need to process commands froms control connection - // - else if (readChar() > 0) - { - // enforce USER than PASS commands before anything else - if (((cmdState == cUserId) && (FTP_CMD(USER) != command)) || - ((cmdState == cPassword) && (FTP_CMD(PASS) != command))) - { - FTP_SEND_MSG(530, "Please login with USER and PASS."); - FTP_DEBUG_MSG("ignoring before login: cwd=%s cmd[%x]=%s, params='%s'", cwd.c_str(), command, cmdString.c_str(), parameters.c_str()); - command = 0; - return; - } - - // process the command - int8_t rc = processCommand(); - // returns - // -1 : command processing indicates, we have to close control (e.g. QUIT) - // 0 : not yet finished, just call processCommend() again - // 1 : finished - if (rc < 0) - { - cmdState = cInit; - } - if (rc > 0) - { - // clear current command, so readChar() can fetch the next command - command = 0; - - // command was successful, update command state - if (cmdState == cUserId) - { - if (_FTP_PASS.length()) - { - // wait 10s for PASS command - updateTimeout(10); - FTP_SEND_MSG(331, "Please specify the password."); - cmdState = cPassword; - } - else - { - cmdState = cLoginOk; - } - } - else if (cmdState == cPassword) - { - cmdState = cLoginOk; - } - else - { - updateTimeout(sTimeOut); - } - } - } - - // - // general connection handling - // (if we have an established control connection) - // - if (cmdState >= cCheck) - { - // detect lost/closed by remote connections - if (!control.connected() || !control) - { - cmdState = cInit; - FTP_DEBUG_MSG("client lost or disconnected"); - } - - // check for timeout - if (!((int32_t)(millisEndConnection - millis()) > 0)) - { - FTP_SEND_MSG(530, "Timeout."); - FTP_DEBUG_MSG("client connection timed out"); - cmdState = cInit; - } - - // handle data file transfer - if (transferState == tRetrieve) // Retrieve data - { - if (!doRetrieve()) - { - closeTransfer(); - transferState = tIdle; - } - } - else if (transferState == tStore) // Store data - { - if (!doStore()) - { - closeTransfer(); - transferState = tIdle; - } - } - } -} - -void FtpServer::disconnectClient(bool gracious) -{ - FTP_DEBUG_MSG("Disconnecting client"); - abortTransfer(); - if (gracious) - { - FTP_SEND_MSG(221, "Goodbye."); - } - else - { - FTP_SEND_MSG(231, "Service terminated."); - } - control.stop(); -} - -int8_t FtpServer::processCommand() -{ - // assume successful operation by default - int8_t rc = 1; - - // make the full path of parameters (even if this makes no sense for all commands) - String path = getFileName(parameters, true); - FTP_DEBUG_MSG("processing: cmd=%s[%x], params='%s' (cwd='%s')", cmdString.c_str(), command, parameters.c_str()); - - /////////////////////////////////////// - // // - // ACCESS CONTROL COMMANDS // - // // - /////////////////////////////////////// - - // - // USER - Provide username - // - if (FTP_CMD(USER) == command) - { - if (_FTP_USER.length() && (_FTP_USER != parameters)) - { - FTP_SEND_MSG(430, "User not found."); - command = 0; - rc = 0; - } - else - { - FTP_DEBUG_MSG("USER ok"); - } - } - - // - // PASS - Provide password - // - else if (FTP_CMD(PASS) == command) - { - if (_FTP_PASS.length() && (_FTP_PASS != parameters)) - { - FTP_SEND_MSG(430, "Password invalid."); - command = 0; - rc = 0; - } - else - { - FTP_DEBUG_MSG("PASS ok"); - } - } - - // - // QUIT - // - else if (FTP_CMD(QUIT) == command) - { - disconnectClient(); - rc = -1; - } - - // - // NOOP - // - else if (FTP_CMD(NOOP) == command) - { - FTP_SEND_MSG(200, "Zzz..."); - } - - // - // CDUP - Change to Parent Directory - // - else if (FTP_CMD(CDUP) == command) - { - // up one level - cwd = getPathName("", false); - FTP_SEND_MSG(250, "Directory successfully changed."); - } - - // - // CWD - Change Working Directory - // - else if (FTP_CMD(CWD) == command) - { - if (parameters == F(".")) // 'CWD .' is the same as PWD command - { - command = FTP_CMD(PWD); // make CWD a PWD command ;-) - rc = 0; // indicate we need another processCommand() call - } - else if (parameters == F("..")) // 'CWD ..' is the same as CDUP command - { - command = FTP_CMD(CDUP); // make CWD a CDUP command ;-) - rc = 0; // indicate we need another processCommand() call - } - else - { -#if (defined esp8266FTPServer_SPIFFS) - // SPIFFS has no directories, it's always ok - cwd = path; - FTP_SEND_MSG(250, "Directory successfully changed."); -#else - // check if directory exists - file = THEFS.open(path, "r"); - if (file.isDirectory()) - { - cwd = path; - FTP_SEND_MSG(250, "Directory successfully changed."); - } - else - { - FTP_SEND_MSG(550, "Failed to change directory."); - } - file.close(); -#endif - } - } - - // - // PWD - Print Directory - // - else if (FTP_CMD(PWD) == command) - { - FTP_SEND_MSG(257, "\"%s\" is the current directory.", cwd.c_str()); - } - - /////////////////////////////////////// - // // - // TRANSFER PARAMETER COMMANDS // - // // - /////////////////////////////////////// - - // - // MODE - Transfer Mode - // - else if (FTP_CMD(MODE) == command) - { - if (parameters == F("S")) - FTP_SEND_MSG(504, "Only S(tream) mode is suported"); - else - FTP_SEND_MSG(200, "Mode set to S."); - } - - // - // PASV - Passive data connection management - // - else if (FTP_CMD(PASV) == command) - { - // stop a possible previous data connection - data.stop(); - // tell client to open data connection to our ip:dataPort - dataPort = FTP_DATA_PORT_PASV; - dataPassiveConn = true; - String ip = control.localIP().toString(); - ip.replace(".", ","); - FTP_SEND_MSG(227, "Entering Passive Mode (%s,%d,%d).", ip.c_str(), dataPort >> 8, dataPort & 255); - } - - // - // PORT - Data Port, Active data connection management - // - else if (FTP_CMD(PORT) == command) - { - if (data) - data.stop(); - - // parse IP and data port of "PORT ip,ip,ip,ip,port,port" - uint8_t parsecount = 0; - uint8_t tmp[6]; - const char *p = parameters.c_str(); - while (parsecount < sizeof(tmp)) - { - tmp[parsecount++] = atoi(p); - p = strchr(p, ','); - if (NULL == p || *(++p) == '\0') - break; - } - if (parsecount < sizeof(tmp)) - { - FTP_SEND_MSG(501, "Can't interpret parameters"); - } - else - { - // copy first 4 bytes = IP - for (uint8_t i = 0; i < 4; ++i) - dataIP[i] = tmp[i]; - // data port is 5,6 - dataPort = tmp[4] * 256 + tmp[5]; - FTP_SEND_MSG(200, "PORT command successful"); - dataPassiveConn = false; - FTP_DEBUG_MSG("Data connection management Active, using %s:%u", dataIP.toString().c_str(), dataPort); - } - } - - // - // STRU - File Structure - // - else if (FTP_CMD(STRU) == command) - { - if (parameters == F("F")) - FTP_SEND_MSG(504, "Only F(ile) is suported"); - else - FTP_SEND_MSG(200, "Structure set to F."); - } - - // - // TYPE - Data Type - // - else if (FTP_CMD(TYPE) == command) - { - if (parameters == F("A")) - FTP_SEND_MSG(200, "TYPE is now ASII."); - else if (parameters == F("I")) - FTP_SEND_MSG(200, "TYPE is now 8-bit Binary."); - else - FTP_SEND_MSG(504, "Unrecognised TYPE."); - } - - /////////////////////////////////////// - // // - // FTP SERVICE COMMANDS // - // // - /////////////////////////////////////// - - // - // ABOR - Abort - // - else if (FTP_CMD(ABOR) == command) - { - abortTransfer(); - FTP_SEND_MSG(226, "Data connection closed"); - } - - // - // DELE - Delete a File - // - else if (FTP_CMD(DELE) == command) - { - if (parameters.length() == 0) - FTP_SEND_MSG(501, "No file name"); - else - { - if (!THEFS.exists(path)) - { - FTP_SEND_MSG(550, "Delete operation failed, file '%s' not found.", path.c_str()); - } - else if (THEFS.remove(path)) - { - FTP_SEND_MSG(250, "Delete operation successful."); - } - else - { - FTP_SEND_MSG(450, "Delete operation failed."); - } - } - } - - // - // LIST - List directory contents - // MLSD - Listing for Machine Processing (see RFC 3659) - // NLST - Name List - // - else if ((FTP_CMD(LIST) == command) || (FTP_CMD(MLSD) == command) || (FTP_CMD(NLST) == command)) - { - rc = dataConnect(); // returns -1: no data connection, 0: need more time, 1: data ok - if (rc < 0) - { - FTP_SEND_MSG(425, "No data connection"); - rc = 1; // mark command as processed - } - else if (rc > 0) - { - FTP_SEND_MSG(150, "Accepted data connection"); - uint16_t dirCount = 0; - Dir dir = THEFS.openDir(path); - while (dir.next()) - { - ++dirCount; - bool isDir = false; - String fn = dir.fileName(); - if (cwd == FPSTR(aSlash) && fn[0] == '/') - fn.remove(0, 1); - isDir = dir.isDirectory(); - if (FTP_CMD(LIST) == command) - { - if (isDir) - { - data.printf_P(PSTR("+d\r\n,\t%s\r\n"), fn.c_str()); - } - else - { - data.printf_P(PSTR("+r,s%lu\r\n,\t%s\r\n"), (uint32_t)dir.fileSize(), fn.c_str()); - } - } - else if (FTP_CMD(MLSD) == command) - { - // "modify=20170122163911;type=dir;UNIX.group=0;UNIX.mode=0775;UNIX.owner=0; dirname" - // "modify=20170121000817;size=12;type=file;UNIX.group=0;UNIX.mode=0644;UNIX.owner=0; filename" - file = dir.openFile("r"); - data.printf_P(PSTR("modify=%s;UNIX.group=0;UNIX.owner=0;UNIX.mode="), makeDateTimeStr(file.getLastWrite()).c_str()); - file.close(); - if (isDir) - { - data.printf_P(PSTR("0755;type=dir; ")); - } - else - { - data.printf_P(PSTR("0644;size=%lu;type=file; "), (uint32_t)dir.fileSize()); - } - data.printf_P(PSTR("%s\r\n"), fn.c_str()); - } - else if (FTP_CMD(NLST) == command) - { - data.println(fn); - } - else - { - FTP_DEBUG_MSG("Implemetation of %s [%x] command - internal BUG", cmdString.c_str(), command); - } - } - - if (FTP_CMD(MLSD) == command) - { - control.println(F("226-options: -a -l\r\n")); - } - FTP_SEND_MSG(226, "%d matches total", dirCount); - } -#if defined ESP32 - File root = THEFS.open(cwd); - if (!root) - { - FTP_SEND_MSG(550, "Can't open directory " + cwd); - // return; - } - else - { - // if(!root.isDirectory()){ - // FTP_DEBUG_MSG("Not a directory: '%s'", cwd.c_str()); - // return; - // } - - File file = root.openNextFile(); - while (file) - { - if (file.isDirectory()) - { - data.println("+r,s <DIR> " + String(file.name())); - // Serial.print(" DIR : "); - // Serial.println(file.name()); - // if(levels){ - // listDir(fs, file.name(), levels -1); - // } - } - else - { - String fn, fs; - fn = file.name(); - // fn.remove(0, 1); - fs = String(file.size()); - data.println("+r,s" + fs); - data.println(",\t" + fn); - nm++; - } - file = root.openNextFile(); - } - FTP_SEND_MSG(226, "%s matches total", nm); - } -#endif - data.stop(); - } - -#if defined ESP32 - // - // FIXME MLSD ESP32 - // - else if (!strcmp(command, "MLSD")) - { - File root = THEFS.open(cwd); - // if(!root){ - // control.println( "550, "Can't open directory " + cwd ); - // // return; - // } else { - // if(!root.isDirectory()){ - // Serial.println("Not a directory"); - // return; - // } - - File file = root.openNextFile(); - while (file) - { - // if(file.isDirectory()){ - // data.println( "+r,s <DIR> " + String(file.name())); - // // Serial.print(" DIR : "); - // // Serial.println(file.name()); - // // if(levels){ - // // listDir(fs, file.name(), levels -1); - // // } - // } else { - String fn, fs; - fn = file.name(); - fn.remove(0, 1); - fs = String(file.size()); - data.println("Type=file;Size=" + fs + ";" + "modify=20000101160656;" + " " + fn); - nm++; - // } - file = root.openNextFile(); - } - FTP_SEND_MSG(226, "-options: -a -l"); - FTP_SEND_MSG(226, "%d matches total", nm); - // } - data.stop(); - } - // - // NLST - // - else if (!strcmp(command, "NLST")) - { - File root = THEFS.open(cwd); - if (!root) - { - FTP_SEND_MSG(550, "Can't open directory %s\n"), cwd.c_str()); - } - else - { - File file = root.openNextFile(); - while (file) - { - data.println(file.name()); - nm++; - file = root.openNextFile(); - } - FTP_SEND_MSG(226, "%d matches total", nm); - } - data.stop(); - } -#endif - - // - // RETR - Retrieve - // - else if (FTP_CMD(RETR) == command) - { - if (parameters.length() == 0) - { - FTP_SEND_MSG(501, "No file name"); - } - else - { - // open the file if not opened before (when re-running processCommand() since data connetion needs time) - if (!file) - file = THEFS.open(path, "r"); - if (!file) - { - FTP_SEND_MSG(550, "File '%s' not found.", parameters.c_str()); - } - else if (!file.isFile()) - { - FTP_SEND_MSG(450, "Cannot open file \"%s\".", parameters.c_str()); - } - else - { - rc = dataConnect(); // returns -1: no data connection, 0: need more time, 1: data ok - if (rc < 0) - { - FTP_SEND_MSG(425, "No data connection"); - rc = 1; // mark command as processed - } - else if (rc > 0) - { - transferState = tRetrieve; - millisBeginTrans = millis(); - bytesTransfered = 0; - uint32_t fs = file.size(); - if (allocateBuffer(fs > 32768 ? 32768 : fs)) - { - FTP_DEBUG_MSG("Sending file '%s'", path.c_str()); - FTP_SEND_MSG(150, "%lu bytes to download", fs); - } - else - { - closeTransfer(); - FTP_SEND_MSG(451, "Internal error. Not enough memory."); - } - } - } - } - } - - // - // STOR - Store - // - else if (FTP_CMD(STOR) == command) - { - if (parameters.length() == 0) - { - FTP_SEND_MSG(501, "No file name."); - } - else - { - FTP_DEBUG_MSG("STOR '%s'", path.c_str()); - if (!file) - file = THEFS.open(path, "w"); - if (!file) - { - FTP_SEND_MSG(451, "Cannot open/create \"%s\"", path.c_str()); - } - else - { - rc = dataConnect(); // returns -1: no data connection, 0: need more time, 1: data ok - if (rc < 0) - { - FTP_SEND_MSG(425, "No data connection"); - file.close(); - rc = 1; // mark command as processed - } - else if (rc > 0) - { - transferState = tStore; - millisBeginTrans = millis(); - bytesTransfered = 0; - if (allocateBuffer(2048)) - { - FTP_DEBUG_MSG("Receiving file '%s' => %s", parameters.c_str(), path.c_str()); - FTP_SEND_MSG(150, "Connected to port %d", dataPort); - } - else - { - closeTransfer(); - FTP_SEND_MSG(451, "Internal error. Not enough memory."); - } - } - } - } - } - - // - // MKD - Make Directory - // - else if (FTP_CMD(MKD) == command) - { -#if (defined esp8266FTPServer_SPIFFS) - FTP_SEND_MSG(550, "Create directory operation failed."); //not support on SPIFFS -#else - FTP_DEBUG_MSG("mkdir(%s)", path.c_str()); - if (THEFS.mkdir(path)) - { - FTP_SEND_MSG(257, "\"%s\" created.", path.c_str()); - } - else - { - FTP_SEND_MSG(550, "Create directory operation failed."); - } -#endif - } - - // - // RMD - Remove a Directory - // - else if (FTP_CMD(RMD) == command) - { -#if (defined esp8266FTPServer_SPIFFS) - FTP_SEND_MSG(550, "Remove directory operation failed."); //not support on SPIFFS -#else - // check directory for files - Dir dir = THEFS.openDir(path); - if (dir.next()) - { - //only delete if dir is empty! - FTP_SEND_MSG(550, "Remove directory operation failed, directory is not empty."); - } - else - { - THEFS.rmdir(path); - FTP_SEND_MSG(250, "Remove directory operation successful."); - } -#endif - } - // - // RNFR - Rename From - // - else if (FTP_CMD(RNFR) == command) - { - if (parameters.length() == 0) - FTP_SEND_MSG(501, "No file name"); - else - { - if (!THEFS.exists(path)) - FTP_SEND_MSG(550, "File \"%s\" not found.", path.c_str()); - else - { - FTP_SEND_MSG(350, "RNFR accepted - file \"%s\" exists, ready for destination", path.c_str()); - rnFrom = path; - } - } - } - // - // RNTO - Rename To - // - else if (FTP_CMD(RNTO) == command) - { - if (rnFrom.length() == 0) - FTP_SEND_MSG(503, "Need RNFR before RNTO"); - else if (parameters.length() == 0) - FTP_SEND_MSG(501, "No file name"); - else if (THEFS.exists(path)) - FTP_SEND_MSG(553, "\"%s\" already exists.", parameters.c_str()); - else - { - FTP_DEBUG_MSG("Renaming '%s' to '%s'", rnFrom.c_str(), path.c_str()); - if (THEFS.rename(rnFrom, path)) - FTP_SEND_MSG(250, "File successfully renamed or moved"); - else - FTP_SEND_MSG(451, "Rename/move failure."); - } - rnFrom.clear(); - } - - /////////////////////////////////////// - // // - // EXTENSIONS COMMANDS (RFC 3659) // - // // - /////////////////////////////////////// - - // - // FEAT - New Features - // - else if (FTP_CMD(FEAT) == command) - { - FTP_SEND_DASHMSG(211, "Features:\r\n MLSD\r\n MDTM\r\n SIZE\r\n211 End."); - } - - // - // MDTM - File Modification Time (see RFC 3659) - // - else if (FTP_CMD(MDTM) == command) - { - file = THEFS.open(path, "r"); - if ((!file) || (0 == parameters.length())) - { - FTP_SEND_MSG(550, "Unable to retrieve time"); - } - else - { - FTP_SEND_MSG(213, "%s", makeDateTimeStr(file.getLastWrite()).c_str()); - } - file.close(); - } - - // - // SIZE - Size of the file - // - else if (FTP_CMD(SIZE) == command) - { - file = THEFS.open(path, "r"); - if ((!file) || (0 == parameters.length())) - { - FTP_SEND_MSG(450, "Cannot open file."); - } - else - { - FTP_SEND_MSG(213, "%lu", (uint32_t)file.size()); - } - file.close(); - } - - // - // SITE - System command - // - else if (FTP_CMD(SITE) == command) - { - FTP_SEND_MSG(502, "SITE command not implemented"); - } - - // - // SYST - System information - // - else if (FTP_CMD(SYST) == command) - { - FTP_SEND_MSG(215, "UNIX Type: L8"); - } - - // - // Unrecognized commands ... - // - else - { - FTP_DEBUG_MSG("Unknown command: %s [%#x], param: '%s')", cmdString.c_str(), command, parameters.c_str()); - FTP_SEND_MSG(500, "unknown command \"%s\"", cmdString.c_str()); - } - - return rc; -} - -int8_t FtpServer::dataConnect() -{ - int8_t rc = 1; // assume success - - if (!dataPassiveConn) - { - // active mode - // open our own data connection - data.stop(); - FTP_DEBUG_MSG("Open active data connection to %s:%u", dataIP.toString().c_str(), dataPort); - data.connect(dataIP, dataPort); - if (!data.connected()) - rc = -1; - } - else - { - // passive mode - // wait for data connection from the client - if (!data.connected()) - { - if (dataServer.hasClient()) - { - data.stop(); - data = dataServer.available(); - FTP_DEBUG_MSG("Got incoming (passive) data connection from %s:%u", data.remoteIP().toString().c_str(), data.remotePort()); - } - else - { - // give me more time waiting for a data connection - rc = 0; - } - } - } - return rc; -} - -uint16_t FtpServer::allocateBuffer(uint16_t desiredBytes) -{ - // allocate a big buffer for file transfers - uint16_t maxBlock = ESP.getMaxFreeBlockSize() / 2; - - if (desiredBytes > maxBlock) - desiredBytes = maxBlock; - - while (fileBuffer == NULL && desiredBytes > 0) - { - fileBuffer = (uint8_t *)malloc(desiredBytes); - if (NULL == fileBuffer) - { - FTP_DEBUG_MSG("Cannot allocate buffer for file transfer, re-trying"); - // try with less bytes - desiredBytes--; - } - else - { - fileBufferSize = desiredBytes; - } - } - return fileBufferSize; -} - -void FtpServer::freeBuffer() -{ - free(fileBuffer); - fileBuffer = NULL; -} - -bool FtpServer::doRetrieve() -{ - // data connection lost or no more bytes to transfer? - if (!data.connected() || (bytesTransfered >= file.size())) - { - return false; - } - - // how many bytes to transfer left? - uint32_t nb = (file.size() - bytesTransfered); - if (nb > fileBufferSize) - nb = fileBufferSize; - - // transfer the file - FTP_DEBUG_MSG("Transfer %d bytes fs->client", nb); - nb = file.readBytes((char *)fileBuffer, nb); - if (nb > 0) - { - data.write(fileBuffer, nb); - bytesTransfered += nb; - } - - return (nb > 0); -} - -bool FtpServer::doStore() -{ - // Avoid blocking by never reading more bytes than are available - int16_t navail = data.available(); - - if (navail > 0) - { - if (navail > fileBufferSize) - navail = fileBufferSize; - FTP_DEBUG_MSG("Transfer %d bytes client->fs", navail); - navail = data.read(fileBuffer, navail); - file.write(fileBuffer, navail); - bytesTransfered += navail; - } - - if (!data.connected() && (navail <= 0)) - { - // connection closed or no more bytes to read - return false; - } - else - { - // inidcate, we need to be called again - return true; - } -} - -void FtpServer::closeTransfer() -{ - uint32_t deltaT = (int32_t)(millis() - millisBeginTrans); - if (deltaT > 0 && bytesTransfered > 0) - { - FTP_SEND_MSG(226, "File successfully transferred, %lu ms, %f kB/s.", deltaT, float(bytesTransfered) / deltaT); - } - else - FTP_SEND_MSG(226, "File successfully transferred"); - - freeBuffer(); - file.close(); - data.stop(); -} - -void FtpServer::abortTransfer() -{ - if (transferState > tIdle) - { - file.close(); - data.stop(); - FTP_SEND_MSG(426, "Transfer aborted"); - } - freeBuffer(); - transferState = tIdle; -} - -// Read a char from client connected to ftp server -// -// returns: -// -1 if cmdLine too long -// 0 cmdLine still incomplete (no \r or \n received yet) -// 1 cmdLine processed, command and parameters available - -int8_t FtpServer::readChar() -{ - // only read/parse, if the previous command has been fully processed! - if (command) - return 1; - - while (control.available()) - { - char c = control.read(); - // FTP_DEBUG_MSG("readChar() cmdLine='%s' <= %c", cmdLine.c_str(), c); - - // substitute '\' with '/' - if (c == '\\') - c = '/'; - - // nl detected? then process line - if (c == '\n' || c == '\r') - { - cmdLine.trim(); - - // but only if we got at least chars in the line! - if (0 == cmdLine.length()) - break; - - // search for space between command and parameters - int pos = cmdLine.indexOf(FPSTR(aSpace)); - if (pos > 0) - { - parameters = cmdLine.substring(pos + 1); - parameters.trim(); - cmdLine.remove(pos); - } - else - { - parameters.remove(0); - } - cmdString = cmdLine; - - // convert command to upper case - cmdString.toUpperCase(); - - // convert the (up to 4 command chars to numerical value) - command = *(const uint32_t *)cmdString.c_str(); - - // clear cmdline - cmdLine.clear(); - FTP_DEBUG_MSG("readChar() success, command=%x, cmdString='%s', params='%s'", command, cmdString.c_str(), parameters.c_str()); - return 1; - } - else - { - // just add char - cmdLine += c; - if (cmdLine.length() > FTP_CMD_SIZE) - { - cmdLine.clear(); - FTP_SEND_MSG(500, "Line too long"); - } - } - } - return 0; -} - -// Get the complete path from cwd + parameters or complete filename from cwd + parameters -// -// 3 possible cases: parameters can be absolute path, relative path or only the name -// -// returns: -// path WITHOUT file-/dirname (fullname=false) -// full path WITH file-/dirname (fullname=true) -String FtpServer::getPathName(const String ¶m, bool fullname) -{ - String tmp; - - // is param an absoulte path? - if (param[0] == '/') - { - tmp = param; - } - else - { - // start with cwd - tmp = cwd; - - // if param != "" then add param - if (param.length()) - { - if (!tmp.endsWith(FPSTR(aSlash))) - tmp += '/'; - tmp += param; - } - // => tmp becomes cdw [ + '/' + param ] - } - - // strip filename - if (!fullname) - { - // search rightmost '/' - int lastslash = tmp.lastIndexOf(FPSTR(aSlash)); - if (lastslash >= 0) - { - tmp.remove(lastslash); - } - } - // sanetize: - // "" -> "/" - // "/some/path/" => "/some/path" - while (tmp.length() > 1 && tmp.endsWith(FPSTR(aSlash))) - tmp.remove(cwd.length() - 1); - if (tmp.length() == 0) - tmp += '/'; - return tmp; -} - -// Get the [complete] file name from cwd + parameters -// -// 3 possible cases: parameters can be absolute path, relative path or only the filename -// -// returns: -// filename or filename with complete path -String FtpServer::getFileName(const String ¶m, bool fullFilePath) -{ - // build the filename with full path - String tmp = getPathName(param, true); - - if (!fullFilePath) - { - // strip path - // search rightmost '/' - int lastslash = tmp.lastIndexOf(FPSTR(aSlash)); - if (lastslash > 0) - { - tmp.remove(0, lastslash); - } - } - - return tmp; -} - -// Formats YYYYMMDDHHMMSS from a time_t timestamp -// -// uses the buf of the FtpServer to store the date string -// -// parameters: -// timestamp -// -// return: -// pointer to buf[0] - -String FtpServer::makeDateTimeStr(time_t ft) -{ - struct tm *_tm = gmtime(&ft); - String tmp; - tmp.reserve(17); - strftime((char *)tmp.c_str(), 17, "%Y%m%d%H%M%S", _tm); - return tmp; -} - -void FtpServer::updateTimeout(uint16_t s) -{ - millisEndConnection = s; - millisEndConnection *= 60000UL; - millisEndConnection += millis(); -} diff --git a/espFtpServer.h b/espFtpServer.h deleted file mode 100644 index d4b3f44..0000000 --- a/espFtpServer.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * FTP SERVER FOR ESP8266/ESP32 - * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) - * based on Jean-Michel Gallego's work - * modified to work with esp8266 SPIFFS by David Paiva (david@nailbuster.com) - * modified to work with esp8266 LitteFS by Daniel Plasa dplasa@gmail.com - * Also done some code reworks and all string contants are now in flash memory - * by using F(), PSTR() ... on the string literals. - * - * 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/>. - */ - -#ifndef ESP_FTP_SERVER_H -#define ESP_FTP_SERVER_H - -#include <WiFiClient.h> -#include <FS.h> - -/******************************************************************************* - ** ** - ** DEFINITIONS FOR FTP SERVER ** - ** ** - *******************************************************************************/ - -// -// DEBUG via Serial Console -// Please select in your Arduino IDE menu Tools->Debug Port to enable debugging. -// (This will provide DEBUG_ESP_PORT at compile time.) -// - -// Use ESP8266 Core Debug functionality -#ifdef DEBUG_ESP_PORT -#define FTP_DEBUG_MSG(fmt, ...) \ - do \ - { \ - DEBUG_ESP_PORT.printf_P(PSTR("[FTP] " fmt "\n"), ##__VA_ARGS__); \ - yield(); \ - } while (0) -#else -#define FTP_DEBUG_MSG(...) -#endif - -#define FTP_SERVER_VERSION "0.9.3-20200526" - -#define FTP_CTRL_PORT 21 // Command port on wich server is listening -#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode -#define FTP_TIME_OUT 5 // Disconnect client after 5 minutes of inactivity -#define FTP_CMD_SIZE 127 // allow max. 127 chars in a received command - -class FtpServer -{ -public: - // contruct an instance of the FTP server using a - // given FS object, e.g. SPIFFS or LittleFS - FtpServer(FS &_FSImplementation); - - // starts the FTP server with username and password, - // either one can be empty to enable anonymous ftp - void begin(const String &uname, const String &pword); - - // stops the FTP server - void stop(); - - // set the FTP server's timeout in seconds - void setTimeout(uint16_t timeout = FTP_TIME_OUT * 60); - - // needs to be called frequently (e.g. in loop() ) - // to process ftp requests - void handleFTP(); - -private: - FS &THEFS; - - enum internalState - { - cInit = 0, - cWait, - cCheck, - cUserId, - cPassword, - cLoginOk, - cProcess, - - tIdle, - tRetrieve, - tStore - }; - void iniVariables(); - void disconnectClient(bool gracious = true); - int8_t processCommand(); - int8_t dataConnect(); - - bool doRetrieve(); - bool doStore(); - void closeTransfer(); - void abortTransfer(); - uint16_t allocateBuffer(uint16_t desiredBytes); - void freeBuffer(); - - String getPathName(const String ¶m, bool includeLast = false); - String getFileName(const String ¶m, bool fullFilePath = false); - String makeDateTimeStr(time_t fileTime); - int8_t readChar(); - void updateTimeout(uint16_t timeout); - - WiFiClient control; - WiFiClient data; - - File file; - - bool dataPassiveConn = true; // PASV (passive) mode is our default - IPAddress dataIP; // IP address for PORT (active) mode - uint16_t dataPort = // holds our PASV port number or the port number provided by PORT - FTP_DATA_PORT_PASV; - - uint32_t command; // numeric command code of command sent by the client - String cmdLine; // command line as read from client - String cmdString; // command as textual representation - String parameters; // parameters sent by client - String cwd; // the current directory - String rnFrom; // previous command was RNFR, this is the source file name - - internalState cmdState, // state of ftp control connection - transferState; // state of ftp data connection - uint16_t sTimeOut = // disconnect after 5 min of inactivity - FTP_TIME_OUT * 60; - uint32_t millisEndConnection, // - millisBeginTrans, // store time of beginning of a transaction - bytesTransfered; // - uint8_t *fileBuffer = NULL; // pointer to buffer for file transfer (by allocateBuffer) - uint16_t fileBufferSize; // size of buffer - String _FTP_USER; // usename - String _FTP_PASS; // password -}; - -#endif // ESP_FTP_SERVER_H |