Implementing FTP server

This commit is contained in:
anschrammh 2019-10-12 23:20:25 +02:00
parent e77e879027
commit 31eb8eb589
9 changed files with 370 additions and 10 deletions

View File

@ -19,8 +19,8 @@ namespace DictionaryHelper
{ {
if(string == NULL) if(string == NULL)
{ {
_string = (char *) malloc((sizeof(char)) * 2); //+1 for the string terminating character _string = (char *) malloc((sizeof(char)));
strcpy(_string, ""); _string[0] = '\0';
} }
else else
{ {

View File

@ -1 +1,58 @@
#include "FTPClient.h" #include "FTPClient.h"
FTPClient::FTPClient(WiFiClient client, uint8_t id, uint16_t clientCommandDataBufferSize) : TCPClient(client, id, clientCommandDataBufferSize),
_ftpCommand({'\0'}),
_cmdParameters(NULL),
_loggedIn(false),
_username(NULL),
_ftpClientState(FTPServer<FTPClient>::FTPClientState::WAITING_FOR_COMMANDS),
_binaryFlag(FTPServer<FTPClient>::BinaryFlag::OFF)
{
}
FTPClient::~FTPClient()
{
delete _cmdParameters;
free(_username);
}
void FTPClient::setDataClient(WiFiClient dataClient)
{
_dataClient = _dataClient;
}
boolean FTPClient::parseCommandAndParameters()
{
//We remove the cr lf at the end
char *cr = strchr((char *)_data,'\r'); *cr = '\0';
char *cmdDelimiter = strchr((char *)_data,' ');
if(cmdDelimiter == NULL) //It means that we do not have any parameters
{
cmdDelimiter = (char *)_data + strlen((char *)_data) - 1;
strcpy(_ftpCommand, (char *)_data);
}
else //we do
{
strncpy(_ftpCommand, (char *)_data, cmdDelimiter - (char *)_data);
_ftpCommand[cmdDelimiter - (char *)_data] = '\0'; // /!\ strncpy does not append the terminating string character
}
//We get the parameters :
DictionaryHelper::StringEntity params(cmdDelimiter+1); //+1 to skip the first space
delete _cmdParameters;
_cmdParameters = params.split(' ');
//At the end, we flush the buffer:
freeDataBuffer(_dataSize);
}
void FTPClient::setUsername(const char *username)
{
if(username != NULL)
{
_username = (char *) malloc((sizeof(char) * strlen(username)) + 1);
strcpy(_username, username);
}
}

View File

@ -2,12 +2,31 @@
#define FTPCLIENT_H #define FTPCLIENT_H
#include "TCPClient.h" #include "TCPClient.h"
#include "FTPServer.h"
#include "Dictionary.h"
class FTPClient class FTPClient : public TCPClient
{ {
template<typename T>
friend class FTPServer;
public: public:
FTPClient(WiFiClient client, uint8_t id, uint16_t clientCommandDataBufferSize = 255);
virtual ~FTPClient();
protected: protected:
private: private:
FTPClient(const FTPClient &Object) : TCPClient(Object){}
void setDataClient(WiFiClient dataClient); //Also known as the data socket
boolean parseCommandAndParameters(void);
void setUsername(const char *username);
char _ftpCommand[5];
Dictionary<DictionaryHelper::StringEntity> *_cmdParameters;
boolean _loggedIn;
char *_username;
FTPServer<FTPClient>::FTPClientState _ftpClientState;
FTPServer<FTPClient>::BinaryFlag _binaryFlag;
WiFiClient _dataClient; //data socket
}; };
#endif //FTPCLIENT_H #endif //FTPCLIENT_H

View File

@ -2,20 +2,31 @@
#define FTPSERVER_H #define FTPSERVER_H
#include "TCPServer.h" #include "TCPServer.h"
#define DEBUG_FTPS
template <typename T> template <typename T>
class FTPServer : public TCPServer<T> class FTPServer : public TCPServer<T>
{ {
public: public:
FTPServer(unsigned int port = 22, const char login = "", const char password = "", uint8_t maxClient = MAX_CLIENT, uint16_t clientCommandDataBufferSize = 255) : TCPServer<T>(port, maxClient, clientCommandDataBufferSize), _login(NULL), _password(NULL) enum FTPClientState {WAITING_FOR_COMMANDS};
{ enum BinaryFlag {OFF = 0, ON};
_login = (char *)malloc((sizeof(char) * strlen(login)) + 1);
if(_login != NULL)
strcpy(_login, login);
_password = (char *)malloc((sizeof(char) * strlen(password)) + 1); FTPServer(unsigned int port = 21, const char *login = NULL, const char *password = NULL, uint8_t maxClient = MAX_CLIENT, uint16_t clientCommandDataBufferSize = 255) : TCPServer<T>(port, maxClient, clientCommandDataBufferSize),
if(_password != NULL) _login(NULL),
_password(NULL),
_dataServer(1024)
{
if(login != NULL)
{
_login = (char *)malloc((sizeof(char) * strlen(login)) + 1);
strcpy(_login, login);
}
if(password != NULL)
{
_password = (char *)malloc((sizeof(char) * strlen(password)) + 1);
strcpy(_password, password); strcpy(_password, password);
}
} }
virtual ~FTPServer() virtual ~FTPServer()
@ -24,9 +35,136 @@ class FTPServer : public TCPServer<T>
} }
protected: 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;
}
virtual void processClientData(T *client)
{
#ifdef DEBUG_FTPS
if(client->_newDataAvailable)
{
Serial.print("Client --> ");Serial.print(client->_id);Serial.print(" : ");Serial.print((char *)client->_data);Serial.println("#");
}
#endif
switch(client->_ftpClientState)
{
case WAITING_FOR_COMMANDS:
processCommands(client);
break;
}
}
private: 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
client->_client.println("530 Username required.");
}
else //The ftp access is open
{
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
{
client->_client.println("530 Wrong username or password !.");
}
}
else
{
client->_client.println("530 Password required.");
}
}
else if(strcmp(client->_ftpCommand,"PWD") == 0)
{
client->_client.println("257 \"/FTP\"");
}
else if(strcmp(client->_ftpCommand,"TYPE") == 0)
{
if(client->_cmdParameters->count() > 0)
{
switch(client->_cmdParameters->getAt(0)->getString()[0])
{
case 'I':
client->_binaryFlag = ON;
client->_client.println("200 Command okay.");
break;
case 'L':
client->_binaryFlag = ON;
client->_client.println("200 Command okay.");
break;
case 'A':
client->_binaryFlag = OFF;
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)
{
_dataServer.begin();
client->_client.printf("227 Entering Passive Mode (%d,%d,%d,%d,%d,%d).\r\n", 192,168,0,29,1024/256, 1024%256);
}
else
client->_client.println("502 Command not implemented.");
}
char *_login; char *_login;
char *_password; char *_password;
WiFiServer _dataServer; //In passive mode, the FTP server opens two different ports (one for the commands and the other for the data stream)
}; };
#endif //FTPSERVER_H #endif //FTPSERVER_H

