diff --git a/src/app/FTPClient.cpp b/src/app/FTPClient.cpp index a7df6e4..27ef6fc 100644 --- a/src/app/FTPClient.cpp +++ b/src/app/FTPClient.cpp @@ -11,6 +11,7 @@ _fileSentBytes(0), _fileRecvBytes(0), _waitingForDataConnection(false), _fileIsBeeingReceived(false), +_actionTimeout(0), _ftpClientState(FTPServer::FTPClientState::INIT), _binaryFlag(FTPServer::BinaryFlag::OFF), _dataTransferPending(FTPServer::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(); +} diff --git a/src/app/FTPClient.h b/src/app/FTPClient.h index 9385012..697b5d2 100644 --- a/src/app/FTPClient.h +++ b/src/app/FTPClient.h @@ -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 *_cmdParameters; @@ -31,6 +32,7 @@ class FTPClient : public TCPClient uint64_t _fileRecvBytes; boolean _waitingForDataConnection; boolean _fileIsBeeingReceived; + uint64_t _actionTimeout; FTPServer::FTPClientState _ftpClientState; FTPServer::BinaryFlag _binaryFlag; diff --git a/src/app/FTPServer.h b/src/app/FTPServer.h index 51f7ddb..aba40b1 100644 --- a/src/app/FTPServer.h +++ b/src/app/FTPServer.h @@ -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 class FTPServer : public TCPServer @@ -262,6 +263,7 @@ class FTPServer : public TCPServer } 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 { 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 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 { 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 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 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 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 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 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 { 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 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 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 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 *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; diff --git a/src/app/versions.h b/src/app/versions.h index f339d6c..7043209 100644 --- a/src/app/versions.h +++ b/src/app/versions.h @@ -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