ESP8266_swiss_army_board/src/app/FTPServer.h

1008 lines
33 KiB
C++

#ifndef FTPSERVER_H
#define FTPSERVER_H
#include <SD.h>
#include "TCPServer.h"
#include "utilities.h"
#include "Dictionary.h"
//#define DEBUG_FTPS
#define READ_BUFFER_SIZE 2048 //2048 is max to read sd card, more will crash
template <typename T>
class FTPServer : public TCPServer<T>
{
public:
enum FTPClientState {INIT, WAITING_FOR_COMMANDS};
enum FTPClientDataTransfer {NONE = 0, LIST_DF, NLST_DF, RETR_DF, STOR_DF, APPE_DF};
enum FileTransferStatus {OK, NOT_FOUND, NO_FILE_NAME};
enum BinaryFlag {OFF = 0, ON};
enum FtpMsgCode {_150, _200, _215, _220, _221, _230, _226, _227, _250, _257, _331, _350, _451, _5_502, _504, _530, _550 };
FTPServer(uint16_t port = 21, SDClass *sdClass = NULL, const char *login = NULL, const char *password = NULL, uint8_t maxClient = MAX_CLIENT, uint16_t clientCommandDataBufferSize = 255) : TCPServer<T>(port, maxClient, clientCommandDataBufferSize),
_login(NULL),
_password(NULL),
_dataPort(1024),
_dataServer(_dataPort),
_sdClass(sdClass)
{
if (login != NULL)
{
if (strlen(login) > 0)
{
_login = (char *)malloc((sizeof(char) * strlen(login)) + 1);
strcpy(_login, login);
}
}
if (password != NULL)
{
if (strlen(password) > 0)
{
_password = (char *)malloc((sizeof(char) * strlen(password)) + 1);
strcpy(_password, password);
}
}
_dataServer.begin(_dataPort);
}
void setCustomDataPort(unsigned int port)
{
_dataPort = port;
}
virtual ~FTPServer()
{
_dataServer.stop();
free(_login); free(_password);
}
virtual void stop()
{
if(TCPServer<T>::_serverStarted)
{
_dataServer.stop();
TCPServer<T>::stop();
}
}
virtual void setFTPDir(const char *FTPDir)
{
_FTPDir = FTPDir;
}
protected:
virtual T* createNewClient(WiFiClient wc)
{
return new T(wc, TCPServer<T>::freeClientId(), TCPServer<T>::_clientDataBufferSize);
}
virtual void greetClient(T *client)
{
//The first time the client connects, we send the server's information
client->_client.println("220 Welcome to the ESP8266SwissArmyBoard embedded FTP server.");
client->_clientState = TCPClient::HANDLED;
}
ICACHE_RAM_ATTR virtual void processClientData(T *client)
{
/*if (client->_waitingForDataConnection)
{
//#ifdef DEBUG_FTPS
//Serial.println("Listening for new data client");
//#endif
WiFiClient dataClient = _dataServer.available();
if(dataClient)
{
#ifdef DEBUG_FTPS
Serial.println("Data client returns true");
#endif
if (dataClient.connected())
{
client->_waitingForDataConnection = false;
client->setDataClient(dataClient);
#ifdef DEBUG_FTPS
Serial.println("Data client accepted successfully");
#endif
}
else
dataClient.stop();
}
}*/
switch(client->_dataTransferPending)
{
case LIST_DF:
if (client->_dataClient.connected())
{
#ifdef DEBUG_FTPS
Serial.println("Listing started");
#endif
client->_client.println("150 File status okay.");
if(sendFSTree(client))
{
client->_client.println("226 Closing data connection.");
}
else
{
client->_client.println("451 Requested action aborted.");
}
client->closeDataConnection();
client->_dataTransferPending = NONE;
}
break;
case RETR_DF:
if (client->_dataClient.connected())
{
if(client->_fileSentBytes == 0)
client->_client.println("150 File status okay.");
FileTransferStatus fts;
if(!sendFile(client,&fts))//File was sent or error occured
{
//we check the return code
if(fts == OK)
{
client->_client.println("226 Closing data connection.");
client->closeDataConnection();
client->_dataTransferPending = NONE;
}
else if(fts == NOT_FOUND)
{
client->_client.println("451 File not found.");
client->closeDataConnection();
client->_dataTransferPending = NONE;
}
}
}
else if(client->_fileSentBytes != 0)
{
client->_client.println("426 Connection closed; transfer aborted.");
client->closeDataConnection();
client->_dataTransferPending = NONE;
}
break;
case STOR_DF:
if (client->_dataClient.connected() || client->_dataClient.available())//Here we need to check if client has some data available for reading. IMPORTANT
{
if(client->_dataClient.available())
{
client->_fileIsBeeingReceived = true;
#ifdef DEBUG_FTPS
Serial.printf("receiving file %s\n", client->_currentFile);
#endif
FileTransferStatus fts = OK;
if(!writeToSdCard(client, &fts)) //An error occured
{
if(fts == NO_FILE_NAME)
{
client->_client.println("501 No file name given.");
}
else
{
client->_client.println("451 Requested action aborted: local error in processing.");
}
client->closeDataConnection();
client->_fileIsBeeingReceived = false;
client->_dataTransferPending = NONE;
}
}
}
//If no data connection exists and no error was raised during writting, then it could be just a file creation with no data connection opened
else if(client->_fileIsBeeingReceived || (client->_dataClientConnected || millis() - client->_actionTimeout > 5000))
{
if(client->_fileRecvBytes == 0) //File was just created empty
{
if(_sdClass->exists(client->_currentFile))
_sdClass->remove(client->_currentFile);
File file2create = _sdClass->open(client->_currentFile, FILE_WRITE);
file2create.close();
#ifdef DEBUG_FTPS
Serial.println("File just created");
#endif
}
else
{
#ifdef DEBUG_FTPS
Serial.println("Whole file received");
#endif
}
client->_client.println("226 Closing data connection.");
client->_fileIsBeeingReceived = false;
client->closeDataConnection();
client->_dataTransferPending = NONE;
client->_fileRecvBytes = 0;
}
break;
case APPE_DF:
if (client->_dataClient.connected() || client->_dataClient.available())//Here we need to check if client has some data for reading. IMPORTANT
{
if(client->_dataClient.available())
{
client->_fileIsBeeingReceived = true;
#ifdef DEBUG_FTPS
Serial.printf("appending to file %s\n", client->_currentFile);
#endif
FileTransferStatus fts = OK;
if(!writeToSdCard(client, &fts, true)) //An error occured
{
if(fts == NO_FILE_NAME)
{
client->_client.println("501 No file name given.");
}
else
{
client->_client.println("451 Requested action aborted: local error in processing.");
}
client->closeDataConnection();
client->_fileIsBeeingReceived = false;
client->_dataTransferPending = NONE;
}
}
}
//If the connection is closed and data has been received, then we got the whole file
else if(client->_fileIsBeeingReceived)
{
#ifdef DEBUG_FTPS
Serial.println("Whole file received");
#endif
client->_client.println("226 Closing data connection.");
client->_fileIsBeeingReceived = false;
client->closeDataConnection();
client->_dataTransferPending = NONE;
client->_fileRecvBytes = 0;
}
break;
}
#ifdef DEBUG_FTPS
if (client->_newDataAvailable)
{
Serial.print("Client --> "); Serial.print(client->_id); Serial.print(" : "); Serial.print((char *)client->_data); Serial.println("#");
}
#endif
if(client->_dataSize > 0)
{
switch (client->_ftpClientState)
{
case WAITING_FOR_COMMANDS:
processCommands(client);
break;
case INIT:
_FTPDir ? client->setCurrentDirectory(_FTPDir) : client->setCurrentDirectory("");
client->_ftpClientState = WAITING_FOR_COMMANDS;
break;
}
}
}
private:
void processCommands(T *client)
{
if (!client->parseCommandAndParameters()) //Failed to retrieve command and parameters
{
//We can close the connection or do other things
return;
}
#ifdef DEBUG_FTPS
Serial.printf("Issued command : '%s'\n", client->_ftpCommand);
Serial.println("Get params :");
for (int i = 0; i < client->_cmdParameters->count(); i++)
{
Serial.print(client->_cmdParameters->getParameter(i)); Serial.print(" : "); Serial.printf("'%s'\n", client->_cmdParameters->getAt(i)->getString());
}
#endif
if (strcmp(client->_ftpCommand, "USER") == 0)
{
//We check if we set a login and a password :
if (_login != NULL && _password != NULL)
{
if (client->_cmdParameters->count() > 0)
{
client->_client.println("331 User name okay, need password.");
client->setUsername(client->_cmdParameters->getAt(0)->getString());
}
else
sendInfoResponse(_530, client);//client->_client.println("530 Username required.");
}
else //The ftp access is open
{
client->_loggedIn = true;
client->_client.println("230 User logged in, proceed.");
}
}
else if (strcmp(client->_ftpCommand, "PASS") == 0)
{
//We now check if the username and password correspond
if (client->_cmdParameters->count() > 0)
{
if (strcmp(_login, client->_username) == 0 && strcmp(_password, client->_cmdParameters->getAt(0)->getString()) == 0)
{
client->_loggedIn = true;
client->_client.println("230 User logged in, proceed.");
}
else
{
sendInfoResponse(_530, client, "Wrong username or password !");//client->_client.println("530 Wrong username or password !.");
}
}
else
{
sendInfoResponse(_530, client,"");//client->_client.println("530 Password required.");
}
}
else if (strcmp(client->_ftpCommand, "PWD") == 0) //We set the default directory
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
client->_client.printf("257 \"%s\"\r\n", client->_currentDirectory);
}
else if (strcmp(client->_ftpCommand, "TYPE") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
switch (client->_cmdParameters->getAt(0)->getString()[0])
{
case 'I':
client->_binaryFlag = ON;
sendInfoResponse(_200, client,"");//client->_client.println("200 Command okay.");
break;
case 'L':
client->_binaryFlag = ON;
sendInfoResponse(_200, client,"");//client->_client.println("200 Command okay.");
break;
case 'A':
client->_binaryFlag = OFF;
sendInfoResponse(_200, client,"");//client->_client.println("200 Command okay.");
break;
default:
client->_client.println("504 Command not implemented for TYPE.");
}
}
else
{
client->_client.println("504 Command not implemented for TYPE.");
}
}
else if (strcmp(client->_ftpCommand, "PASV") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
//We need to test if we are in softAP or STA to chose the right IP
uint8_t addr[4] = {0};
if(WiFi.status() == WL_CONNECTED)
{
addr[0] = WiFi.localIP()[0];
addr[1] = WiFi.localIP()[1];
addr[2] = WiFi.localIP()[2];
addr[3] = WiFi.localIP()[3];
}else
{
addr[0] = WiFi.softAPIP()[0];
addr[1] = WiFi.softAPIP()[1];
addr[2] = WiFi.softAPIP()[2];
addr[3] = WiFi.softAPIP()[3];
}
client->_client.printf("227 Entering Passive Mode (%u,%u,%u,%u,%d,%d).\r\n", addr[0], addr[1], addr[2], addr[3], _dataPort / 256, _dataPort % 256);
#ifdef DEBUG_FTPS
Serial.println("Opening data server for new data client");
#endif
//We listen for 100 ms directly to accept the client
uint64_t timeOut(millis());
while(true)
{
WiFiClient dataClient = _dataServer.available();
//Serial.printf("Client state : %d\n", dataClient.status());
if (dataClient)
{
//Serial.printf("Data client is true , available : %d\n", dataClient.available());
if (dataClient.connected())//Connected returns true if the status() is ESTABLISHED or available() returns true
{
client->setDataClient(dataClient);
#ifdef DEBUG_FTPS
Serial.println("Data client accepted from loop");
#endif
break;
}
else
dataClient.stop();
}
if(millis() - timeOut > 100)
{
client->_waitingForDataConnection = true;
break;
}
yield();
}
}
else if (strcmp(client->_ftpCommand, "LIST") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
//We inform that a data transfer is pending
client->_dataTransferPending = LIST_DF;
}
else if (strcmp(client->_ftpCommand, "CWD") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
//Go back one level
if (strcmp(client->_cmdParameters->getAt(0)->getString(), "..") == 0)
{
char *dirCopy = (char *)malloc((sizeof(char) * strlen(client->_currentDirectory)) + 1);
strcpy(dirCopy, client->_currentDirectory);
char *p = lastIndexOf(dirCopy, '/');
if (dirCopy == p)
{
*(p + 1) = '\0';
}
else
*(p) = '\0';
#ifdef DEBUG_FTPS
Serial.printf("Final dir : %s\n", dirCopy);
#endif
client->setCurrentDirectory(dirCopy);
free(dirCopy);
}
else if (strcmp(client->_cmdParameters->getAt(0)->getString(), "/") == 0)
{
client->setCurrentDirectory("/");
}
else //If the client already nows the path, he will send it with a /
{
if(client->_cmdParameters->getAt(0)->getString()[0] == '/')//Then this is a name prefix
{
char *fullDirPath = constructFileNameWithPath("",client->_cmdParameters);
client->setCurrentDirectory(fullDirPath);
free(fullDirPath);
}
else
{
//Directories with spaces are now working:
char *directoryFullpath = constructFileNameWithPath(client->_currentDirectory,client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("Final dir : %s\n", directoryFullpath);
#endif
client->setCurrentDirectory(directoryFullpath);
free(directoryFullpath);
}
}
client->_client.println("250 Requested file action okay, completed.");
#ifdef DEBUG_FTPS
Serial.printf("CWD new dir : %s\n", client->_currentDirectory);
#endif
}
}
else if(strcmp(client->_ftpCommand, "RETR") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
//We save the file path to be sent
char *file2store(NULL);
if(client->_cmdParameters->getAt(0)->getString()[0] == '/')
file2store = constructFileNameWithPath("", client->_cmdParameters);
else
file2store = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("Final file path : %s\n",file2store);
#endif
client->setCurrentFile(file2store);
free(file2store);
client->_dataTransferPending = RETR_DF;
}
}
else if(strcmp(client->_ftpCommand, "MKD") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
char *dirNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
if(dirNameWithPath != NULL)
{
#ifdef DEBUG_FTPS
Serial.printf("Final dirName : #%s#\n",dirNameWithPath);
#endif
if(_sdClass->mkdir(dirNameWithPath))
{
client->_client.printf("257 \"%s\"\r\n", dirNameWithPath);
}
else
client->_client.println("550 Failed to mkdir (no spaces allowed in dir name).");
free(dirNameWithPath);
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
}
else if(strcmp(client->_ftpCommand, "RMD") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
//We have the dir name, we need to append the current directory...
char *dirNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("pathDirName to delete : #%s#\n",dirNameWithPath);
#endif
if(_sdClass->rmdir(dirNameWithPath))
{
client->_client.println("250 Requested file action okay.");
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
free(dirNameWithPath);
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
}
else if(strcmp(client->_ftpCommand, "STOR") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
client->_client.printf_P(PSTR("150 File status okay; about to open data connection.\r\n"));
//We save the file path to be sent
char *fileNameWithPath(NULL);
if(client->_cmdParameters->getAt(0)->getString()[0] == '/')
fileNameWithPath = constructFileNameWithPath("", client->_cmdParameters);
else
fileNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
client->setCurrentFile(fileNameWithPath);
#ifdef DEBUG_FTPS
Serial.printf("File to store : #%s#\n", client->_currentFile);
#endif
free(fileNameWithPath);
client->_dataTransferPending = STOR_DF;
client->startTimeout();
}
}
else if(strcmp(client->_ftpCommand, "APPE") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
client->_client.printf_P(PSTR("150 File status okay; about to open data connection.\r\n"));
//We save the file path to be sent
char *fileNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
client->setCurrentFile(fileNameWithPath);
#ifdef DEBUG_FTPS
Serial.printf("File to append : #%s#\n", client->_currentFile);
#endif
free(fileNameWithPath);
client->_dataTransferPending = APPE_DF;
}
}
else if(strcmp(client->_ftpCommand, "SYST") == 0)
{
client->_client.println("215 UNIX Type: L8");
}
else if(strcmp(client->_ftpCommand, "DELE") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
//We have the file name, we need to append the current directory...
char *file2deleteNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("file to delete : #%s#\n",file2deleteNameWithPath);
#endif
if(_sdClass->remove(file2deleteNameWithPath))
{
client->_client.println("250 Requested file action okay.");
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
free(file2deleteNameWithPath);
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
}
else if(strcmp(client->_ftpCommand, "RNFR") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0)
{
//We have the file name, we need to append the current directory...
char *fileNameWithPath(NULL);
if(client->_cmdParameters->getAt(0)->getString()[0] == '/')
fileNameWithPath = constructFileNameWithPath("", client->_cmdParameters);
else
fileNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("file to rename : #%s#\n",fileNameWithPath);
#endif
client->setCurrentFile(fileNameWithPath);
free(fileNameWithPath);
client->_client.println("350 Requested file action pending further information.");
}
else
{
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
}
}
else if(strcmp(client->_ftpCommand, "RNTO") == 0)
{
if(!client->_loggedIn)
{
sendInfoResponse(_530, client);
return;
}
if (client->_cmdParameters->count() > 0) //If the name starts with a /, we do not need to prepend the directory
{
//Here we rename the file
char *file2RenameNameWithPath(NULL);
if(client->_cmdParameters->getAt(0)->getString()[0] == '/')
file2RenameNameWithPath = constructFileNameWithPath("", client->_cmdParameters);
else
file2RenameNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
#ifdef DEBUG_FTPS
Serial.printf("file to rename to : #%s#\n",file2RenameNameWithPath);
Serial.printf("Old name : %s --> %s\n",client->_currentFile,file2RenameNameWithPath);
#endif
if(_sdClass->rename(client->_currentFile,file2RenameNameWithPath))
{
client->_client.println("250 Requested file action okay.");
}else
sendInfoResponse(_550, client,"");//client->_client.println("550 Requested action not taken.");
free(file2RenameNameWithPath);
}
}
else if(strcmp(client->_ftpCommand, "QUIT") == 0)
{
if(client->_dataTransferPending == NONE)//If no transfers are in progress
{
sendInfoResponse(_221, client);
client->_client.stop();
client->_clientState = TCPClient::ClientState::DISCARDED;
}
else
sendInfoResponse(_221, client);
}
/*else if(strcmp(client->_ftpCommand, "SIZE") == 0)
{
if (client->_cmdParameters->count() > 0)
{
client->_client.println("213 15224");
}
}*/
else
{
client->_client.println("502 Command not implemented.");
#ifdef DEBUG_FTPS
Serial.println("Command not implemented");
#endif
}
}
//Here we write the received file to the sd card
ICACHE_RAM_ATTR inline boolean writeToSdCard(T *client, FileTransferStatus *fts, boolean append = false)
{
if (client->_currentFile != NULL)
{
if(_sdClass->exists(client->_currentFile) && client->_fileRecvBytes == 0 && !append)
_sdClass->remove(client->_currentFile);
File fileBeeingReceived = _sdClass->open(client->_currentFile, FILE_WRITE);
if(fileBeeingReceived)
{
uint8_t recvBuffer[2048];
/*fileBeeingReceived.seek(client->_fileRecvBytes);*/
uint16_t size = client->_dataClient.read(recvBuffer, 2048);
fileBeeingReceived.write(recvBuffer, size);
client->_fileRecvBytes += size;
fileBeeingReceived.close();
#ifdef DEBUG_FTPS
Serial.printf("Writting : %d bytes to file\n", size);
#endif
}
else
{
return false;
}
}
else
{
*fts = NO_FILE_NAME;
return false;
}
return true;
}
//Here we send the fs tree to the ftp client
ICACHE_RAM_ATTR inline boolean sendFSTree(T *client)
{
if (client->_currentDirectory != NULL)
{
#ifdef DEBUG_FTPS
Serial.printf("Directory : %s\n",client->_currentDirectory);
#endif
File currentDirectory = _sdClass->open(client->_currentDirectory);
if (currentDirectory)
{
currentDirectory.rewindDirectory();
while (true) //May be removed in the future to improve responsiveness
{
File fileOrDir = currentDirectory.openNextFile();
if (!fileOrDir) //No more files in the directory
break;
//We try to retrieve the last modification date and we remove one day and one hour
const time_t fileModifDate = fileOrDir.getLastWrite() - 60*60;
struct tm *timeP = localtime(&fileModifDate);
//We get the month's three letter abbreviation
uint32_t monthAbbreviation = monthNumTo3LetterAbbreviation(timeP->tm_mon + 1); //+1 because in the tm struct, month goes from 0 to 11 ...
#ifdef DEBUG_FTPS
Serial.printf("Filename : %s, %s %d %d %d:%d\n", fileOrDir.name(), (char *)&monthAbbreviation, timeP->tm_mon, timeP->tm_mday, timeP->tm_hour, timeP->tm_min);
#endif
char zero_prepended[3][3] = {"","",""};
client->_dataClient.printf("%crwxrwxrwx 1 owner esp8266 %d %s %s %s:%s %s\r\n",
fileOrDir.isDirectory() ? 'd' : '-',
fileOrDir.isDirectory() ? 0 : fileOrDir.size(),
(char *)&monthAbbreviation,
dateTimeFormater(zero_prepended[0],timeP->tm_mday,'0'),
dateTimeFormater(zero_prepended[1],timeP->tm_hour ,'0'),
dateTimeFormater(zero_prepended[2],timeP->tm_min,'0'),
fileOrDir.name());
fileOrDir.close();
}
currentDirectory.close();
}
else //Failed to open directory
{
#ifdef DEBUG_FTPS
Serial.println("Failed to open directory");
#endif
return false;
}
return true;
}
return false;
}
//The binary flag needs to be taken into consideration
ICACHE_RAM_ATTR inline boolean sendFile(T *client, FileTransferStatus *fts)
{
if (client->_currentFile != NULL)
{
uint8_t sendBuffer[READ_BUFFER_SIZE];
File fileToSend = _sdClass->open(client->_currentFile);
if (fileToSend)
{
*fts = OK;
unsigned int readBytes(0);
fileToSend.seek(client->_fileSentBytes);
if(fileToSend.available())
{
readBytes = fileToSend.read(sendBuffer, READ_BUFFER_SIZE);
client->_dataClient.write(sendBuffer, readBytes);
client->_fileSentBytes += readBytes;
#ifdef DEBUG_FTPS
Serial.printf("File : bytes sent : %u\n",readBytes);
#endif
}
else //The whole file has been sent
{
fileToSend.close();
client->_fileSentBytes = 0;
return false;
}
fileToSend.close();
}
else //Failed to open file maybe not found
{
#ifdef DEBUG_FTPS
Serial.println("Failed to open file");
#endif
*fts = NOT_FOUND;
return false;
}
return true;//Still things to send
}
*fts = NOT_FOUND;
return false;
}
//This functions construct the full file path.
//ie : if the current directory is : "/somedir/subdir" and the received file name is : "my file .txt"
//then it will return : "/somedir/subdir/my file .txt"
//Note that the file name is contained in a parameter list : "my" in the first element, then "file" in the second and finally ".txt" in the third.
//Return NULL if malloc fails
// DO NOT FORGET TO FREE THE ALLOCATED STRING AFTER USING IT....
static char *constructFileNameWithPath(const char *dir, Dictionary<DictionaryHelper::StringEntity> *parameters)
{
uint16_t fileWithPathSize(strlen(dir) + 1) /*dir path plus filename*/, paramCount(parameters->count());
char *fileNameWithPath(NULL);
for(int i(0); i < paramCount; i++)
{
fileWithPathSize += strlen(parameters->getAt(i)->getString()) + 1;
}
fileNameWithPath = (char *)malloc( sizeof(char) * fileWithPathSize );
#ifdef DEBUG_FTPS
Serial.printf("Allocated string size : %d\n",fileWithPathSize);
#endif
if(fileNameWithPath == NULL)//Malloc fails
return NULL;
strcpy(fileNameWithPath, dir);
if(strcmp(fileNameWithPath, "/") != 0 && strlen(dir) != 0)
strcat(fileNameWithPath,"/");
for(int i(0); i < paramCount; i++)
{
strcat(fileNameWithPath, parameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(fileNameWithPath," ");
}
return fileNameWithPath;
}
//Error code functions
void sendInfoResponse(FtpMsgCode code, T *client, const char *msg = "")
{
switch(code)
{
case _200:
client->_client.printf_P(PSTR("200 Command okay. %s\r\n"), msg);
break;
case _221:
client->_client.printf_P(PSTR("221 Service closing control connection. %s\r\n"));
break;
case _530:
client->_client.printf_P(PSTR("530 Password required. %s\r\n"), msg);
break;
case _550:
client->_client.printf_P(PSTR("550 Requested action not taken. %s\r\n"), msg);
break;
}
}
char *_login;
char *_password;
const char *_FTPDir = NULL; //Pointer to constant string and char * cont is a constant pointer to string
uint16_t _dataPort;
WiFiServer _dataServer; //In passive mode, the FTP server opens two different ports (one for the commands and the other for the data stream)
SDClass *_sdClass;
};
#endif //FTPSERVER_H