diff --git a/lib/Makefile.am b/lib/Makefile.am index d9e8c4e5..a5dcbcf4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp stream.h stream.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto libmist_1_0_la_LDFLAGS = -version-info 1:0:0 @@ -9,4 +9,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h stream.h +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp new file mode 100644 index 00000000..06f5cbb7 --- /dev/null +++ b/lib/filesystem.cpp @@ -0,0 +1,273 @@ +#include "filesystem.h" + + +Filesystem::Directory::Directory( std::string PathName, std::string BasePath ) { + MyBase = BasePath; + if( PathName[0] == '/' ) { PathName.erase(0,1); } + if( BasePath[BasePath.size()-1] != '/' ) { BasePath += "/"; } + MyPath = PathName; + FillEntries( ); +} + +Filesystem::Directory::~Directory( ) { } + +void Filesystem::Directory::FillEntries( ) { + fprintf( stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str() ); + ValidDir = true; + struct stat StatBuf; + Entries.clear(); + DIR * Dirp = opendir( (MyBase + MyPath).c_str() ); + if( !Dirp ) { + ValidDir = false; + } else { + dirent * entry; + while( entry = readdir( Dirp ) ) { + if( stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1 ) { + fprintf( stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno) ); + continue; + } + ///Convert stat to string + Entries[ std::string( entry->d_name ) ] = StatBuf; + } + } + fprintf( stderr, "Valid dir: %d\n", ValidDir ); + fprintf( stderr, "#Entries: %d\n", Entries.size() ); +} + +void Filesystem::Directory::Print( ) { + if( !ValidDir ) { + printf( "%s is not a valid directory\n", (MyBase + MyPath).c_str() ); + return; + } + printf( "%s:\n", (MyBase + MyPath).c_str() ); + for( std::map::iterator it = Entries.begin(); it != Entries.end(); it++ ) { + printf( "\t%s\n", (*it).first.c_str() ); + } + printf( "\n" ); +} + +bool Filesystem::Directory::IsDir( ) { + return ValidDir; +} + +std::string Filesystem::Directory::PWD( ) { + return "/" + MyPath; +} + +std::string Filesystem::Directory::LIST( std::vector ActiveStreams ) { + FillEntries( ); + int MyPermissions; + std::stringstream Converter; + passwd* pwd;//For Username + group* grp;//For Groupname + tm* tm;//For time localisation + char datestring[256];//For time localisation + + std::string MyLoc = MyBase + MyPath; + if( MyLoc[MyLoc.size()-1] != '/' ) { MyLoc += "/"; } + + for( std::map::iterator it = Entries.begin(); it != Entries.end(); it++ ) { + + bool Active = ( std::find( ActiveStreams.begin(), ActiveStreams.end(), (*it).first ) != ActiveStreams.end() ); + fprintf( stderr, "%s active?: %d\n", (*it).first.c_str(), Active ); + fprintf( stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath] ); + fprintf( stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE ); + if( ( Active && ( MyVisible[MyPath] & S_ACTIVE ) ) || ( (!Active) && ( MyVisible[MyPath] & S_INACTIVE ) ) || ( ((*it).second.st_mode / 010000 ) == 4 ) ) { + if( ((*it).second.st_mode / 010000) == 4 ) { Converter << 'd'; } else { Converter << '-'; } + MyPermissions = ( ( (*it).second.st_mode % 010000 ) / 0100 ); + if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } + if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } + if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } + MyPermissions = ( ( (*it).second.st_mode % 0100 ) / 010 ); + if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } + if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } + if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } + MyPermissions = ( (*it).second.st_mode % 010 ); + if( MyPermissions & 4 ) { Converter << 'r'; } else { Converter << '-'; } + if( MyPermissions & 2 ) { Converter << 'w'; } else { Converter << '-'; } + if( MyPermissions & 1 ) { Converter << 'x'; } else { Converter << '-'; } + Converter << ' '; + Converter << (*it).second.st_nlink; + Converter << ' '; + if( (pwd = getpwuid((*it).second.st_uid)) ) { + Converter << pwd->pw_name; + } else { + Converter << (*it).second.st_uid; + } + Converter << ' '; + if( (grp = getgrgid((*it).second.st_gid) ) ) { + Converter << grp->gr_name; + } else { + Converter << (*it).second.st_gid; + } + Converter << ' '; + Converter << (*it).second.st_size; + Converter << ' '; + tm = localtime(&((*it).second.st_mtime)); + strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm); + Converter << datestring; + Converter << ' '; + Converter << (*it).first; + Converter << '\n'; + } + } + return Converter.str(); +} + +bool Filesystem::Directory::CWD( std::string Path ) { + if( Path[0] == '/' ) { + Path.erase(0,1); + MyPath = Path; + } else { + if( MyPath != "" ) { + MyPath += "/"; + } + MyPath += Path; + } + FillEntries(); + printf( "New Path: %s\n", MyPath.c_str() ); + if( MyPermissions.find( MyPath ) != MyPermissions.end() ) { + printf( "\tPermissions: %d\n", MyPermissions[MyPath] ); + } + return SimplifyPath( ); +} + +bool Filesystem::Directory::CDUP( ) { + return CWD( ".." ); +} + +std::string Filesystem::Directory::RETR( std::string Path ) { + std::string Result; + std::string FileName; + if( Path[0] == '/' ) { + Path.erase(0,1); + FileName = MyBase + Path; + } else { + FileName = MyBase + MyPath + "/" + Path; + } + std::ifstream File; + File.open( FileName.c_str() ); + while( File.good() ) { Result += File.get(); } + File.close(); + return Result; +} + +void Filesystem::Directory::STOR( std::string Path, std::string Data ) { + if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_STOR ) ) { + std::string FileName; + if( Path[0] == '/' ) { + Path.erase(0,1); + FileName = MyBase + Path; + } else { + FileName = MyBase + MyPath + "/" + Path; + } + std::ofstream File; + File.open( FileName.c_str() ); + File << Data; + File.close(); + } +} + +bool Filesystem::Directory::SimplifyPath( ) { + MyPath += "/"; + fprintf( stderr, "MyPath: %s\n", MyPath.c_str() ); + std::vector TempPath; + std::string TempString; + for( std::string::iterator it = MyPath.begin(); it != MyPath.end(); it ++ ) { + if( (*it) == '/' ) { + if( TempString == ".." ) { + if( !TempPath.size() ) { + return false; + } + TempPath.erase( (TempPath.end()-1) ); + } else if ( TempString != "." && TempString != "" ) { + TempPath.push_back( TempString ); + } + TempString = ""; + } else { + TempString += (*it); + } + } + MyPath = ""; + for( std::vector::iterator it = TempPath.begin(); it != TempPath.end(); it++ ) { + MyPath += (*it); + if( it != ( TempPath.end() - 1 ) ) { MyPath += "/"; } + } + if( MyVisible.find( MyPath ) == MyVisible.end() ) { + MyVisible[MyPath] = S_ALL; + } + return true; +} + +bool Filesystem::Directory::DELE( std::string Path ) { + if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_DELE ) ) { + std::string FileName; + if( Path[0] == '/' ) { + Path.erase(0,1); + FileName = MyBase + Path; + } else { + FileName = MyBase + MyPath + "/" + Path; + } + if( std::remove( FileName.c_str() ) ) { + fprintf( stderr, "Removing file %s unsuccesfull\n", FileName.c_str() ); + return false; + } + return true; + } + return false; +} + +bool Filesystem::Directory::MKD( std::string Path ) { + std::string FileName; + if( Path[0] == '/' ) { + Path.erase(0,1); + FileName = MyBase + Path; + } else { + FileName = MyBase + MyPath + "/" + Path; + } + if( mkdir( FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) ) { + fprintf( stderr, "Creating directory %s unsuccesfull\n", FileName.c_str() ); + return false; + } + MyVisible[FileName] = S_ALL; + return true; +} + +bool Filesystem::Directory::Rename( std::string From, std::string To ) { + if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & P_RNFT ) ) { + std::string FileFrom; + if( From[0] == '/' ) { + From.erase(0,1); + FileFrom = MyBase + From; + } else { + FileFrom = MyBase + MyPath + "/" + From; + } + std::string FileTo; + if( To[0] == '/' ) { + FileTo = MyBase + To; + } else { + FileTo = MyBase + MyPath + "/" + To; + } + if( std::rename( FileFrom.c_str(), FileTo.c_str() ) ) { + fprintf( stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str() ); + return false; + } + return true; + } + return false; +} + +void Filesystem::Directory::SetPermissions( std::string Path, char Permissions ) { + MyPermissions[Path] = Permissions; +} + +bool Filesystem::Directory::HasPermission( char Permission ) { + if( MyPermissions.find( MyPath ) == MyPermissions.end() || ( MyPermissions[MyPath] & Permission ) ) { + return true; + } + return false; +} + +void Filesystem::Directory::SetVisibility( std::string Pathname, char Visible ) { + MyVisible[Pathname] = Visible; +} diff --git a/lib/filesystem.h b/lib/filesystem.h new file mode 100644 index 00000000..f20e91a0 --- /dev/null +++ b/lib/filesystem.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Filesystem { + enum DIR_Permissions { + P_LIST = 0x01,//List + P_RETR = 0x02,//Retrieve + P_STOR = 0x04,//Store + P_RNFT = 0x08,//Rename From/To + P_DELE = 0x10,//Delete + P_MKD = 0x20,//Make directory + P_RMD = 0x40,//Remove directory + }; + + enum DIR_Show { + S_NONE = 0x00, + S_ACTIVE = 0x01, + S_INACTIVE = 0x02, + S_ALL = 0x03, + }; + + class Directory { + public: + Directory( std::string PathName = "", std::string BasePath = "."); + ~Directory( ); + void Print( ); + bool IsDir( ); + std::string PWD( ); + std::string LIST( std::vector ActiveStreams = std::vector() ); + bool CWD( std::string Path ); + bool CDUP( ); + bool DELE( std::string Path ); + bool MKD( std::string Path ); + std::string RETR( std::string Path ); + void STOR( std::string Path, std::string Data ); + bool Rename( std::string From, std::string To ); + void SetPermissions( std::string PathName, char Permissions ); + bool HasPermission( char Permission ); + void SetVisibility( std::string Pathname, char Visible ); + private: + bool ValidDir; + bool SimplifyPath( ); + void FillEntries( ); + std::string MyBase; + std::string MyPath; + std::map< std::string, struct stat > Entries; + std::map< std::string, char > MyPermissions; + std::map< std::string, char > MyVisible; + };//Directory Class +};//Filesystem namespace diff --git a/lib/ftp.cpp b/lib/ftp.cpp new file mode 100644 index 00000000..c3617848 --- /dev/null +++ b/lib/ftp.cpp @@ -0,0 +1,406 @@ +#include "ftp.h" + +FTP::User::User( Socket::Connection NewConnection, std::map Credentials ) { + Conn = NewConnection; + USER = ""; + PASS = ""; + MODE = MODE_STREAM; + STRU = STRU_FILE; + TYPE = TYPE_ASCII_NONPRINT; + PORT = 20; + RNFR = ""; + AllCredentials = Credentials; + + MyDir = Filesystem::Directory( "", FTPBasePath ); + MyDir.SetPermissions( "", Filesystem::P_LIST ); + MyDir.SetPermissions( "Unconverted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_STOR | Filesystem::P_RETR ); + MyDir.SetPermissions( "Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR ); + MyDir.SetPermissions( "OnDemand", Filesystem::P_LIST | Filesystem::P_RETR ); + MyDir.SetPermissions( "Live", Filesystem::P_LIST ); + + MyDir.SetVisibility( "Converted", Filesystem::S_INACTIVE ); + MyDir.SetVisibility( "OnDemand", Filesystem::S_ACTIVE ); + + JSON::Value MyConfig = JSON::fromFile( "/tmp/mist/streamlist" ); + fprintf( stderr, "Streamamount: %d\n", MyConfig["streams"].size() ); + for( JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++ ) { + std::string ThisStream = (*it).second["channel"]["URL"].toString(); + ThisStream.erase( ThisStream.begin() ); + ThisStream.erase( ThisStream.end() - 1 ); + while( ThisStream.find( '/' ) != std::string::npos ) { + ThisStream.erase(0,ThisStream.find('/')+1); + } + ActiveStreams.push_back( ThisStream ); + fprintf( stderr, "\t%s\n", ThisStream.c_str() ); + } +} + +FTP::User::~User( ) { } + +int FTP::User::ParseCommand( std::string Command ) { + Commands ThisCmd = CMD_NOCMD; + if( Command.substr(0,4) == "NOOP" ) { ThisCmd = CMD_NOOP; Command.erase(0,5); } + if( Command.substr(0,4) == "USER" ) { ThisCmd = CMD_USER; Command.erase(0,5); } + if( Command.substr(0,4) == "PASS" ) { ThisCmd = CMD_PASS; Command.erase(0,5); } + if( Command.substr(0,4) == "QUIT" ) { ThisCmd = CMD_QUIT; Command.erase(0,5); } + if( Command.substr(0,4) == "PORT" ) { ThisCmd = CMD_PORT; Command.erase(0,5); } + if( Command.substr(0,4) == "RETR" ) { ThisCmd = CMD_RETR; Command.erase(0,5); } + if( Command.substr(0,4) == "STOR" ) { ThisCmd = CMD_STOR; Command.erase(0,5); } + if( Command.substr(0,4) == "TYPE" ) { ThisCmd = CMD_TYPE; Command.erase(0,5); } + if( Command.substr(0,4) == "MODE" ) { ThisCmd = CMD_MODE; Command.erase(0,5); } + if( Command.substr(0,4) == "STRU" ) { ThisCmd = CMD_STRU; Command.erase(0,5); } + if( Command.substr(0,4) == "EPSV" ) { ThisCmd = CMD_EPSV; Command.erase(0,5); } + if( Command.substr(0,4) == "PASV" ) { ThisCmd = CMD_PASV; Command.erase(0,5); } + if( Command.substr(0,4) == "LIST" ) { ThisCmd = CMD_LIST; Command.erase(0,5); } + if( Command.substr(0,4) == "CDUP" ) { ThisCmd = CMD_CDUP; Command.erase(0,5); } + if( Command.substr(0,4) == "DELE" ) { ThisCmd = CMD_DELE; Command.erase(0,5); } + if( Command.substr(0,4) == "RNFR" ) { ThisCmd = CMD_RNFR; Command.erase(0,5); } + if( Command.substr(0,4) == "RNTO" ) { ThisCmd = CMD_RNTO; Command.erase(0,5); } + if( Command.substr(0,3) == "PWD" ) { ThisCmd = CMD_PWD; Command.erase(0,4); } + if( Command.substr(0,3) == "CWD" ) { ThisCmd = CMD_CWD; Command.erase(0,4); } + if( Command.substr(0,3) == "RMD" ) { ThisCmd = CMD_RMD; Command.erase(0,4); } + if( Command.substr(0,3) == "MKD" ) { ThisCmd = CMD_MKD; Command.erase(0,4); } + if( ThisCmd != CMD_RNTO ) { RNFR = ""; } + switch( ThisCmd ) { + case CMD_NOOP: { + return 200;//Command okay. + break; + } + case CMD_USER: { + USER = ""; + PASS = ""; + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + USER = Command; + return 331;//User name okay, need password. + break; + } + case CMD_PASS: { + if( USER == "" ) { return 503; }//Bad sequence of commands + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + PASS = Command; + if( !LoggedIn( ) ) { + USER = ""; + PASS =""; + return 530;//Not logged in. + } + return 230; + break; + } + case CMD_LIST: { + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.Send( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.Send( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Sending LIST information\n" ); + Connected.Send( MyDir.LIST( ActiveStreams ) ); + Connected.close( ); + return 226; + break; + } + case CMD_QUIT: { + return 221;//Service closing control connection. Logged out if appropriate. + break; + } + case CMD_PORT: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + PORT = atoi( Command.c_str() ); + return 200;//Command okay. + break; + } + case CMD_EPSV: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + MyPassivePort = (rand() % 9999); + std::cout << ":" << MyPassivePort << "\n"; + Passive = Socket::Server(MyPassivePort,"0.0.0.0",true); + return 229; + break; + } + case CMD_PASV: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + MyPassivePort = (rand() % 9999) + 49152; + std::cout << ":" << MyPassivePort << "\n"; + Passive = Socket::Server(MyPassivePort,"0.0.0.0",true); + return 227; + break; + } + case CMD_RETR: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( !MyDir.HasPermission( Filesystem::P_RETR ) ) { return 550; }//Access denied. + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.Send( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.Send( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Sending RETR information\n" ); + Connected.Send( MyDir.RETR( Command ) ); + Connected.close(); + return 226; + break; + } + case CMD_STOR: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( !MyDir.HasPermission( Filesystem::P_STOR ) ) { return 550; }//Access denied. + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.Send( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.Send( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Reading STOR information\n" ); + std::string Buffer; + while( Connected.spool() ) { } + Buffer = Connected.Received(); + MyDir.STOR( Command, Buffer ); + return 250; + break; + } + case CMD_TYPE: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( Command.size() != 1 && Command.size() != 3 ) { return 501; }//Syntax error in parameters or arguments. + switch( Command[0] ) { + case 'A': { + if( Command.size() > 1 ) { + if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. + if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. + } + TYPE = TYPE_ASCII_NONPRINT; + break; + } + case 'I': { + if( Command.size() > 1 ) { + if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. + if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. + } + TYPE = TYPE_IMAGE_NONPRINT; + break; + } + default: { + return 504;//Command not implemented for that parameter. + break; + } + } + return 200;//Command okay. + break; + } + case CMD_MODE: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. + if( Command[0] != 'S' ) { return 504; }//Command not implemented for that parameter. + MODE = MODE_STREAM; + return 200;//Command okay. + break; + } + case CMD_STRU: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( Command.size() != 1 ) { return 501; }//Syntax error in parameters or arguments. + switch( Command[0] ) { + case 'F': { + STRU = STRU_FILE; + break; + } + case 'R': { + STRU = STRU_RECORD; + break; + } + default: { + return 504;//Command not implemented for that parameter. + break; + } + } + return 200;//Command okay. + break; + } + case CMD_PWD: { + if( !LoggedIn( ) ) { return 550; }//Not logged in. + if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. + return 2570;//257 -- 0 to indicate PWD over MKD + break; + } + case CMD_CWD: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + Filesystem::Directory TmpDir = MyDir; + if( TmpDir.CWD( Command ) ) { + if( TmpDir.IsDir( ) ) { + MyDir = TmpDir; + return 250; + } + } + return 550; + break; + } + case CMD_CDUP: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. + Filesystem::Directory TmpDir = MyDir; + if( TmpDir.CDUP( ) ) { + if( TmpDir.IsDir( ) ) { + MyDir = TmpDir; + return 250; + } + } + return 550; + break; + } + case CMD_DELE: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( !MyDir.DELE( Command ) ) { return 550; } + return 250; + break; + } + case CMD_RMD: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( !MyDir.HasPermission( Filesystem::P_RMD ) ) { return 550; } + if( !MyDir.DELE( Command ) ) { return 550; } + return 250; + break; + } + case CMD_MKD: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( !MyDir.HasPermission( Filesystem::P_MKD ) ) { return 550; } + if( !MyDir.MKD( Command ) ) { return 550; } + return 2571; + break; + } + case CMD_RNFR: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + RNFR = Command; + return 350;//Awaiting further information + } + case CMD_RNTO: { + if( !LoggedIn( ) ) { return 530; }//Not logged in. + if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. + if( RNFR == "" ) { return 503; } //Bad sequence of commands + if( !MyDir.Rename( RNFR, Command ) ) { return 550; } + return 250; + } + default: { + return 502;//Command not implemented. + break; + } + } +} + +bool FTP::User::LoggedIn( ) { + if( USER == "" || PASS == "" ) { return false; } + if( !AllCredentials.size() ) { + return true; + } + if( ( AllCredentials.find( USER ) != AllCredentials.end() ) && AllCredentials[USER] == PASS ) { + return true; + } + return false; +} + +std::string FTP::User::NumToMsg( int MsgNum ) { + std::string Result; + switch( MsgNum ) { + case 200: { + Result = "200 Message okay.\n"; + break; + } + case 221: { + Result = "221 Service closing control connection. Logged out if appropriate.\n"; + break; + } + case 226: { + Result = "226 Closing data connection.\n"; + break; + } + case 227: { + std::stringstream sstr; + sstr << "227 Entering passive mode (0,0,0,0,"; + sstr << (MyPassivePort >> 8) % 256; + sstr << ","; + sstr << MyPassivePort % 256; + sstr << ").\n"; + Result = sstr.str(); + break; + } + case 229: { + std::stringstream sstr; + sstr << "229 Entering extended passive mode (|||"; + sstr << MyPassivePort; + sstr << "|).\n"; + Result = sstr.str(); + break; + } + case 230: { + Result = "230 User logged in, proceed.\n"; + break; + } + case 250: { + Result = "250 Requested file action okay, completed.\n"; + break; + } + case 2570: {//PWD + Result = "257 \"" + MyDir.PWD( ) + "\" selected as PWD\n"; + break; + } + case 2571: {//MKD + Result = "257 \"" + MyDir.PWD( ) + "\" created\n"; + break; + } + case 331: { + Result = "331 User name okay, need password.\n"; + break; + } + case 350: { + Result = "350 Requested file action pending further information\n"; + break; + } + case 501: { + Result = "501 Syntax error in parameters or arguments.\n"; + break; + } + case 502: { + Result = "502 Command not implemented.\n"; + break; + } + case 503: { + Result = "503 Bad sequence of commands.\n"; + break; + } + case 504: { + Result = "504 Command not implemented for that parameter.\n"; + break; + } + case 530: { + Result = "530 Not logged in.\n"; + break; + } + case 550: { + Result = "550 Requested action not taken.\n"; + break; + } + default: { + Result = "Error msg not implemented?\n"; + break; + } + } + return Result; +} diff --git a/lib/ftp.h b/lib/ftp.h new file mode 100644 index 00000000..2ea6b3c2 --- /dev/null +++ b/lib/ftp.h @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include "./socket.h" +#include "./filesystem.h" +#include + +#include "./json.h" + +namespace FTP { + static std::string FTPBasePath = "/tmp/mist/OnDemand/"; + + enum Mode { + MODE_STREAM, + };//FTP::Mode enumeration + + enum Structure { + STRU_FILE, + STRU_RECORD, + };//FTP::Structure enumeration + + enum Type { + TYPE_ASCII_NONPRINT, + TYPE_IMAGE_NONPRINT, + };//FTP::Type enumeration + + enum Commands { + CMD_NOCMD, + CMD_NOOP, + CMD_USER, + CMD_PASS, + CMD_QUIT, + CMD_PORT, + CMD_RETR, + CMD_STOR, + CMD_TYPE, + CMD_MODE, + CMD_STRU, + CMD_EPSV, + CMD_PASV, + CMD_LIST, + CMD_PWD, + CMD_CWD, + CMD_CDUP, + CMD_DELE, + CMD_RMD, + CMD_MKD, + CMD_RNFR, + CMD_RNTO, + };//FTP::Commands enumeration + + class User { + public: + User( Socket::Connection NewConnection = Socket::Connection(), std::map Credentials = std::map() ); + ~User( ); + int ParseCommand( std::string Command ); + bool LoggedIn( ); + std::string NumToMsg( int MsgNum ); + Socket::Connection Conn; + private: + std::map AllCredentials; + std::string USER; + std::string PASS; + Mode MODE; + Structure STRU; + Type TYPE; + int PORT; + Socket::Server Passive; + int MyPassivePort; + Filesystem::Directory MyDir; + std::string RNFR; + std::vector< std::string > ActiveStreams; + };//FTP::User class + +};//FTP Namespace