Improving FTPserver again and again, added the rename function, code refactoring, what is missing is verifying if the client is logged in before commands - will be added soon

This commit is contained in:
anschrammh 2019-10-19 15:34:27 +02:00
parent 49b16c1b17
commit 34ffc920a1
4 changed files with 141 additions and 152 deletions

View File

@ -11,6 +11,7 @@ _fileSentBytes(0),
_fileRecvBytes(0), _fileRecvBytes(0),
_waitingForDataConnection(false), _waitingForDataConnection(false),
_fileIsBeeingReceived(false), _fileIsBeeingReceived(false),
_actionTimeout(0),
_ftpClientState(FTPServer<FTPClient>::FTPClientState::INIT), _ftpClientState(FTPServer<FTPClient>::FTPClientState::INIT),
_binaryFlag(FTPServer<FTPClient>::BinaryFlag::OFF), _binaryFlag(FTPServer<FTPClient>::BinaryFlag::OFF),
_dataTransferPending(FTPServer<FTPClient>::FTPClientDataTransfer::NONE) _dataTransferPending(FTPServer<FTPClient>::FTPClientDataTransfer::NONE)
@ -35,7 +36,19 @@ void FTPClient::setDataClient(WiFiClient dataClient)
boolean FTPClient::parseCommandAndParameters() boolean FTPClient::parseCommandAndParameters()
{ {
//We remove the cr lf at the end //We remove the cr lf at the end
char *cr = strchr((char *)_data,'\r'); *cr = '\0'; char *cr = strchr((char *)_data,'\r');
if(cr != NULL)
{
*cr = '\0';
}
else
{
cr = strchr((char *)_data,'\n');
if(cr != NULL)
*cr = '\0';
}
char *cmdDelimiter = strchr((char *)_data,' '); char *cmdDelimiter = strchr((char *)_data,' ');
if(cmdDelimiter == NULL) //It means that we do not have any parameters if(cmdDelimiter == NULL) //It means that we do not have any parameters
@ -56,6 +69,8 @@ boolean FTPClient::parseCommandAndParameters()
//At the end, we flush the buffer: //At the end, we flush the buffer:
freeDataBuffer(_dataSize); freeDataBuffer(_dataSize);
return true;
} }
void FTPClient::setUsername(const char *username) void FTPClient::setUsername(const char *username)
@ -87,3 +102,8 @@ void FTPClient::setCurrentFile(const char *file)
strcpy(_currentFile, file); strcpy(_currentFile, file);
} }
} }
void FTPClient::startTimeout()
{
_actionTimeout = millis();
}

View File

@ -20,6 +20,7 @@ class FTPClient : public TCPClient
void setUsername(const char *username); void setUsername(const char *username);
void setCurrentDirectory(const char *dir); void setCurrentDirectory(const char *dir);
void setCurrentFile(const char *file); void setCurrentFile(const char *file);
void startTimeout();
char _ftpCommand[5]; char _ftpCommand[5];
Dictionary<DictionaryHelper::StringEntity> *_cmdParameters; Dictionary<DictionaryHelper::StringEntity> *_cmdParameters;
@ -31,6 +32,7 @@ class FTPClient : public TCPClient
uint64_t _fileRecvBytes; uint64_t _fileRecvBytes;
boolean _waitingForDataConnection; boolean _waitingForDataConnection;
boolean _fileIsBeeingReceived; boolean _fileIsBeeingReceived;
uint64_t _actionTimeout;
FTPServer<FTPClient>::FTPClientState _ftpClientState; FTPServer<FTPClient>::FTPClientState _ftpClientState;
FTPServer<FTPClient>::BinaryFlag _binaryFlag; FTPServer<FTPClient>::BinaryFlag _binaryFlag;

View File

