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),
_waitingForDataConnection(false),
_fileIsBeeingReceived(false),
_actionTimeout(0),
_ftpClientState(FTPServer<FTPClient>::FTPClientState::INIT),
_binaryFlag(FTPServer<FTPClient>::BinaryFlag::OFF),
_dataTransferPending(FTPServer<FTPClient>::FTPClientDataTransfer::NONE)
@ -35,7 +36,19 @@ void FTPClient::setDataClient(WiFiClient dataClient)
boolean FTPClient::parseCommandAndParameters()
{
//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,' ');
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:
freeDataBuffer(_dataSize);
return true;
}
void FTPClient::setUsername(const char *username)
@ -87,3 +102,8 @@ void FTPClient::setCurrentFile(const char *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 setCurrentDirectory(const char *dir);
void setCurrentFile(const char *file);
void startTimeout();
char _ftpCommand[5];
Dictionary<DictionaryHelper::StringEntity> *_cmdParameters;
@ -31,6 +32,7 @@ class FTPClient : public TCPClient
uint64_t _fileRecvBytes;
boolean _waitingForDataConnection;
boolean _fileIsBeeingReceived;
uint64_t _actionTimeout;
FTPServer<FTPClient>::FTPClientState _ftpClientState;
FTPServer<FTPClient>::BinaryFlag _binaryFlag;

View File

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

View File

@ -19,5 +19,6 @@
#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.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