ESP3D  3.0
Firmware for ESP boards connected to 3D Printer
FtpServer.cpp
Go to the documentation of this file.
1 /*
2  * FTP Serveur for Arduino Due or Mega 2580
3  * and Ethernet shield W5100, W5200 or W5500
4  * or for Esp8266 with external SD card or SpiFfs
5  * Copyright (c) 2014-2018 by Jean-Michel Gallego
6  *
7  * Please read file ReadMe.txt for instructions
8  *
9  * Use ExtStreaming based on Streaming from Mial Hart
10  *
11  * Use FatLib library to easily switch between
12  * libraries SdFat, FatFs or SpiFfs
13  *
14  * Use Ethernet library (version 2.0.0) or
15  * ESP8266WiFi library
16  *
17  * Commands implemented:
18  * USER, PASS, AUTH (AUTH only return 'not implemented' code)
19  * CDUP, CWD, PWD, QUIT, NOOP
20  * MODE, PASV, PORT, STRU, TYPE
21  * ABOR, DELE, LIST, NLST, MLST, MLSD
22  * APPE, RETR, STOR
23  * MKD, RMD
24  * RNTO, RNFR
25  * MDTM, MFMT
26  * FEAT, SIZE
27  * SITE FREE
28  *
29  * Tested with those clients:
30  * under Windows:
31  * FTP Rush
32  * Filezilla
33  * WinSCP
34  * NcFTP, ncftpget, ncftpput
35  * Firefox
36  * command line ftp.exe
37  * under Ubuntu:
38  * gFTP
39  * Filezilla
40  * NcFTP, ncftpget, ncftpput
41  * lftp
42  * ftp
43  * Firefox
44  * under Android:
45  * AndFTP
46  * FTP Express
47  * Firefox
48  * with a second Arduino and sketch of SurferTim at
49  * http://playground.arduino.cc/Code/FTP
50  *
51  * This program is free software: you can redistribute it and/or modify
52  * it under the terms of the GNU General Public License as published by
53  * the Free Software Foundation, either version 3 of the License, or
54  * (at your option) any later version.
55  *
56  * This program is distributed in the hope that it will be useful,
57  * but WITHOUT ANY WARRANTY; without even the implied warranty of
58  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59  * GNU General Public License for more details.
60  *
61  * You should have received a copy of the GNU General Public License
62  * along with this program. If not, see <http://www.gnu.org/licenses/>.
63  */
64 /*
65  * 2019-10-27 Modified version for ESP3D by Luc LEBOSSE @luc-github
66  * support for ESP8266 and ESP32 in ESP3D project
67  */
68 
69 #include "../../include/esp3d_config.h"
70 #if defined (FTP_FEATURE)
71 #include <WiFiServer.h>
72 #include <WiFiClient.h>
73 #include "FtpServer.h"
74 #include "ExtStreaming.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"
82 typedef ESP_GBFS FTPFS;
83 #endif //FTP_FEATURE == FS_ROOT
84 
85 #if FTP_FEATURE == FS_FLASH
86 #include "../filesystem/esp_filesystem.h"
87 typedef ESP_File FTPFile;
88 typedef ESP_FileSystem FTPFS;
89 #endif //FTP_FEATURE == FS_FLASH
90 
91 #if FTP_FEATURE == FS_SD
92 #include "../filesystem/esp_sd.h"
93 typedef ESP_SDFile FTPFile;
94 typedef ESP_SD FTPFS;
95 #endif //FTP_FEATURE == FS_SD
96 
97 // Uncomment to print additional info for log_esp3d
98 //#define FTP_DEBUG
99 
100 //width in char of file size output in listing
101 #define SIZELISTPADING 15
102 
105 
107 
108 bool legalChar( char c )
109 {
110  if( c == '"' || c == '*' || c == '?' || c == ':' ||
111  c == '<' || c == '>' || c == '|' ) {
112  return false;
113  }
114  return 0x1f < c && c < 0x7f;
115 }
116 
117 
119 {
120  client.stop();
121 }
122 
124 {
125  return client.connected();
126 }
127 
129 {
130  ftpServer = nullptr;
131  dataServer = nullptr;
132  ctrlPort = 0;
133  activePort = 0;
134  passivePort = 0;
135  _root = FS_ROOT;
136 }
137 
139 {
140  end();
141 }
142 
144 {
145  if(ftpServer) {
146  delete(ftpServer);
147  ftpServer = nullptr;
148  }
149  if(dataServer) {
150  delete(dataServer);
151  dataServer = nullptr;
152  }
153  ctrlPort = 0;
154  activePort = 0;
155  passivePort = 0;
156  _started = false;
157  _root = FS_ROOT;
158 }
159 
161 {
162  static String res;
163  res = "0.0.0.0";
164  if (client && client.connected()) {
165  res = client.remoteIP().toString();
166  }
167  return res.c_str();
168 }
169 
171 {
172  return _started;
173 }
174 
176 {
177  end();
179  return true;
180  }
184  ftpServer = new WiFiServer(ctrlPort);
185  if (!ftpServer) {
186  return false;
187  }
188  dataServer = new WiFiServer(passivePort);
189  if (!dataServer) {
190  return false;
191  }
192  // Tells the ftp server to begin listening for incoming connection
193  ftpServer->begin();
194  ftpServer->setNoDelay( true );
195  dataServer->begin();
196  millisDelay = 0;
197  cmdStage = FTP_Stop;
198  iniVariables();
199  _started = true;
200  return _started;
201 }
202 
203 void FtpServer::iniVariables()
204 {
205  // Default for data port
206  dataPort = activePort;
207 
208  // Default Data connection is Active
209  dataConn = FTP_NoConn;
210 
211  // Set the root directory
212  strcpy( cwdName, "/" );
213 
214  rnfrCmd = false;
215  transferStage = FTP_Close;
216 }
217 
218 
220 {
221  if (!_started) {
222  return;
223  }
224 #ifdef FTP_DEBUG
225  int8_t data0 = data.status();
226  ftpTransfer transferStage0 = transferStage;
227  ftpCmd cmdStage0 = cmdStage;
228 #endif
229 
230  if((int32_t) ( millisDelay - millis() ) > 0 ) {
231  return;
232  }
233 
234  if( cmdStage == FTP_Stop ) {
235  log_esp3d("FTP_STOP");
236  if( client.connected()) {
237  disconnectClient();
238  }
239  cmdStage = FTP_Init;
240  } else if( cmdStage == FTP_Init ) { // Ftp server waiting for connection
241  abortTransfer();
242  iniVariables();
243  log_esp3d(" Ftp server waiting for connection on port %d", ctrlPort);
244  cmdStage = FTP_Client;
245  } else if( cmdStage == FTP_Client ) { // Ftp server idle
246  if( ftpServer->hasClient()) {
247  client.stop();
248  client = ftpServer->available();
249  }
250  if( client.connected()) { // A client connected
251  clientConnected();
252  millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait client id for 10 s.
253  cmdStage = FTP_User;
254  }
255  } else if( readChar() > 0 ) { // got response
256  processCommand();
257  if( cmdStage == FTP_Stop ) {
258  millisEndConnection = millis() + 1000L * FTP_AUTH_TIME_OUT; // wait authentication for 10 s.
259  } else if( cmdStage < FTP_Cmd ) {
260  millisDelay = millis() + 200; // delay of 100 ms
261  } else {
262  millisEndConnection = millis() + 1000L * FTP_TIME_OUT;
263  }
264  } else if( ! client.connected() ) {
265  cmdStage = FTP_Init;
266  }
267 
268  if( transferStage == FTP_Retrieve ) { // Retrieve data
269  if( ! doRetrieve()) {
270  transferStage = FTP_Close;
271  }
272  } else if( transferStage == FTP_Store ) { // Store data
273  if( ! doStore()) {
274  transferStage = FTP_Close;
275  }
276  } else if( transferStage == FTP_List ||
277  transferStage == FTP_Nlst) { // LIST or NLST
278  if( ! doList()) {
279  transferStage = FTP_Close;
280  }
281  } else if( transferStage == FTP_Mlsd ) { // MLSD listing
282  if( ! doMlsd()) {
283  transferStage = FTP_Close;
284  }
285  } else if( cmdStage > FTP_Client &&
286  ! ((int32_t) ( millisEndConnection - millis() ) > 0 )) {
287  client << F("530 Timeout") << eol;
288  millisDelay = millis() + 200; // delay of 200 ms
289  cmdStage = FTP_Stop;
290  }
291 
292 #ifdef FTP_DEBUG
293  uint8_t dstat = data.status();
294  if( cmdStage != cmdStage0 || transferStage != transferStage0 ||
295  dstat != data0 ) {
296  log_esp3d (" Command: %d Transfer: %d Data: %d", cmdStage, transferStage, _HEX( dstat ));
297  }
298 #endif
299 }
300 
301 void FtpServer::clientConnected()
302 {
303  log_esp3d(" Client connected!");
304  client << F("220--- Welcome to FTP for ESP3D ---") << eol;
305  client << F("220 -- Version ") << FW_VERSION << F(" --") << eol;
306  iCL = 0;
307 }
308 
309 bool FtpServer::isUser(const char * user)
310 {
311  log_esp3d("Check User");
312  _currentUser = "";
313 #ifdef AUTHENTICATION_FEATURE
314  if ((user != nullptr) && ((strcmp(user, DEFAULT_ADMIN_LOGIN) == 0) || (strcmp(user, DEFAULT_USER_LOGIN) == 0))) {
315  _currentUser = user;
316  return true;
317  }
318  return false;
319 #endif //AUTHENTICATION_FEATURE
320  (void)user;
321  _currentUser = DEFAULT_ADMIN_LOGIN;
322  log_esp3d("User is %s",_currentUser.c_str());
323  return true;
324 
325 }
326 bool FtpServer::isPassword(const char * password)
327 {
328  log_esp3d("Check Password");
329 #ifdef AUTHENTICATION_FEATURE
330  if(((_currentUser == DEFAULT_ADMIN_LOGIN) && AuthenticationService::isadmin(password)) ||
331  ((_currentUser == DEFAULT_USER_LOGIN) && AuthenticationService::isuser(password))) {
332  log_esp3d("Password ok");
333  return true;
334  }
335  return false;
336 #endif //AUTHENTICATION_FEATURE
337  (void)password;
338  log_esp3d("Password ok");
339  return true;
340 }
341 
342 void FtpServer::disconnectClient()
343 {
344  log_esp3d(" Disconnecting client");
345  abortTransfer();
346  client << F("221 Goodbye") << eol;
347  client.stop();
348  _currentUser = "";
349 }
350 
351 bool FtpServer::processCommand()
352 {
354  // //
355  // AUTHENTICATION COMMANDS //
356  // //
358 
359  //
360  // USER - User Identity
361  //
362  if( CommandIs( "USER" )) {
363  log_esp3d("USER : Command: %s Param: %s", command, (parameter == nullptr)?"":parameter);
364  if( isUser(parameter)) {
365  client << F("331 Ok. Password required") << eol;
366  strcpy( cwdName, "/" );
367  cmdStage = FTP_Pass;
368  } else {
369  log_esp3d("Error USER");
370  client << F("530 ") << eol;
371  cmdStage = FTP_Stop;
372  }
373  }
374  //
375  // PASS - Password
376  //
377  else if( CommandIs( "PASS" )) {
378  log_esp3d("PASS : Command: %s Param: %s", command, (parameter == nullptr)?"":parameter);
379  if( cmdStage != FTP_Pass ) {
380  log_esp3d("Error PASS");
381  client << F("503 ") << eol;
382  cmdStage = FTP_Stop;
383  }
384  if( isPassword(parameter)) {
385  log_esp3d(" Authentication Ok. Waiting for commands.");
386  client << F("230 Ok") << eol;
387  cmdStage = FTP_Cmd;
388  } else {
389  log_esp3d("Wrong PASS");
390  client << F("530 ") << eol;
391  cmdStage = FTP_Stop;
392  }
393  }
394  //
395  // FEAT - New Features
396  //
397  else if( CommandIs( "FEAT" )) {
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;
406  }
407  //
408  // AUTH - Not implemented
409  //
410  else if( CommandIs( "AUTH" )) {
411  client << F("502 ") << eol;
412  }
413  //
414  // OPTS / SYST - Not implemented
415  //
416  else if((cmdStage < FTP_Cmd) && ( CommandIs( "OPTS" ) || CommandIs( "SYST" ) || CommandIs( "TYPE" ))) {
417  log_esp3d("Unsupported Command: %s Param: %s stage %d", command, (parameter == nullptr)?"":parameter, cmdStage);
418  client << F("500 ") << eol;
419  cmdStage = FTP_User;
420  }
421  //
422  // Unrecognized commands at stage of authentication
423  //
424  else if(cmdStage < FTP_Cmd) {
425  log_esp3d("Unknow Command: %s Param: %s stage %d", command, (parameter == nullptr)?"":parameter, cmdStage);
426  client << F("200 ") << eol;
427  cmdStage = FTP_Stop;
428  }
429 
431  // //
432  // ACCESS CONTROL COMMANDS //
433  // //
435 
436  //
437  // PWD - Print Directory
438  //
439  else if( CommandIs( "PWD" ) ||
440  ( CommandIs( "CWD" ) && ParameterIs( "." ))) {
441  client << F("257 \"") << cwdName << F("\"") << F(" is your current directory") << eol;
442  }
443  //
444  // CDUP - Change to Parent Directory
445  //
446  else if( CommandIs( "CDUP" ) ||
447  ( CommandIs( "CWD" ) && ParameterIs( ".." ))) {
448  bool ok = false;
449 
450  if( strlen( cwdName ) > 1 ) { // do nothing if cwdName is root
451  // if cwdName ends with '/', remove it (must not append)
452  if( cwdName[ strlen( cwdName ) - 1 ] == '/' ) {
453  cwdName[ strlen( cwdName ) - 1 ] = 0;
454  }
455  // search last '/'
456  char * pSep = strrchr( cwdName, '/' );
457  ok = pSep > cwdName;
458  // if found, ends the string on its position
459  if( ok ) {
460  * pSep = 0;
461  ok = FTPFS::exists( cwdName );
462  }
463  }
464  // if an error appends, move to root
465  if( ! ok ) {
466  strcpy( cwdName, "/" );
467  }
468  client << F("250 Ok. Current directory is ") << cwdName << eol;
469  }
470  //
471  // CWD - Change Working Directory
472  //
473  else if( CommandIs( "CWD" )) {
474  char path[ FTP_CWD_SIZE ];
475  if( haveParameter() && makeExistsPath( path )) {
476  strcpy( cwdName, path );
477  client << F("250 Directory changed to ") << cwdName << eol;
478  }
479  }
480  //
481  // QUIT
482  //
483  else if( CommandIs( "QUIT" )) {
484  log_esp3d("QUIT");
485  client << F("221 Goodbye") << eol;
486  disconnectClient();
487  cmdStage = FTP_Stop;
488  }
489 
491  // //
492  // TRANSFER PARAMETER COMMANDS //
493  // //
495 
496  //
497  // MODE - Transfer Mode
498  //
499  else if( CommandIs( "MODE" )) {
500  if( ParameterIs( "S" )) {
501  client << F("200 S Ok") << eol;
502  } else {
503  client << F("504 Only S(tream) is suported") << eol;
504  }
505  }
506  //
507  // PASV - Passive Connection management
508  //
509  else if( CommandIs( "PASV" )) {
510  data.stop();
511  dataServer->begin();
512  dataIp.fromString(NetConfig::localIP());
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;
520  dataConn = FTP_Pasive;
521  }
522  //
523  // PORT - Data Port
524  //
525  else if( CommandIs( "PORT" )) {
526  data.stop();
527  // get IP of data client
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, ',' );
533  }
534  // get port of data client
535  dataPort = 256 * atoi( ++ p );
536  p = strchr( p, ',' );
537  dataPort += atoi( ++ p );
538  if( p == NULL ) {
539  client << F("501 Can't interpret parameters") << eol;
540  } else {
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;
544  dataConn = FTP_Active;
545  }
546  }
547  //
548  // STRU - File Structure
549  //
550  else if( CommandIs( "STRU" )) {
551  if( ParameterIs( "F" )) {
552  client << F("200 F Ok") << eol;
553  }
554  // else if( ParameterIs( "R" ))
555  // client << F("200 B Ok") << eol;
556  else {
557  client << F("504 Only F(ile) is suported") << eol;
558  }
559  }
560  //
561  // TYPE - Data Type
562  //
563  else if( CommandIs( "TYPE" )) {
564  if( ParameterIs( "A" )) {
565  client << F("200 TYPE is now ASCII") << eol;
566  } else if( ParameterIs( "I" )) {
567  client << F("200 TYPE is now 8-bit binary") << eol;
568  } else {
569  client << F("504 Unknow TYPE") << eol;
570  }
571  }
572 
574  // //
575  // FTP SERVICE COMMANDS //
576  // //
578 
579  //
580  // ABOR - Abort
581  //
582  else if( CommandIs( "ABOR" )) {
583  abortTransfer();
584  client << F("226 Data connection closed") << eol;
585  }
586  //
587  // DELE - Delete a File
588  //
589  else if( CommandIs( "DELE" )) {
590  char path[ FTP_CWD_SIZE ];
591  if(haveParameter() && makeExistsPath( path )) {
592  if( FTPFS::remove( path )) {
593  client << F("250 Deleted ") << parameter << eol;
594  } else {
595  client << F("450 Can't delete ") << parameter << eol;
596  }
597  }
598  }
599  //
600  // LIST - List
601  // NLST - Name List
602  // MLSD - Listing for Machine Processing (see RFC 3659)
603  //
604  else if( CommandIs( "LIST" ) || CommandIs( "NLST" ) || CommandIs( "MLSD" )) {
605  if( dataConnect()) {
606  dir = FTPFS::open(cwdName);
607  }
608  if (dir) {
609  nbMatch = 0;
610  if( CommandIs( "LIST" )) {
611  transferStage = FTP_List;
612  } else if( CommandIs( "NLST" )) {
613  transferStage = FTP_Nlst;
614  } else {
615  transferStage = FTP_Mlsd;
616  }
617  } else {
618  client << F("550 Can't open directory ") << cwdName << eol;
619  data.stop();
620  }
621  }
622  //
623  // MLST - Listing for Machine Processing (see RFC 3659)
624  //
625  else if( CommandIs( "MLST" )) {
626  char path[ FTP_CWD_SIZE ];
627  time_t t = 0;
628  char dtStr[ 15 ];
629  bool isdir = false;
630  if( haveParameter() && makeExistsPath( path )) {
631  if( ! getFileModTime( path, t )) {
632  client << F("550 Unable to retrieve time for ") << parameter << eol;
633  } else {
634  if( file = FTPFS::open(path)) {
635  isdir = file.isDirectory();
636  t = file.getLastWrite();
637  file.close();
638  }
639  client << F("250-Begin") << eol
640  << F(" Type=") << ( isdir ? F("dir") : F("file"))
641  << F(";Modify=") << makeDateTimeStr( dtStr, t );
642  if( ! isdir ) {
643  client << F(";Size=") << file.size();
644  }
645  client << F("; ") << path << eol
646  << F("250 End.") << eol;
647  }
648  }
649  }
650  //
651  // NOOP
652  //
653  else if( CommandIs( "NOOP" )) {
654  client << F("200 Zzz...") << eol;
655  }
656  //
657  // RETR - Retrieve
658  //
659  else if( CommandIs( "RETR" )) {
660  char path[ FTP_CWD_SIZE ];
661  if( haveParameter() && makeExistsPath( path )) {
662  file = FTPFS::open(path);
663  if( ! file.isOpen()) {
664  client << F("450 Can't open ") << parameter << eol;
665  } else if( dataConnect( false )) {
666  log_esp3d(" Sending %s", parameter);
667  client << F("150-Connected to port ") << dataPort << eol;
668  client << F("150 ") << file.size() << F(" bytes to download") << eol;
669  millisBeginTrans = millis();
670  bytesTransfered = 0;
671  transferStage = FTP_Retrieve;
672  }
673  }
674  }
675  //
676  // STOR - Store
677  // APPE - Append
678  //
679  else if( CommandIs( "STOR" ) || CommandIs( "APPE" )) {
680  char path[ FTP_CWD_SIZE ];
681  if( haveParameter() && makePath( path )) {
682  if( FTPFS::exists( path )) {
684  } else {
685  file = FTPFS::open( path, ESP_FILE_WRITE );
686  }
687  if( ! file.isOpen() ) {
688  client << F("451 Can't open/create ") << parameter << eol;
689  } else if( ! dataConnect()) {
690  file.close();
691  } else {
692  log_esp3d(" Receiving %s", parameter);
693  millisBeginTrans = millis();
694  bytesTransfered = 0;
695  transferStage = FTP_Store;
696  }
697  }
698  }
699  //
700  // MKD - Make Directory
701  //
702  else if( CommandIs( "MKD" ) || CommandIs( "XMKD" )) {
703  char path[ FTP_CWD_SIZE ];
704  if( haveParameter() && makePath( path )) {
705  if( FTPFS::exists( path )) {
706  client << F("521 \"") << parameter << F("\" directory already exists") << eol;
707  } else {
708  log_esp3d(" Creating directory %s", parameter);
709  if( FTPFS::mkdir( path )) {
710  client << F("257 \"") << parameter << F("\"") << F(" created") << eol;
711  } else {
712  client << F("550 Can't create \"") << parameter << F("\"") << eol;
713  }
714  }
715  }
716  }
717  //
718  // RMD - Remove a Directory
719  //
720  else if( CommandIs( "RMD" ) || CommandIs( "XRMD" )) {
721  char path[ FTP_CWD_SIZE ];
722  if( haveParameter() && makeExistsPath( path )) {
723  if( FTPFS::rmdir( path )) {
724  log_esp3d(" Deleting %s", path);
725  client << F("250 \"") << parameter << F("\" deleted") << eol;
726  } else {
727  client << F("550 Can't remove \"") << parameter << F("\". Directory not empty?") << eol;
728  }
729  }
730  }
731  //
732  // RNFR - Rename From
733  //
734  else if( CommandIs( "RNFR" )) {
735  rnfrName[ 0 ] = 0;
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;
739  rnfrCmd = true;
740  }
741  }
742  //
743  // RNTO - Rename To
744  //
745  else if( CommandIs( "RNTO" )) {
746  char path[ FTP_CWD_SIZE ];
747  char dirp[ FTP_FIL_SIZE ];
748  if( strlen( rnfrName ) == 0 || ! rnfrCmd ) {
749  client << F("503 Need RNFR before RNTO") << eol;
750  } else if( haveParameter() && makePath( path )) {
751  if( FTPFS::exists( path )) {
752  client << F("553 ") << parameter << F(" already exists") << eol;
753  } else {
754  strcpy( dirp, path );
755  char * psep = strrchr( dirp, '/' );
756  bool fail = psep == NULL;
757  if( ! fail ) {
758  if( psep == dirp ) {
759  psep ++;
760  }
761  * psep = 0;
762  FTPFile f = FTPFS::open( dirp );
763  f.close();
764  fail = ! f.isDirectory();
765  if( fail ) {
766  client << F("550 \"") << dirp << F("\" is not directory") << eol;
767  } else {
768  log_esp3d(" Renaming %s to %s", rnfrName, path);
769  if( FTPFS::rename( rnfrName, path )) {
770  client << F("250 File successfully renamed or moved") << eol;
771  } else {
772  fail = true;
773  }
774  }
775  }
776  if( fail ) {
777  client << F("451 Rename/move failure") << eol;
778  }
779  }
780  }
781  rnfrCmd = false;
782  }
783 
784  //
785  // SYST - System
786  //
787  else if( CommandIs( "SYST" )) {
788  client << F("215 ESP3D") << eol;
789  }
790 
791 
793  // //
794  // EXTENSIONS COMMANDS (RFC 3659) //
795  // //
797 
798  //
799  // MDTM && MFMT - File Modification Time (see RFC 3659)
800  //
801  else if( CommandIs( "MDTM" ) || CommandIs( "MFMT" )) {
802  if( haveParameter()) {
803  char path[ FTP_CWD_SIZE ];
804  char * fname = parameter;
805  uint16_t year;
806  uint8_t month, day, hour, minute, second, setTime;
807  char dt[ 15 ];
808  bool mdtm = CommandIs( "MDTM" );
809 
810  setTime = getDateTime( dt, & year, & month, & day, & hour, & minute, & second );
811  // fname point to file name
812  fname += setTime;
813  if( strlen( fname ) <= 0 ) {
814  client << "501 No file name" << eol;
815  } else if( makeExistsPath( path, fname )) {
816  if( setTime ) { // set file modification time
817  if( timeStamp( path, year, month, day, hour, minute, second )) {
818  client << "213 " << dt << eol;
819  } else {
820  client << "550 Unable to modify time" << eol;
821  }
822  } else if( mdtm ) { // get file modification time
823  time_t t = 0;
824  char dtStr[ 15 ];
825  //TODO: add date time support
826  if( getFileModTime( path, t)) {
827  client << "213 " << makeDateTimeStr( dtStr, t ) << eol;
828  } else {
829  client << "550 Unable to retrieve time" << eol;
830  }
831  }
832  }
833  }
834  }
835  //
836  // SIZE - Size of the file
837  //
838  else if( CommandIs( "SIZE" )) {
839  char path[ FTP_CWD_SIZE ];
840  if( haveParameter() && makeExistsPath( path )) {
841  file = FTPFS::open( path );
842  }
843  if( ! file.isOpen()) {
844  client << F("450 Can't open ") << parameter << eol;
845  } else {
846  client << F("213 ") << file.size() << eol;
847  file.close();
848  }
849  }
850  //
851  // SITE - System command
852  //
853  else if( CommandIs( "SITE" )) {
854  if( ParameterIs( "FREE" )) {
855 #if FTP_FEATURE == FS_ROOT
856  uint8_t fs = FTPFS::getFSType(cwdName);
857  uint64_t capacity = FTPFS::totalBytes(fs);
858  uint64_t free = FTPFS::freeBytes(fs);
859 #else
860 #if FTP_FEATURE == FS_FLASH
861  size_t capacity;
862  size_t free;
863 #endif
864 #if FTP_FEATURE == FS_SD
865  uint64_t capacity;
866  uint64_t free;
867 #endif
868 
869  capacity = FTPFS::totalBytes();
870  free = FTPFS::freeBytes();
871 #endif
872  client << F("200 ") << FTPFS::formatBytes(free) << F(" free of ")
873  << FTPFS::formatBytes(capacity) << F(" capacity") << eol;
874  } else {
875  client << F("500 Unknow SITE command ") << parameter << eol;
876  }
877  }
878  //
879  // Unrecognized commands ...
880  //
881  else {
882  client << F("500 Unknow command") << eol;
883  }
884  return true;
885 }
886 
887 int FtpServer::dataConnect( bool out150 )
888 {
889  if( ! data.connected()) {
890  if( dataConn == FTP_Pasive ) {
891  uint16_t count = 1000; // wait up to a second
892  while( ! data.connected() && count -- > 0 ) {
893  if( dataServer->hasClient()) {
894  data.stop();
895  data = dataServer->available();
896  }
897  delay( 1 );
898  }
899  } else if( dataConn == FTP_Active ) {
900  data.connect( dataIp, dataPort );
901  }
902  }
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;
907  }
908 
909  return data.connected();
910 }
911 
912 bool FtpServer::dataConnected()
913 {
914  if( data.connected()) {
915  return true;
916  }
917  data.stop();
918  client << F("426 Data connection closed. Transfer aborted") << eol;
919  transferStage = FTP_Close;
920  return false;
921 }
922 
923 bool FtpServer::doRetrieve()
924 {
925  if( ! dataConnected()) {
926  file.close();
927  return false;
928  }
929  int16_t nb = file.read( buf, FTP_BUF_SIZE );
930  if( nb > 0 ) {
931  data.write( buf, nb );
932  bytesTransfered += nb;
933  return true;
934  }
935  closeTransfer();
936  return false;
937 }
938 
939 bool FtpServer::doStore()
940 {
941  int16_t na = data.available();
942  if( na == 0 ) {
943  if( data.connected()) {
944  return true;
945  } else {
946  closeTransfer();
947  return false;
948  }
949  }
950  if( na > FTP_BUF_SIZE ) {
951  na = FTP_BUF_SIZE;
952  }
953  int16_t nb = data.read((uint8_t *) buf, na );
954  int16_t rc = 0;
955  if( nb > 0 ) {
956  rc = file.write( buf, nb );
957  bytesTransfered += nb;
958  }
959  if( nb < 0 || rc == nb ) {
960  return true;
961  }
962  client << F("552 Probably insufficient storage space") << eol;
963  file.close();
964  data.stop();
965  return false;
966 }
967 
968 bool FtpServer::doList()
969 {
970  if( ! dataConnected()) {
971  dir.close();
972  return false;
973  }
974  if (dir) {
975  if (file) {
976  file.close();
977  }
978  file = dir.openNextFile();
979  if (file) {
980  time_t t = file.getLastWrite();
981  char dtStr[ 15 ];
982  data << (file.isDirectory()?"d":"-") << "rwxrwxrwx 1 " << _currentUser.c_str() << " " << _currentUser.c_str();
983  String s = String(file.size());
984  for(uint i = 0; i < SIZELISTPADING - s.length(); i++) {
985  data << " ";
986  }
987  data << file.size() << " " << makeDateTimeString(dtStr,t) << " " << file.name() << eol;
988  nbMatch ++;
989  file.close();
990  return true;
991  }
992  }
993  client << F("226 ") << nbMatch << F(" matches total") << eol;
994  dir.close();
995  data.stop();
996  return false;
997 }
998 
999 bool FtpServer::doMlsd()
1000 {
1001  if( ! dataConnected()) {
1002  dir.close();
1003  return false;
1004  }
1005  if (dir) {
1006  if(file) {
1007  file.close();
1008  }
1009  file = dir.openNextFile();
1010  if (file) {
1011  char dtStr[ 15 ];
1012  time_t t = file.getLastWrite();
1013  data << "Type=" << ( file.isDirectory() ? F("dir") : F("file")) << ";Size=" << file.size() << ";Modify=" << makeDateTimeStr( dtStr, t) << "; " << file.name() << eol;
1014  log_esp3d("%s %u %s %s", file.isDirectory() ? "dir" : "file", file.size(), makeDateTimeStr( dtStr, t), file.name());
1015  file.close();
1016  nbMatch ++;
1017  return true;
1018  }
1019  }
1020  client << F("226-options: -a -l") << eol;
1021  client << F("226 ") << nbMatch << F(" matches total") << eol;
1022  dir.close();
1023  data.stop();
1024  return false;
1025 }
1026 
1027 void FtpServer::closeTransfer()
1028 {
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;
1035  } else {
1036  client << F("226 File successfully transferred") << eol;
1037  }
1038 
1039  file.close();
1040  data.stop();
1041 }
1042 
1043 void FtpServer::abortTransfer()
1044 {
1045  if( transferStage != FTP_Close ) {
1046  file.close();
1047  dir.close();
1048  client << F("426 Transfer aborted") << eol;
1049  log_esp3d(" Transfer aborted!");
1050  transferStage = FTP_Close;
1051  }
1052 // if( data.connected())
1053  data.stop();
1054 }
1055 
1056 // Read a char from client connected to ftp server
1057 //
1058 // update cmdLine and command buffers, iCL and parameter pointers
1059 //
1060 // return:
1061 // -2 if buffer cmdLine is full
1062 // -1 if line not completed
1063 // 0 if empty line received
1064 // length of cmdLine (positive) if no empty line received
1065 
1066 int8_t FtpServer::readChar()
1067 {
1068  int8_t rc = -1;
1069 
1070  if( client.available()) {
1071  char c = client.read();
1072  log_esp3d("read %c", c);
1073  if( c == '\\' ) {
1074  c = '/';
1075  }
1076  if( c != '\r' ) {
1077  if( c != '\n' ) {
1078  if( iCL < FTP_CMD_SIZE ) {
1079  cmdLine[ iCL ++ ] = c;
1080  } else {
1081  rc = -2; // Line too long
1082  }
1083  } else {
1084  cmdLine[ iCL ] = 0;
1085  command[ 0 ] = 0;
1086  parameter = NULL;
1087  // empty line?
1088  if( iCL == 0 ) {
1089  rc = 0;
1090  } else {
1091  rc = iCL;
1092  // search for space between command and parameter
1093  parameter = strchr( cmdLine, ' ' );
1094  if( parameter != NULL ) {
1095  if( parameter - cmdLine > 4 ) {
1096  rc = -2; // Syntax error
1097  } else {
1098  strncpy( command, cmdLine, parameter - cmdLine );
1099  command[ parameter - cmdLine ] = 0;
1100  while( * ( ++ parameter ) == ' ' )
1101  ;
1102  }
1103  } else if( strlen( cmdLine ) > 4 ) {
1104  rc = -2; // Syntax error.
1105  } else {
1106  strcpy( command, cmdLine );
1107  }
1108  iCL = 0;
1109  }
1110  }
1111  }
1112  if( rc > 0 )
1113  for( uint8_t i = 0 ; i < strlen( command ); i ++ ) {
1114  command[ i ] = toupper( command[ i ] );
1115  }
1116  if( rc == -2 ) {
1117  iCL = 0;
1118  client << F("500 Syntax error") << eol;
1119  }
1120  }
1121  return rc;
1122 }
1123 
1124 bool FtpServer::haveParameter()
1125 {
1126  if( parameter != NULL && strlen( parameter ) > 0 ) {
1127  return true;
1128  }
1129  client << "501 No file name" << eol;
1130  return false;
1131 }
1132 
1133 // Make complete path/name from cwdName and param
1134 //
1135 // 3 possible cases: parameter can be absolute path, relative path or only the name
1136 //
1137 // parameter:
1138 // fullName : where to store the path/name
1139 //
1140 // return:
1141 // true, if done
1142 
1143 bool FtpServer::makePath( char * fullName, char * param )
1144 {
1145  if( param == NULL ) {
1146  param = parameter;
1147  }
1148 
1149  // Root or empty?
1150  if( strcmp( param, "/" ) == 0 || strlen( param ) == 0 ) {
1151  strcpy( fullName, "/" );
1152  return true;
1153  }
1154  // If relative path, concatenate with current dir
1155  if( param[0] != '/' ) {
1156  strcpy( fullName, cwdName );
1157  if( fullName[ strlen( fullName ) - 1 ] != '/' ) {
1158  strncat( fullName, "/", FTP_CWD_SIZE );
1159  }
1160  strncat( fullName, param, FTP_CWD_SIZE );
1161  } else {
1162  strcpy( fullName, param );
1163  }
1164  // If ends with '/', remove it
1165  uint16_t strl = strlen( fullName ) - 1;
1166  if( fullName[ strl ] == '/' && strl > 1 ) {
1167  fullName[ strl ] = 0;
1168  }
1169  if( strlen( fullName ) >= FTP_CWD_SIZE ) {
1170  client << F("500 Command line too long") << eol;
1171  return false;
1172  }
1173  for( uint8_t i = 0; i < strlen( fullName ); i ++ )
1174  if( ! legalChar( fullName[i])) {
1175  client << F("553 File name not allowed") << eol;
1176  return false;
1177  }
1178  return true;
1179 }
1180 
1181 bool FtpServer::makeExistsPath( char * path, char * param )
1182 {
1183  if( ! makePath( path, param )) {
1184  return false;
1185  }
1186  if( FTPFS::exists( path )) {
1187  return true;
1188  }
1189  client << F("550 ") << path << F(" not found.") << eol;
1190  return false;
1191 }
1192 
1193 // Calculate year, month, day, hour, minute and second
1194 // from first parameter sent by MDTM command (YYYYMMDDHHMMSS)
1195 // Accept longer parameter YYYYMMDDHHMMSSmmm where mmm are milliseconds
1196 // but don't take in account additional digits
1197 //
1198 // parameters:
1199 // dt: 15 length string for 14 digits and terminator
1200 // pyear, pmonth, pday, phour, pminute and psecond: pointer of
1201 // variables where to store data
1202 //
1203 // return:
1204 // 0 if parameter is not YYYYMMDDHHMMSS
1205 // length of parameter + space
1206 //
1207 // Date/time are expressed as a 14 digits long string
1208 // terminated by a space and followed by name of file
1209 
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 )
1212 {
1213  uint8_t i;
1214  dt[ 0 ] = 0;
1215  if( strlen( parameter ) < 15 ) { //|| parameter[ 14 ] != ' ' )
1216  return 0;
1217  }
1218  for( i = 0; i < 14; i ++ )
1219  if( ! isdigit( parameter[ i ])) {
1220  return 0;
1221  }
1222  for( i = 14; i < 18; i ++ )
1223  if( parameter[ i ] == ' ' ) {
1224  break;
1225  } else if( ! isdigit( parameter[ i ])) {
1226  return 0;
1227  }
1228  if( i == 18 ) {
1229  return 0;
1230  }
1231  i ++ ;
1232 
1233  strncpy( dt, parameter, 14 );
1234  dt[ 14 ] = 0;
1235  * psecond = atoi( dt + 12 );
1236  dt[ 12 ] = 0;
1237  * pminute = atoi( dt + 10 );
1238  dt[ 10 ] = 0;
1239  * phour = atoi( dt + 8 );
1240  dt[ 8 ] = 0;
1241  * pday = atoi( dt + 6 );
1242  dt[ 6 ] = 0 ;
1243  * pmonth = atoi( dt + 4 );
1244  dt[ 4 ] = 0 ;
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 ));
1248  return i;
1249 }
1250 
1251 // Create string YYYYMMDDHHMMSS from time_t
1252 //
1253 // parameters:
1254 // time_t
1255 // tstr: where to store the string. Must be at least 15 characters long
1256 //
1257 // return:
1258 // pointer to tstr
1259 
1260 char * FtpServer::makeDateTimeStr( char * tstr, time_t timefile )
1261 {
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);
1264  return tstr;
1265 }
1266 
1267 // Create string MMM DD YYYY or MMM DD HH:MM from time_t
1268 //
1269 // parameters:
1270 // time_t
1271 // tstr: where to store the string. Must be at least 13 characters long
1272 //
1273 // return:
1274 // pointer to tstr
1275 
1276 char * FtpServer::makeDateTimeString( char * tstr, time_t timefile )
1277 {
1278  struct tm * tmstruct = localtime(&timefile);
1279  time_t now;
1280  time(&now);
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);
1285  } else {
1286  strftime (tstr, 13, "%b %d %Y", tmstruct);
1287  }
1288  return tstr;
1289 }
1290 
1291 
1292 bool FtpServer::getFileModTime(const char * path, time_t & t)
1293 {
1294  FTPFile f = FTPFS::open(path);
1295  if (f) {
1296  t = f.getLastWrite();
1297  f.close();
1298  return true;
1299  }
1300  log_esp3d("Cannot get getLastWrite");
1301  t = 0;
1302  return false;
1303 }
1304 //TODO
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 )
1306 {
1307  //Not available yet
1308  return false;
1309 }
1310 
1311 #endif //FTP_FEATURE
ESP_FTP_DATA_ACTIVE_PORT
#define ESP_FTP_DATA_ACTIVE_PORT
Definition: settings_esp3d.h:92
FTP_Pasive
@ FTP_Pasive
Definition: FtpServer.h:69
ESP_GBFS::formatBytes
static String & formatBytes(uint64_t bytes)
Definition: esp_globalFS.cpp:57
FtpServer::handle
void handle()
Definition: FtpServer.cpp:219
ESP_GBFile::getLastWrite
time_t getLastWrite()
Definition: esp_globalFS.cpp:525
ftpCmd
ftpCmd
Definition: FtpServer.h:52
FTP_Nlst
@ FTP_Nlst
Definition: FtpServer.h:64
ESP_GBFS::open
static ESP_GBFile open(const char *path, uint8_t mode=ESP_FILE_READ)
Definition: esp_globalFS.cpp:304
ESP_GBFile::isDirectory
bool isDirectory()
Definition: esp_globalFS.cpp:485
FtpServer::started
bool started()
Definition: FtpServer.cpp:170
ESP_GBFS::mkdir
static bool mkdir(const char *path)
Definition: esp_globalFS.cpp:251
SIZELISTPADING
#define SIZELISTPADING
Definition: FtpServer.cpp:101
FTP_Client
@ FTP_Client
Definition: FtpServer.h:54
FtpServer::closeClient
void closeClient()
Definition: FtpServer.cpp:118
ESP_FILE_WRITE
#define ESP_FILE_WRITE
Definition: defines.h:121
ESP_GBFile::openNextFile
ESP_GBFile openNextFile()
Definition: esp_globalFS.cpp:711
FTP_Store
@ FTP_Store
Definition: FtpServer.h:62
file
FTPFile file
Definition: FtpServer.cpp:104
FTP_BUF_SIZE
#define FTP_BUF_SIZE
Definition: FtpServer.h:44
ESP_SDFile
Definition: esp_sd.h:31
ESP_FILE_APPEND
#define ESP_FILE_APPEND
Definition: defines.h:122
legalChar
bool legalChar(char c)
Definition: FtpServer.cpp:108
ExtStreaming.h
FtpServer::~FtpServer
~FtpServer()
Definition: FtpServer.cpp:138
FTP_Close
@ FTP_Close
Definition: FtpServer.h:60
ftpTransfer
ftpTransfer
Definition: FtpServer.h:60
ESP_GBFile::close
void close()
Definition: esp_globalFS.cpp:390
ESP_GBFS::remove
static bool remove(const char *path)
Definition: esp_globalFS.cpp:209
ESP_GBFS::exists
static bool exists(const char *path)
Definition: esp_globalFS.cpp:187
FTP_Cmd
@ FTP_Cmd
Definition: FtpServer.h:57
ParameterIs
#define ParameterIs(a)
Definition: FtpServer.h:49
FtpServer::isUser
bool isUser(const char *user)
Definition: FtpServer.cpp:309
ESP_SD
Definition: esp_sd.h:65
ESP_GBFile::write
size_t write(uint8_t i)
Definition: esp_globalFS.cpp:556
FTP_FIL_SIZE
#define FTP_FIL_SIZE
Definition: FtpServer.h:43
FTP_TIME_OUT
#define FTP_TIME_OUT
Definition: FtpServer.h:39
ESP_GBFS::rmdir
static bool rmdir(const char *path)
Definition: esp_globalFS.cpp:272
FtpServer::FtpServer
FtpServer()
Definition: FtpServer.cpp:128
DEFAULT_USER_LOGIN
const char DEFAULT_USER_LOGIN[]
Definition: authentication_service.h:32
FW_VERSION
#define FW_VERSION
Definition: version.h:25
FTPFile
ESP_GBFile FTPFile
Definition: FtpServer.cpp:81
ESP_GBFile::name
const char * name() const
Definition: esp_globalFS.cpp:429
FtpServer::begin
bool begin()
Definition: FtpServer.cpp:175
ESP_FTP_ON
#define ESP_FTP_ON
Definition: settings_esp3d.h:94
ESP_GBFile
Definition: esp_globalFS.h:34
FTP_Stop
@ FTP_Stop
Definition: FtpServer.h:52
FTP_AUTH_TIME_OUT
#define FTP_AUTH_TIME_OUT
Definition: FtpServer.h:40
ESP_File
Definition: esp_filesystem.h:30
ESP_FileSystem
Definition: esp_filesystem.h:64
ESP_FTP_CTRL_PORT
#define ESP_FTP_CTRL_PORT
Definition: settings_esp3d.h:91
FS_ROOT
#define FS_ROOT
Definition: defines.h:124
FtpServer::isConnected
bool isConnected()
Definition: FtpServer.cpp:123
eol
@ eol
Definition: ExtStreaming.h:119
ESP_GBFS::freeBytes
static uint64_t freeBytes(uint8_t FS)
Definition: esp_globalFS.cpp:103
DEFAULT_ADMIN_LOGIN
const char DEFAULT_ADMIN_LOGIN[]
Definition: authentication_service.h:31
ESP_GBFS::getFSType
static uint8_t getFSType(const char *path)
Definition: esp_globalFS.cpp:136
FTP_Active
@ FTP_Active
Definition: FtpServer.h:70
FTP_CWD_SIZE
#define FTP_CWD_SIZE
Definition: FtpServer.h:42
log_esp3d
#define log_esp3d(format,...)
Definition: debug_esp3d.h:29
FtpServer
Definition: FtpServer.h:73
FTP_CMD_SIZE
#define FTP_CMD_SIZE
Definition: FtpServer.h:41
Settings_ESP3D::read_uint32
static uint32_t read_uint32(int pos, bool *haserror=NULL)
Definition: settings_esp3d.cpp:919
_HEX
#define _HEX(a)
Definition: ExtStreaming.h:70
FTP_Pass
@ FTP_Pass
Definition: FtpServer.h:56
ESP_GBFS::totalBytes
static uint64_t totalBytes(uint8_t FS)
Definition: esp_globalFS.cpp:73
FTP_List
@ FTP_List
Definition: FtpServer.h:63
ESP_GBFS::rename
static bool rename(const char *oldpath, const char *newpath)
Definition: esp_globalFS.cpp:230
FTP_NoConn
@ FTP_NoConn
Definition: FtpServer.h:68
ESP_GBFile::read
int read()
Definition: esp_globalFS.cpp:596
ESP_GBFile::isOpen
bool isOpen()
Definition: esp_globalFS.cpp:409
FtpServer::end
void end()
Definition: FtpServer.cpp:143
FTP_Retrieve
@ FTP_Retrieve
Definition: FtpServer.h:61
FTP_User
@ FTP_User
Definition: FtpServer.h:55
Settings_ESP3D::read_byte
static uint8_t read_byte(int pos, bool *haserror=NULL)
Definition: settings_esp3d.cpp:715
FTPFS
ESP_GBFS FTPFS
Definition: FtpServer.cpp:82
ESP_GBFile::size
size_t size()
Definition: esp_globalFS.cpp:505
dir
FTPFile dir
Definition: FtpServer.cpp:103
FtpServer::isPassword
bool isPassword(const char *password)
Definition: FtpServer.cpp:326
ESP_FTP_DATA_PASSIVE_PORT
#define ESP_FTP_DATA_PASSIVE_PORT
Definition: settings_esp3d.h:93
ftp_server
FtpServer ftp_server
Definition: FtpServer.cpp:106
FtpServer::clientIPAddress
const char * clientIPAddress()
Definition: FtpServer.cpp:160
FtpServer.h
NetConfig::localIP
static String localIP()
Definition: netconfig.cpp:119
FTP_Init
@ FTP_Init
Definition: FtpServer.h:53
CommandIs
#define CommandIs(a)
Definition: FtpServer.h:48
FTP_Mlsd
@ FTP_Mlsd
Definition: FtpServer.h:65
ESP_GBFS
Definition: esp_globalFS.h:80