@ -4,8 +4,9 @@
#include "TCPServer.h" #include "TCPServer.h"
#include "SDCardManager.h" #include "SDCardManager.h"
#include "definition.h" #include "definition.h"
#include "Dictionary.h"
#define DEBUG_FTPS #define DEBUG_FTPS
#define READ_WRITE_BUFFER_SIZE 2500 #define READ_BUFFER_SIZE 2500 //2500 is max to read sd card, more will crash
template <typename T> template <typename T>
class FTPServer : public TCPServer<T> class FTPServer : public TCPServer<T>
@ -262,6 +263,7 @@ class FTPServer : public TCPServer<T>
} }
else //The ftp access is open else //The ftp access is open
{ {
client->_loggedIn = true;
client->_client.println("230 User logged in, proceed."); client->_client.println("230 User logged in, proceed.");
} }
} }
@ -358,43 +360,24 @@ class FTPServer : public TCPServer<T>
{ {
client->setCurrentDirectory("/"); client->setCurrentDirectory("/");
} }
else 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 if(client->_cmdParameters->getAt(0)->getString()[0] == '/')//Then this is a name prefix
{ {
client->setCurrentDirectory(client->_cmdParameters->getAt(0)->getString()); char *fullDirPath = constructFileNameWithPath("",client->_cmdParameters);
client->setCurrentDirectory(fullDirPath);
free(fullDirPath);
} }
else else
{ {
//Directories with spaces are now working: //Directories with spaces are now working:
uint16_t dirNameSize(0), paramCount(client->_cmdParameters->count()); char *directoryFullpath = constructFileNameWithPath(client->_currentDirectory,client->_cmdParameters);
for(int i(0); i < paramCount; i++)
{
dirNameSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1;
}
dirNameSize += strlen(client->_currentDirectory) + 1;
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("Param size : %d\n", dirNameSize); Serial.printf("Final dir : %s\n", directoryFullpath);
#endif #endif
client->setCurrentDirectory(directoryFullpath);
char *temp = (char *)malloc(sizeof(char) * dirNameSize + 1);// /!\ test for malloc fail free(directoryFullpath);
strcpy(temp,client->_currentDirectory);
if(strcmp(temp, "/") != 0)strcat(temp,"/");
for(int i(0); i < paramCount; i++)
{
strcat(temp,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(temp," ");
}
#ifdef DEBUG_FTPS
Serial.printf("Final dir : %s, size : %d --> %d\n",temp, dirNameSize, strlen(temp));
#endif
client->setCurrentDirectory(temp);
free(temp);
} }
} }
@ -409,37 +392,17 @@ class FTPServer : public TCPServer<T>
if (client->_cmdParameters->count() > 0) if (client->_cmdParameters->count() > 0)
{ {
//We save the file path to be sent //We save the file path to be sent
uint16_t filePathSize(0), paramCount(client->_cmdParameters->count()); char *file2store(NULL);
for(int i(0); i < paramCount; i++) if(client->_cmdParameters->getAt(0)->getString()[0] == '/')
{ file2store = constructFileNameWithPath("", client->_cmdParameters);
filePathSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1; else
} file2store = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
filePathSize += strlen(client->_currentDirectory) + 1;
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("filePathSize : %d\n", filePathSize); Serial.printf("Final file path : %s\n",file2store);
#endif #endif
client->setCurrentFile(file2store);
char *temp = (char *)malloc(sizeof(char) * filePathSize + 1);// /!\ test for malloc fail free(file2store);
strcpy(temp,client->_currentDirectory);
if(strcmp(temp, "/") != 0)strcat(temp,"/");
for(int i(0); i < paramCount; i++)
{
strcat(temp,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(temp," ");
}
#ifdef DEBUG_FTPS
Serial.printf("Final file path : %s, size : %d --> %d\n",temp, filePathSize, strlen(temp));
#endif
client->setCurrentFile(temp);
#ifdef DEBUG_FTPS
Serial.printf("File to donwload : %s\n", temp);
#endif
free(temp);
client->_dataTransferPending = RETR_DF; client->_dataTransferPending = RETR_DF;
} }
} }
@ -447,32 +410,12 @@ class FTPServer : public TCPServer<T>
{ {
if (client->_cmdParameters->count() > 0) if (client->_cmdParameters->count() > 0)
{ {
uint16_t dirPathSize(0) /*dir path plus dirname*/, paramCount(client->_cmdParameters->count()); char *dirNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
char *dirNameWithPath(NULL);
for(int i(0); i < paramCount; i++)
{
dirPathSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1;
}
dirPathSize += strlen(client->_currentDirectory) + 1;
dirNameWithPath = (char *)malloc( sizeof(char) * dirPathSize );
if(dirNameWithPath != NULL) if(dirNameWithPath != NULL)
{ {
strcpy(dirNameWithPath,client->_currentDirectory);
if(strcmp(dirNameWithPath, "/") != 0)strcat(dirNameWithPath,"/");
for(int i(0); i < paramCount; i++)
{
strcat(dirNameWithPath,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(dirNameWithPath," ");
}
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("Final dirName : #%s#, size : %d\n",dirNameWithPath, dirPathSize); Serial.printf("Final dirName : #%s#\n",dirNameWithPath);
#endif #endif
if(_sdCardManager->mkdir(dirNameWithPath)) if(_sdCardManager->mkdir(dirNameWithPath))
@ -499,30 +442,10 @@ class FTPServer : public TCPServer<T>
if (client->_cmdParameters->count() > 0) if (client->_cmdParameters->count() > 0)
{ {
//We have the dir name, we need to append the current directory... //We have the dir name, we need to append the current directory...
uint16_t dirPathSize(0) /*dir path plus dirname*/, paramCount(client->_cmdParameters->count()); char *dirNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
char *dirNameWithPath(NULL);
for(int i(0); i < paramCount; i++)
{
dirPathSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1;
}
dirPathSize += strlen(client->_currentDirectory) + 1;
dirNameWithPath = (char *)malloc( sizeof(char) * dirPathSize );
strcpy(dirNameWithPath,client->_currentDirectory);
if(strcmp(dirNameWithPath, "/") != 0)strcat(dirNameWithPath,"/");
for(int i(0); i < paramCount; i++)
{
strcat(dirNameWithPath,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(dirNameWithPath," ");
}
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("pathDirName to delete : #%s#, size : %d\n",dirNameWithPath, dirPathSize); Serial.printf("pathDirName to delete : #%s#\n",dirNameWithPath);
#endif #endif
if(_sdCardManager->rmdir(dirNameWithPath)) if(_sdCardManager->rmdir(dirNameWithPath))
@ -546,36 +469,18 @@ class FTPServer : public TCPServer<T>
if (client->_cmdParameters->count() > 0) if (client->_cmdParameters->count() > 0)
{ {
//We save the file path to be sent //We save the file path to be sent
uint16_t filePathSize(0) /*dir path plus dirname*/, paramCount(client->_cmdParameters->count()); char *fileNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
char *fileNameWithPath(NULL);
for(int i(0); i < paramCount; i++)
{
filePathSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1;
}
filePathSize += strlen(client->_currentDirectory) + 1;
fileNameWithPath = (char *)malloc( sizeof(char) * filePathSize );
strcpy(fileNameWithPath,client->_currentDirectory);
if(strcmp(fileNameWithPath, "/") != 0)strcat(fileNameWithPath,"/");
for(int i(0); i < paramCount; i++)
{
strcat(fileNameWithPath,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(fileNameWithPath," ");
}
client->_client.println("150 File status okay; about to open data connection."); client->_client.println("150 File status okay; about to open data connection.");
client->setCurrentFile(fileNameWithPath); client->setCurrentFile(fileNameWithPath);
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("File to store : #%s#, size : %d\n", client->_currentFile, filePathSize); Serial.printf("File to store : #%s#\n", client->_currentFile);
#endif #endif
free(fileNameWithPath); free(fileNameWithPath);
client->_dataTransferPending = STOR_DF; client->_dataTransferPending = STOR_DF;
client->startTimeout();
} }
} }
else if(strcmp(client->_ftpCommand, "SYST") == 0) else if(strcmp(client->_ftpCommand, "SYST") == 0)
@ -587,33 +492,13 @@ class FTPServer : public TCPServer<T>
if (client->_cmdParameters->count() > 0) if (client->_cmdParameters->count() > 0)
{ {
//We have the file name, we need to append the current directory... //We have the file name, we need to append the current directory...
uint16_t filePathSize(0) /*dir path plus dirname*/, paramCount(client->_cmdParameters->count()); char *file2deleteNameWithPath = constructFileNameWithPath(client->_currentDirectory, client->_cmdParameters);
char *fileNameWithPath(NULL);
for(int i(0); i < paramCount; i++)
{
filePathSize += strlen(client->_cmdParameters->getAt(i)->getString()) + 1;
}
filePathSize += strlen(client->_currentDirectory) + 1;
fileNameWithPath = (char *)malloc( sizeof(char) * filePathSize );
strcpy(fileNameWithPath,client->_currentDirectory);
if(strcmp(fileNameWithPath, "/") != 0)strcat(fileNameWithPath,"/");
for(int i(0); i < paramCount; i++)
{
strcat(fileNameWithPath,client->_cmdParameters->getAt(i)->getString());
if(i != paramCount-1)
strcat(fileNameWithPath," ");
}
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.printf("file to delete : #%s#, size : %d\n",fileNameWithPath,filePathSize); Serial.printf("file to delete : #%s#\n",file2deleteNameWithPath);
#endif #endif
if(_sdCardManager->remove(fileNameWithPath)) if(_sdCardManager->remove(file2deleteNameWithPath))
{ {
client->_client.println("250 Requested file action okay."); client->_client.println("250 Requested file action okay.");
} }
@ -622,13 +507,54 @@ class FTPServer : public TCPServer<T>
client->_client.println("550 Requested action not taken."); client->_client.println("550 Requested action not taken.");
} }
free(fileNameWithPath); free(file2deleteNameWithPath);
} }
else else
{ {
client->_client.println("550 Requested action not taken."); client->_client.println("550 Requested action not taken.");
} }
} }
else if(strcmp(client->_ftpCommand, "RNFR") == 0)
{
if (client->_cmdParameters->count() > 0)
{
//We have the file name, we need to append the current directory...
char *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
{
client->_client.println("550 Requested action not taken.");
}
}
else if(strcmp(client->_ftpCommand, "RNTO") == 0)
{
if (client->_cmdParameters->count() > 0)
{
//Here we rename the file
char *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(_sdCardManager->rename(client->_currentFile,file2RenameNameWithPath))
{
client->_client.println("250 Requested file action okay.");
}else
client->_client.println("550 Requested action not taken.");
free(file2RenameNameWithPath);
}
}
else else
client->_client.println("502 Command not implemented."); client->_client.println("502 Command not implemented.");
} }
@ -645,17 +571,17 @@ class FTPServer : public TCPServer<T>
if(fileBeeingReceived) if(fileBeeingReceived)
{ {
char recvBuffer[1024]; uint8_t recvBuffer[2048];
/*fileBeeingReceived.seek(client->_fileRecvBytes);*/ /*fileBeeingReceived.seek(client->_fileRecvBytes);*/
uint16_t size = client->_dataClient.read((uint8_t *)recvBuffer, 1024); uint16_t size = client->_dataClient.read(recvBuffer, 2048);
fileBeeingReceived.write(recvBuffer, size); fileBeeingReceived.write(recvBuffer, size);
client->_fileRecvBytes += size; client->_fileRecvBytes += size;
fileBeeingReceived.close(); fileBeeingReceived.close();
#ifdef DEBUG_FTPS #ifdef DEBUG_FTPS
Serial.println("File beeing written"); Serial.printf("Writting : %d bytes to file\n", size);
#endif #endif
} }
else else
@ -720,7 +646,7 @@ class FTPServer : public TCPServer<T>
{ {
if (client->_currentFile != NULL) if (client->_currentFile != NULL)
{ {
uint8_t sendBuffer[READ_WRITE_BUFFER_SIZE]; uint8_t sendBuffer[READ_BUFFER_SIZE];
File fileToSend = _sdCardManager->open(client->_currentFile); File fileToSend = _sdCardManager->open(client->_currentFile);
if (fileToSend) if (fileToSend)
@ -731,7 +657,7 @@ class FTPServer : public TCPServer<T>
if(fileToSend.available()) if(fileToSend.available())
{ {
readBytes = fileToSend.read(sendBuffer, READ_WRITE_BUFFER_SIZE); readBytes = fileToSend.read(sendBuffer, READ_BUFFER_SIZE);
client->_dataClient.write(sendBuffer, readBytes); client->_dataClient.write(sendBuffer, readBytes);
client->_fileSentBytes += readBytes; client->_fileSentBytes += readBytes;
@ -765,7 +691,7 @@ class FTPServer : public TCPServer<T>
return false; return false;
} }
char *_83FileNameFormat(char *filename) static char *_83FileNameFormat(char *filename)
{ {
char *buffer = (char *)malloc((sizeof(char) * strlen(filename)) + 1); char *buffer = (char *)malloc((sizeof(char) * strlen(filename)) + 1);
strcpy(buffer, filename); strcpy(buffer, filename);
@ -797,6 +723,46 @@ class FTPServer : public TCPServer<T>
return filename; return filename;
} }
//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;
}
char *_login; char *_login;
char *_password; char *_password;
unsigned int _dataPort; unsigned int _dataPort;

View File

@ -19,5 +19,6 @@
#define SOFT_VERSION "1.3.1" //Fixed sdCardUnmount api call #define SOFT_VERSION "1.3.1" //Fixed sdCardUnmount api call
#define SOFT_VERSION "1.3.2" //Modified TCPServer and WEBServer core logic #define SOFT_VERSION "1.3.2" //Modified TCPServer and WEBServer core logic
#define SOFT_VERSION "1.4.0" //Added the new FTPServer #define SOFT_VERSION "1.4.0" //Added the new FTPServer
#define SOFT_VERSION "1.4.1" //Updated FTP server to use the new SD library for the ESP8266
#endif //VERSIONS_H #endif //VERSIONS_H