View File

@ -0,0 +1,33 @@
#include "Dictionary.h"
#include "definition.h"
Dictionary<DictionaryHelper::StringEntity> *DictionaryHelper::StringEntity::split(char character)
{
Dictionary<DictionaryHelper::StringEntity> *ref = NULL;
unsigned int i(0), counter(0);
char *parseBuffer(NULL);
if(_string == NULL)return NULL;
ref = new Dictionary<StringEntity>();
while(_string[i] != '\0')
{
if(_string[i] == character)
{
ref->add(counter++, parseBuffer);
free(parseBuffer);parseBuffer = NULL;
}
else
{
parseBuffer = addChar(parseBuffer, _string[i]);
}
i++;
}
ref->add(counter++,parseBuffer);
free(parseBuffer);parseBuffer = NULL;
return ref;
}

View File

@ -36,7 +36,7 @@ class WEBServer : public TCPServer<T>
uint16_t maxBodyBuffer; uint16_t maxBodyBuffer;
}; };
WEBServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 512) : TCPServer<T>(port, maxClient, clientDataBufferSize) {} WEBServer(unsigned int port = 80, uint8_t maxClient = MAX_CLIENT, uint16_t clientDataBufferSize = 255) : TCPServer<T>(port, maxClient, clientDataBufferSize) {}
boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED) boolean addApiRoutine(const char *uri, boolean (*apiRoutine)(HttpRequestData&, WiFiClient*, void*), void *pData, HttpRequestMethod HRM = UNDEFINED)
{ {
@ -53,7 +53,7 @@ class WEBServer : public TCPServer<T>
private: private:
virtual T* createNewClient(WiFiClient wc) virtual T* createNewClient(WiFiClient wc)
{ {
return new T(wc, TCPServer<T>::freeClientId()); return new T(wc, TCPServer<T>::freeClientId(), TCPServer<T>::_clientDataBufferSize);
} }
virtual void greetClient(T *client) virtual void greetClient(T *client)

View File

@ -0,0 +1,51 @@
#include "definition.h"
char *addChar(char *pointer, const char character)
{
char *tempAddr = NULL;
if(pointer == NULL)
{
tempAddr = (char *) realloc(pointer, 2*sizeof(char));
if(tempAddr == NULL)
return NULL;
else
{
pointer = tempAddr;
pointer[0] = character;
pointer[1] = '\0';
}
}
else
{
tempAddr = (char *) realloc(pointer, (strlen(pointer)+2)*sizeof(char));
if(tempAddr == NULL)
{
free(pointer);
return NULL;
}
else
{
pointer = tempAddr;
pointer[strlen(pointer)+1] = '\0';
pointer[strlen(pointer)] = character;
}
}
return pointer;
}
char *dateTimeFormater(char *pointer, const uint8_t value, const char character)
{
if(pointer == NULL)
return pointer;
if(value < 10)
{
sprintf(pointer,"%d", value);
*(pointer+1) = *(pointer);*(pointer) = character;*(pointer+2) = '\0';
}
else
sprintf(pointer,"%d", value);
return pointer;
}

View File

@ -0,0 +1,60 @@
#ifndef DEFINITION_H
#define DEFINITION_H
#include <Adafruit_SSD1306.h>
#include <Arduino.h>
typedef enum { GPIO_0 = 0,
GPIO_1_TX = 1,
GPIO_2 = 2,
GPIO_3_RX = 3,
GPIO_4_SDA = 4,
GPIO_5_SCL = 5,
GPIO_10 = 10,
GPIO_12_MISO = 12,
GPIO_13_MOSI = 13,
GPIO_14_CLK = 14,
GPIO_15 = 15,
GPIO_16 = 16,
ADC = A0,
DEFAULT_PIN = -1 } Pin;
#define AP_AND_STA_ENABLED_ERR B00000001
#define AP_SETUP_ERR B00000010
#define STA_SETUP_ERR B00000100
#define NO_ERROR 0
#define NO_CURRENT_VIEW NULL
#define LAST_VIEW -1
#define RESERVED_VIEW_UID -2
#define BATT_FULL 870 //8.4v
#define BATT_EMPTY 775 //7.4v
#define BATT_DIFF 95
#define USB_THRESHOLD 600
//SD card file structure :
#define AP_CFG_FILE "/CONFIG/AP.CFG"
#define STA_CFG_FILE "/CONFIG/STA.CFG"
#define WWW_DIR "/WWW"
#define LOG_DIR "/LOGS"
#define FTP_DIR "/FTP"
typedef enum { OR_0 = 2, OR_90 = 3, OR_180 = 0, OR_270 = 1 } Orientation;
typedef enum { BIT = 0, BYTE, KBIT, KBYTE, MBIT, MBYTE, GBIT, GBYTE } SizeUnit;
//Data structure for the view handling
typedef struct viewLink{
boolean (*viewLogicFunction)(Adafruit_SSD1306&, void*);
void *pData;
const int UID;
struct viewLink *next;
} ViewLink, *ViewLinkedList;
char *addChar(char *pointer, const char character);
char *dateTimeFormater(char *pointer, const uint8_t value, const char character);
#endif //DEFINITION_H

View File

@ -15,6 +15,7 @@ uint16_t lastClientCount(0);
//TCPServer<TCPClient> server(80, MAX_CLIENT, 5); //TCPServer<TCPClient> server(80, MAX_CLIENT, 5);
//WEBServer<WEBClient> webServer(8080); //WEBServer<WEBClient> webServer(8080);
FTPServer<FTPClient> ftpServer;
WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; WiFiEventHandler gotIpEventHandler, disconnectedEventHandler;
@ -53,6 +54,7 @@ void loop() {
//server.runServer(); //server.runServer();
//webServer.runServer(); //webServer.runServer();
debugInfo(); debugInfo();
ftpServer.runServer();
} }