69 #include "../../include/esp3d_config.h"
70 #if defined (FTP_FEATURE)
71 #include <WiFiServer.h>
72 #include <WiFiClient.h>
75 #include "../network/netconfig.h"
76 #include "../authentication/authentication_service.h"
77 #include "../../core/settings_esp3d.h"
78 #include "../../core/esp3doutput.h"
79 #if FTP_FEATURE == FS_ROOT
80 #include "../filesystem/esp_globalFS.h"
83 #endif //FTP_FEATURE == FS_ROOT
85 #if FTP_FEATURE == FS_FLASH
86 #include "../filesystem/esp_filesystem.h"
89 #endif //FTP_FEATURE == FS_FLASH
91 #if FTP_FEATURE == FS_SD
92 #include "../filesystem/esp_sd.h"
95 #endif //FTP_FEATURE == FS_SD
101 #define SIZELISTPADING 15
110 if( c ==
'"' || c ==
'*' || c ==
'?' || c ==
':' ||
111 c ==
'<' || c ==
'>' || c ==
'|' ) {
114 return 0x1f < c && c < 0x7f;
125 return client.connected();
131 dataServer =
nullptr;
151 dataServer =
nullptr;
164 if (client && client.connected()) {
165 res = client.remoteIP().toString();
184 ftpServer =
new WiFiServer(ctrlPort);
188 dataServer =
new WiFiServer(passivePort);
194 ftpServer->setNoDelay(
true );
203 void FtpServer::iniVariables()
206 dataPort = activePort;
212 strcpy( cwdName,
"/" );
225 int8_t data0 = data.status();
227 ftpCmd cmdStage0 = cmdStage;
230 if((int32_t) ( millisDelay - millis() ) > 0 ) {
236 if( client.connected()) {
243 log_esp3d(
" Ftp server waiting for connection on port %d", ctrlPort);
246 if( ftpServer->hasClient()) {
248 client = ftpServer->available();
250 if( client.connected()) {
255 }
else if( readChar() > 0 ) {
259 }
else if( cmdStage <
FTP_Cmd ) {
260 millisDelay = millis() + 200;
264 }
else if( ! client.connected() ) {
269 if( ! doRetrieve()) {
272 }
else if( transferStage ==
FTP_Store ) {
276 }
else if( transferStage ==
FTP_List ||
281 }
else if( transferStage ==
FTP_Mlsd ) {
286 ! ((int32_t) ( millisEndConnection - millis() ) > 0 )) {
287 client << F(
"530 Timeout") <<
eol;
288 millisDelay = millis() + 200;
293 uint8_t dstat = data.status();
294 if( cmdStage != cmdStage0 || transferStage != transferStage0 ||
296 log_esp3d (
" Command: %d Transfer: %d Data: %d", cmdStage, transferStage,
_HEX( dstat ));
301 void FtpServer::clientConnected()
304 client << F(
"220--- Welcome to FTP for ESP3D ---") <<
eol;
305 client << F(
"220 -- Version ") <<
FW_VERSION << F(
" --") <<
eol;
313 #ifdef AUTHENTICATION_FEATURE
319 #endif //AUTHENTICATION_FEATURE
322 log_esp3d(
"User is %s",_currentUser.c_str());
329 #ifdef AUTHENTICATION_FEATURE
336 #endif //AUTHENTICATION_FEATURE
342 void FtpServer::disconnectClient()
346 client << F(
"221 Goodbye") <<
eol;
351 bool FtpServer::processCommand()
363 log_esp3d(
"USER : Command: %s Param: %s", command, (parameter ==
nullptr)?
"":parameter);
365 client << F(
"331 Ok. Password required") <<
eol;
366 strcpy( cwdName,
"/" );
370 client << F(
"530 ") <<
eol;
378 log_esp3d(
"PASS : Command: %s Param: %s", command, (parameter ==
nullptr)?
"":parameter);
381 client << F(
"503 ") <<
eol;
385 log_esp3d(
" Authentication Ok. Waiting for commands.");
386 client << F(
"230 Ok") <<
eol;
390 client << F(
"530 ") <<
eol;
398 client << F(
"211-Extensions suported:") <<
eol;
399 client << F(
" MLST type*;modify*;size*;") <<
eol;
400 client << F(
" MLSD") <<
eol;
401 client << F(
" MDTM") <<
eol;
402 client << F(
" MFMT") <<
eol;
403 client << F(
" SIZE") <<
eol;
404 client << F(
" SITE FREE") <<
eol;
405 client << F(
"211 End.") <<
eol;
411 client << F(
"502 ") <<
eol;
417 log_esp3d(
"Unsupported Command: %s Param: %s stage %d", command, (parameter ==
nullptr)?
"":parameter, cmdStage);
418 client << F(
"500 ") <<
eol;
425 log_esp3d(
"Unknow Command: %s Param: %s stage %d", command, (parameter ==
nullptr)?
"":parameter, cmdStage);
426 client << F(
"200 ") <<
eol;
441 client << F(
"257 \"") << cwdName << F(
"\"") << F(
" is your current directory") <<
eol;
450 if( strlen( cwdName ) > 1 ) {
452 if( cwdName[ strlen( cwdName ) - 1 ] ==
'/' ) {
453 cwdName[ strlen( cwdName ) - 1 ] = 0;
456 char * pSep = strrchr( cwdName,
'/' );
466 strcpy( cwdName,
"/" );
468 client << F(
"250 Ok. Current directory is ") << cwdName <<
eol;
475 if( haveParameter() && makeExistsPath( path )) {
476 strcpy( cwdName, path );
477 client << F(
"250 Directory changed to ") << cwdName <<
eol;
485 client << F(
"221 Goodbye") <<
eol;
501 client << F(
"200 S Ok") <<
eol;
503 client << F(
"504 Only S(tream) is suported") <<
eol;
513 dataPort = passivePort;
514 log_esp3d(
" Connection management set to passive");
515 log_esp3d(
" Data port set to %d", dataPort);
516 client << F(
"227 Entering Passive Mode") << F(
" (")
517 << dataIp[0] << F(
",") << dataIp[1] << F(
",")
518 << dataIp[2] << F(
",") << dataIp[3] << F(
",")
519 << ( dataPort >> 8 ) << F(
",") << ( dataPort & 255 ) << F(
")") <<
eol;
528 dataIp[ 0 ] = atoi( parameter );
529 char * p = strchr( parameter,
',' );
530 for( uint8_t i = 1; i < 4; i ++ ) {
531 dataIp[ i ] = atoi( ++ p );
532 p = strchr( p,
',' );
535 dataPort = 256 * atoi( ++ p );
536 p = strchr( p,
',' );
537 dataPort += atoi( ++ p );
539 client << F(
"501 Can't interpret parameters") <<
eol;
541 log_esp3d(
" Data IP set to %s", dataIp.toString().c_str());
542 log_esp3d(
" Data port set to %d", dataPort);
543 client << F(
"200 PORT command successful") <<
eol;
552 client << F(
"200 F Ok") <<
eol;
557 client << F(
"504 Only F(ile) is suported") <<
eol;
565 client << F(
"200 TYPE is now ASCII") <<
eol;
567 client << F(
"200 TYPE is now 8-bit binary") <<
eol;
569 client << F(
"504 Unknow TYPE") <<
eol;
584 client << F(
"226 Data connection closed") <<
eol;
591 if(haveParameter() && makeExistsPath( path )) {
593 client << F(
"250 Deleted ") << parameter <<
eol;
595 client << F(
"450 Can't delete ") << parameter <<
eol;
618 client << F(
"550 Can't open directory ") << cwdName <<
eol;
630 if( haveParameter() && makeExistsPath( path )) {
631 if( ! getFileModTime( path, t )) {
632 client << F(
"550 Unable to retrieve time for ") << parameter <<
eol;
639 client << F(
"250-Begin") <<
eol
640 << F(
" Type=") << ( isdir ? F(
"dir") : F(
"file"))
641 << F(
";Modify=") << makeDateTimeStr( dtStr, t );
643 client << F(
";Size=") <<
file.
size();
645 client << F(
"; ") << path <<
eol
646 << F(
"250 End.") <<
eol;
654 client << F(
"200 Zzz...") <<
eol;
661 if( haveParameter() && makeExistsPath( path )) {
664 client << F(
"450 Can't open ") << parameter <<
eol;
665 }
else if( dataConnect(
false )) {
667 client << F(
"150-Connected to port ") << dataPort <<
eol;
668 client << F(
"150 ") <<
file.
size() << F(
" bytes to download") <<
eol;
669 millisBeginTrans = millis();
681 if( haveParameter() && makePath( path )) {
688 client << F(
"451 Can't open/create ") << parameter <<
eol;
689 }
else if( ! dataConnect()) {
693 millisBeginTrans = millis();
704 if( haveParameter() && makePath( path )) {
706 client << F(
"521 \"") << parameter << F(
"\" directory already exists") <<
eol;
708 log_esp3d(
" Creating directory %s", parameter);
710 client << F(
"257 \"") << parameter << F(
"\"") << F(
" created") <<
eol;
712 client << F(
"550 Can't create \"") << parameter << F(
"\"") <<
eol;
722 if( haveParameter() && makeExistsPath( path )) {
725 client << F(
"250 \"") << parameter << F(
"\" deleted") <<
eol;
727 client << F(
"550 Can't remove \"") << parameter << F(
"\". Directory not empty?") <<
eol;
736 if( haveParameter() && makeExistsPath( rnfrName )) {
737 log_esp3d(
" Ready for renaming %s", rnfrName);
738 client << F(
"350 RNFR accepted - file exists, ready for destination") <<
eol;
748 if( strlen( rnfrName ) == 0 || ! rnfrCmd ) {
749 client << F(
"503 Need RNFR before RNTO") <<
eol;
750 }
else if( haveParameter() && makePath( path )) {
752 client << F(
"553 ") << parameter << F(
" already exists") <<
eol;
754 strcpy( dirp, path );
755 char * psep = strrchr( dirp,
'/' );
756 bool fail = psep == NULL;
766 client << F(
"550 \"") << dirp << F(
"\" is not directory") <<
eol;
768 log_esp3d(
" Renaming %s to %s", rnfrName, path);
770 client << F(
"250 File successfully renamed or moved") <<
eol;
777 client << F(
"451 Rename/move failure") <<
eol;
788 client << F(
"215 ESP3D") <<
eol;
802 if( haveParameter()) {
804 char * fname = parameter;
806 uint8_t month, day, hour, minute, second, setTime;
810 setTime = getDateTime( dt, & year, & month, & day, & hour, & minute, & second );
813 if( strlen( fname ) <= 0 ) {
814 client <<
"501 No file name" <<
eol;
815 }
else if( makeExistsPath( path, fname )) {
817 if( timeStamp( path, year, month, day, hour, minute, second )) {
818 client <<
"213 " << dt <<
eol;
820 client <<
"550 Unable to modify time" <<
eol;
826 if( getFileModTime( path, t)) {
827 client <<
"213 " << makeDateTimeStr( dtStr, t ) <<
eol;
829 client <<
"550 Unable to retrieve time" <<
eol;
840 if( haveParameter() && makeExistsPath( path )) {
844 client << F(
"450 Can't open ") << parameter <<
eol;
855 #if FTP_FEATURE == FS_ROOT
860 #if FTP_FEATURE == FS_FLASH
864 #if FTP_FEATURE == FS_SD
875 client << F(
"500 Unknow SITE command ") << parameter <<
eol;
882 client << F(
"500 Unknow command") <<
eol;
887 int FtpServer::dataConnect(
bool out150 )
889 if( ! data.connected()) {
891 uint16_t count = 1000;
892 while( ! data.connected() && count -- > 0 ) {
893 if( dataServer->hasClient()) {
895 data = dataServer->available();
900 data.connect( dataIp, dataPort );
903 if( ! data.connected()) {
904 client << F(
"425 No data connection") <<
eol;
905 }
else if( out150 ) {
906 client << F(
"150 Accepted data connection to port ") << dataPort <<
eol;
909 return data.connected();
912 bool FtpServer::dataConnected()
914 if( data.connected()) {
918 client << F(
"426 Data connection closed. Transfer aborted") <<
eol;
923 bool FtpServer::doRetrieve()
925 if( ! dataConnected()) {
931 data.write( buf, nb );
932 bytesTransfered += nb;
939 bool FtpServer::doStore()
941 int16_t na = data.available();
943 if( data.connected()) {
953 int16_t nb = data.read((uint8_t *) buf, na );
957 bytesTransfered += nb;
959 if( nb < 0 || rc == nb ) {
962 client << F(
"552 Probably insufficient storage space") <<
eol;
968 bool FtpServer::doList()
970 if( ! dataConnected()) {
982 data << (
file.
isDirectory()?
"d":
"-") <<
"rwxrwxrwx 1 " << _currentUser.c_str() <<
" " << _currentUser.c_str();
993 client << F(
"226 ") << nbMatch << F(
" matches total") <<
eol;
999 bool FtpServer::doMlsd()
1001 if( ! dataConnected()) {
1013 data <<
"Type=" << (
file.
isDirectory() ? F(
"dir") : F(
"file")) <<
";Size=" <<
file.size() <<
";Modify=" << makeDateTimeStr( dtStr, t) <<
"; " <<
file.name() <<
eol;
1020 client << F(
"226-options: -a -l") <<
eol;
1021 client << F(
"226 ") << nbMatch << F(
" matches total") <<
eol;
1027 void FtpServer::closeTransfer()
1029 uint32_t deltaT = (int32_t) ( millis() - millisBeginTrans );
1030 if( deltaT > 0 && bytesTransfered > 0 ) {
1031 log_esp3d(
" Transfer completed in %d ms, %f kbytes/s", deltaT, 1.0*bytesTransfered / deltaT);
1032 client << F(
"226-File successfully transferred") <<
eol;
1033 client << F(
"226 ") << deltaT << F(
" ms, ")
1034 << bytesTransfered / deltaT << F(
" kbytes/s") <<
eol;
1036 client << F(
"226 File successfully transferred") <<
eol;
1043 void FtpServer::abortTransfer()
1048 client << F(
"426 Transfer aborted") <<
eol;
1066 int8_t FtpServer::readChar()
1070 if( client.available()) {
1071 char c = client.read();
1079 cmdLine[ iCL ++ ] = c;
1093 parameter = strchr( cmdLine,
' ' );
1094 if( parameter != NULL ) {
1095 if( parameter - cmdLine > 4 ) {
1098 strncpy( command, cmdLine, parameter - cmdLine );
1099 command[ parameter - cmdLine ] = 0;
1100 while( * ( ++ parameter ) ==
' ' )
1103 }
else if( strlen( cmdLine ) > 4 ) {
1106 strcpy( command, cmdLine );
1113 for( uint8_t i = 0 ; i < strlen( command ); i ++ ) {
1114 command[ i ] = toupper( command[ i ] );
1118 client << F(
"500 Syntax error") <<
eol;
1124 bool FtpServer::haveParameter()
1126 if( parameter != NULL && strlen( parameter ) > 0 ) {
1129 client <<
"501 No file name" <<
eol;
1143 bool FtpServer::makePath(
char * fullName,
char * param )
1145 if( param == NULL ) {
1150 if( strcmp( param,
"/" ) == 0 || strlen( param ) == 0 ) {
1151 strcpy( fullName,
"/" );
1155 if( param[0] !=
'/' ) {
1156 strcpy( fullName, cwdName );
1157 if( fullName[ strlen( fullName ) - 1 ] !=
'/' ) {
1162 strcpy( fullName, param );
1165 uint16_t strl = strlen( fullName ) - 1;
1166 if( fullName[ strl ] ==
'/' && strl > 1 ) {
1167 fullName[ strl ] = 0;
1170 client << F(
"500 Command line too long") <<
eol;
1173 for( uint8_t i = 0; i < strlen( fullName ); i ++ )
1175 client << F(
"553 File name not allowed") <<
eol;
1181 bool FtpServer::makeExistsPath(
char * path,
char * param )
1183 if( ! makePath( path, param )) {
1189 client << F(
"550 ") << path << F(
" not found.") <<
eol;
1210 uint8_t FtpServer::getDateTime(
char * dt, uint16_t * pyear, uint8_t * pmonth, uint8_t * pday,
1211 uint8_t * phour, uint8_t * pminute, uint8_t * psecond )
1215 if( strlen( parameter ) < 15 ) {
1218 for( i = 0; i < 14; i ++ )
1219 if( ! isdigit( parameter[ i ])) {
1222 for( i = 14; i < 18; i ++ )
1223 if( parameter[ i ] ==
' ' ) {
1225 }
else if( ! isdigit( parameter[ i ])) {
1233 strncpy( dt, parameter, 14 );
1235 * psecond = atoi( dt + 12 );
1237 * pminute = atoi( dt + 10 );
1239 * phour = atoi( dt + 8 );
1241 * pday = atoi( dt + 6 );
1243 * pmonth = atoi( dt + 4 );
1245 * pyear = atoi( dt );
1246 strncpy( dt, parameter, 14 );
1247 log_esp3d(
" Modification time: %d/%d/%d %d:%d:%d of file: %s", * pyear, * pmonth, * pday, * phour, * pminute, * psecond, (
char *) ( parameter + i ));
1260 char * FtpServer::makeDateTimeStr(
char * tstr, time_t timefile )
1262 struct tm * tmstruct = localtime(&timefile);
1263 sprintf( tstr,
"%04u%02u%02u%02u%02u%02u",(tmstruct->tm_year)+1900,(tmstruct->tm_mon)+1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
1276 char * FtpServer::makeDateTimeString(
char * tstr, time_t timefile )
1278 struct tm * tmstruct = localtime(&timefile);
1281 struct tm tmstructnow;
1282 localtime_r(&now, &tmstructnow);
1283 if ((tmstruct->tm_year == tmstructnow.tm_year) && (timefile != 0)) {
1284 strftime (tstr, 13,
"%b %d %R", tmstruct);
1286 strftime (tstr, 13,
"%b %d %Y", tmstruct);
1292 bool FtpServer::getFileModTime(
const char * path, time_t & t)
1305 bool FtpServer::timeStamp(
const char * path, uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second )
1311 #endif //FTP_FEATURE