ftp_server_commands.c
Go to the documentation of this file.
1 /**
2  * @file ftp_server_commands.c
3  * @brief FTP server (command processing)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL FTP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "ipv4/ipv4_misc.h"
37 #include "ftp/ftp_server.h"
39 #include "ftp/ftp_server_data.h"
40 #include "ftp/ftp_server_misc.h"
41 #include "str.h"
42 #include "path.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (FTP_SERVER_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief FTP command processing
51  * @param[in] connection Pointer to the client connection
52  **/
53 
55 {
56  size_t n;
57  char_t *p;
58 
59  //The CRLF sequence should be used to terminate the command line
60  for(n = 0; n < connection->commandLen; n++)
61  {
62  if(connection->command[n] == '\n')
63  break;
64  }
65 
66  //Any command to process?
67  if(n < connection->commandLen)
68  {
69  //Properly terminate the string with a NULL character
70  connection->command[n] = '\0';
71  //Remove trailing whitespace from the command line
72  strRemoveTrailingSpace(connection->command);
73 
74  //Debug message
75  TRACE_DEBUG("FTP client: %s\r\n", connection->command);
76 
77  //Command line too long?
78  if(connection->controlChannel.state == FTP_CHANNEL_STATE_DISCARD)
79  {
80  //Switch to idle state
81  connection->controlChannel.state = FTP_CHANNEL_STATE_IDLE;
82  //Format response message
83  osStrcpy(connection->response, "500 Command line too long\r\n");
84  }
85  else
86  {
87  //The command name and the arguments are separated by one or more
88  //spaces
89  for(p = connection->command; *p != '\0' && *p != ' '; p++)
90  {
91  }
92 
93  //Space character found?
94  if(*p == ' ')
95  {
96  //Split the string at the first occurrence of the space character
97  *(p++) = '\0';
98 
99  //Skip extra whitespace
100  while(*p == ' ')
101  {
102  p++;
103  }
104  }
105 
106  //NOOP command received?
107  if(!osStrcasecmp(connection->command, "NOOP"))
108  {
109  ftpServerProcessNoop(connection, p);
110  }
111  //SYST command received?
112  else if(!osStrcasecmp(connection->command, "SYST"))
113  {
114  ftpServerProcessSyst(connection, p);
115  }
116  //FEAT command received?
117  else if(!osStrcasecmp(connection->command, "FEAT"))
118  {
119  ftpServerProcessFeat(connection, p);
120  }
121 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
122  //AUTH command received?
123  else if(!osStrcasecmp(connection->command, "AUTH"))
124  {
125  ftpServerProcessAuth(connection, p);
126  }
127  //PBSZ command received?
128  else if(!osStrcasecmp(connection->command, "PBSZ"))
129  {
130  ftpServerProcessPbsz(connection, p);
131  }
132  //PROT command received?
133  else if(!osStrcasecmp(connection->command, "PROT"))
134  {
135  ftpServerProcessProt(connection, p);
136  }
137 #endif
138  //TYPE command received?
139  else if(!osStrcasecmp(connection->command, "TYPE"))
140  {
141  ftpServerProcessType(connection, p);
142  }
143  //STRU command received?
144  else if(!osStrcasecmp(connection->command, "STRU"))
145  {
146  ftpServerProcessStru(connection, p);
147  }
148  //MODE command received?
149  else if(!osStrcasecmp(connection->command, "MODE"))
150  {
151  ftpServerProcessMode(connection, p);
152  }
153  //USER command received?
154  else if(!osStrcasecmp(connection->command, "USER"))
155  {
156  ftpServerProcessUser(connection, p);
157  }
158  //PASS command received?
159  else if(!osStrcasecmp(connection->command, "PASS"))
160  {
161  ftpServerProcessPass(connection, p);
162  }
163  //REIN command received?
164  else if(!osStrcasecmp(connection->command, "REIN"))
165  {
166  ftpServerProcessRein(connection, p);
167  }
168  //QUIT command received?
169  else if(!osStrcasecmp(connection->command, "QUIT"))
170  {
171  ftpServerProcessQuit(connection, p);
172  }
173  //PORT command received?
174  else if(!osStrcasecmp(connection->command, "PORT"))
175  {
176  ftpServerProcessPort(connection, p);
177  }
178  //EPRT command received?
179  else if(!osStrcasecmp(connection->command, "EPRT"))
180  {
181  ftpServerProcessEprt(connection, p);
182  }
183  //PASV command received?
184  else if(!osStrcasecmp(connection->command, "PASV"))
185  {
186  ftpServerProcessPasv(connection, p);
187  }
188  //EPSV command received?
189  else if(!osStrcasecmp(connection->command, "EPSV"))
190  {
191  ftpServerProcessEpsv(connection, p);
192  }
193  //ABOR command received?
194  else if(!osStrcasecmp(connection->command, "ABOR"))
195  {
196  ftpServerProcessAbor(connection, p);
197  }
198  //PWD command received?
199  else if(!osStrcasecmp(connection->command, "PWD"))
200  {
201  ftpServerProcessPwd(connection, p);
202  }
203  //LIST command received?
204  else if(!osStrcasecmp(connection->command, "LIST"))
205  {
206  ftpServerProcessList(connection, p);
207  }
208  //NLST command received?
209  else if(!osStrcasecmp(connection->command, "NLST"))
210  {
211  ftpServerProcessNlst(connection, p);
212  }
213  //CWD command received?
214  else if(!osStrcasecmp(connection->command, "CWD"))
215  {
216  ftpServerProcessCwd(connection, p);
217  }
218  //CDUP command received?
219  else if(!osStrcasecmp(connection->command, "CDUP"))
220  {
221  ftpServerProcessCdup(connection, p);
222  }
223  //MKD command received?
224  else if(!osStrcasecmp(connection->command, "MKD"))
225  {
226  ftpServerProcessMkd(connection, p);
227  }
228  //RMD command received?
229  else if(!osStrcasecmp(connection->command, "RMD"))
230  {
231  ftpServerProcessRmd(connection, p);
232  }
233  //SIZE command received?
234  else if(!osStrcasecmp(connection->command, "SIZE"))
235  {
236  ftpServerProcessSize(connection, p);
237  }
238  //RETR command received?
239  else if(!osStrcasecmp(connection->command, "RETR"))
240  {
241  ftpServerProcessRetr(connection, p);
242  }
243  //STOR command received?
244  else if(!osStrcasecmp(connection->command, "STOR"))
245  {
246  ftpServerProcessStor(connection, p);
247  }
248  //APPE command received?
249  else if(!osStrcasecmp(connection->command, "APPE"))
250  {
251  ftpServerProcessAppe(connection, p);
252  }
253  //RNFR command received?
254  else if(!osStrcasecmp(connection->command, "RNFR"))
255  {
256  ftpServerProcessRnfr(connection, p);
257  }
258  //RNTO command received?
259  else if(!osStrcasecmp(connection->command, "RNTO"))
260  {
261  ftpServerProcessRnto(connection, p);
262  }
263  //DELE command received?
264  else if(!osStrcasecmp(connection->command, "DELE"))
265  {
266  ftpServerProcessDele(connection, p);
267  }
268  //Unknown command received?
269  else
270  {
271  ftpServerProcessUnknownCmd(connection, p);
272  }
273  }
274 
275  //Debug message
276  TRACE_DEBUG("FTP server: %s", connection->response);
277 
278  //Number of bytes in the response buffer
279  connection->responseLen = osStrlen(connection->response);
280  connection->responsePos = 0;
281  }
282  else if(connection->commandLen >= FTP_SERVER_MAX_LINE_LEN)
283  {
284  //Drop incoming data
285  connection->controlChannel.state = FTP_CHANNEL_STATE_DISCARD;
286  }
287 
288  //Flush command line
289  connection->commandLen = 0;
290 }
291 
292 
293 /**
294  * @brief NOOP command processing
295  *
296  * The NOOP command does not affect any parameters or previously entered
297  * commands. It specifies no action other than that the server send an OK reply
298  *
299  * @param[in] connection Pointer to the client connection
300  * @param[in] param Command line parameters
301  **/
302 
304 {
305  //Send an OK reply
306  osStrcpy(connection->response, "200 Command okay\r\n");
307 }
308 
309 
310 /**
311  * @brief SYST command processing
312  *
313  * The SYST command is used to find out the type of operating system
314  * at the server side
315  *
316  * @param[in] connection Pointer to the client connection
317  * @param[in] param Command line parameters
318  **/
319 
321 {
322  //Format the response to the SYST command
323  osStrcpy(connection->response, "215 UNIX Type: L8\r\n");
324 }
325 
326 
327 /**
328  * @brief FEAT command processing
329  *
330  * The FEAT command allows a client to discover which optional
331  * commands a server supports
332  *
333  * @param[in] connection Pointer to the client connection
334  * @param[in] param Command line parameters
335  **/
336 
338 {
339 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
340  FtpServerContext *context;
341 
342  //Point to the FTP server context
343  context = connection->context;
344 #endif
345 
346  //Format the response to the FEAT command
347  osStrcpy(connection->response, "211-Extensions supported:\r\n");
348 
349  //Each extension supported must be listed on a separate line
350  osStrcat(connection->response, " SIZE\r\n");
351  osStrcat(connection->response, " EPRT\r\n");
352  osStrcat(connection->response, " EPSV\r\n");
353 
354 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
355  //TLS security mode supported by the server?
356  if((context->settings.mode & FTP_SERVER_MODE_IMPLICIT_TLS) != 0 ||
357  (context->settings.mode & FTP_SERVER_MODE_EXPLICIT_TLS) != 0)
358  {
359  //If a server supports the FEAT command, then it must advertise
360  //supported AUTH, PBSZ, and PROT commands in the reply (refer to
361  //RFC 4217, section 6)
362  osStrcat(connection->response, " AUTH TLS\r\n");
363  osStrcat(connection->response, " PBSZ\r\n");
364  osStrcat(connection->response, " PROT\r\n");
365  }
366 #endif
367 
368  //Terminate feature listing
369  osStrcat(connection->response, "211 End\r\n");
370 }
371 
372 
373 /**
374  * @brief AUTH command processing
375  *
376  * The AUTH command specifies the security mechanism
377  *
378  * @param[in] connection Pointer to the client connection
379  * @param[in] param Command line parameters
380  **/
381 
383 {
384 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
385  FtpServerContext *context;
386 
387  //Point to the FTP server context
388  context = connection->context;
389 
390  //TLS security mode supported by the server?
391  if((context->settings.mode & FTP_SERVER_MODE_IMPLICIT_TLS) != 0)
392  {
393  //When using implicit FTPS, a TLS connection is immediately established
394  //via port 990 before any command is exchanged
395  osStrcpy(connection->response, "534 Secure connection already negotiated\r\n");
396  }
397  else if((context->settings.mode & FTP_SERVER_MODE_EXPLICIT_TLS) != 0)
398  {
399  //The argument specifies the security mechanism
400  if(*param != '\0')
401  {
402  //TLS security mechanism?
403  if(!osStrcasecmp(param, "TLS"))
404  {
405  //If the server is willing to accept the named security mechanism,
406  //and does not require any security data, it must respond with reply
407  //code 234
408  osStrcpy(connection->response, "234 AUTH TLS OK\r\n");
409 
410  //Establish a protected session
411  connection->controlChannel.state = FTP_CHANNEL_STATE_AUTH_TLS_1;
412  }
413  else
414  {
415  //The security mechanism is unknown
416  osStrcpy(connection->response, "504 Unknown security scheme\r\n");
417  }
418  }
419  else
420  {
421  //The argument is missing
422  osStrcpy(connection->response, "501 Missing parameter\r\n");
423  }
424  }
425  else
426 #endif
427  {
428  //TLS security mode is not supported
429  osStrcpy(connection->response, "502 Command not implemented\r\n");
430  }
431 }
432 
433 
434 /**
435  * @brief PBSZ command processing
436  *
437  * The PBSZ command specifies the maximum size, in bytes, of the encoded data
438  * blocks to be sent or received during file transfer
439  *
440  * @param[in] connection Pointer to the client connection
441  * @param[in] param Command line parameters
442  **/
443 
445 {
446 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
447  FtpServerContext *context;
448 
449  //Point to the FTP server context
450  context = connection->context;
451 
452  //TLS security mode supported by the server?
453  if((context->settings.mode & FTP_SERVER_MODE_IMPLICIT_TLS) != 0 ||
454  (context->settings.mode & FTP_SERVER_MODE_EXPLICIT_TLS) != 0)
455  {
456  //The argument specifies the maximum size of the encoded data blocks
457  if(*param != '\0')
458  {
459  //Format the response to the PBSZ command
460  osStrcpy(connection->response, "200 PBSZ=0\r\n");
461  }
462  else
463  {
464  //The argument is missing
465  osStrcpy(connection->response, "501 Missing parameter\r\n");
466  }
467  }
468  else
469 #endif
470  {
471  //TLS security mode is not supported
472  osStrcpy(connection->response, "502 Command not implemented\r\n");
473  }
474 }
475 
476 
477 /**
478  * @brief PROT command processing
479  *
480  * The PROT command specifies the data channel protection level
481  *
482  * @param[in] connection Pointer to the client connection
483  * @param[in] param Command line parameters
484  **/
485 
487 {
488 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
489  FtpServerContext *context;
490 
491  //Point to the FTP server context
492  context = connection->context;
493 
494  //TLS security mode supported by the server?
495  if((context->settings.mode & FTP_SERVER_MODE_IMPLICIT_TLS) != 0 ||
496  (context->settings.mode & FTP_SERVER_MODE_EXPLICIT_TLS) != 0)
497  {
498  //The argument specifies the data protection level
499  if(*param != '\0')
500  {
501  //Private protection level?
502  if(!osStrcasecmp(param, "P"))
503  {
504  //The server must reply with a 200 reply code to indicate that the
505  //specified protection level is accepted
506  osStrcpy(connection->response, "200 Data protection level set to P\r\n");
507  }
508  //Unknown security mechanism?
509  else
510  {
511  //If the server does not understand the specified protection level,
512  //it should respond with reply code 504
513  osStrcpy(connection->response, "504 Unknown protection level\r\n");
514  }
515  }
516  else
517  {
518  //The argument is missing
519  osStrcpy(connection->response, "501 Missing parameter\r\n");
520  }
521  }
522  else
523 #endif
524  {
525  //TLS security mode is not supported
526  osStrcpy(connection->response, "502 Command not implemented\r\n");
527  }
528 }
529 
530 
531 /**
532  * @brief TYPE command processing
533  *
534  * The TYPE command specifies the representation type
535  *
536  * @param[in] connection Pointer to the client connection
537  * @param[in] param Command line parameters
538  **/
539 
541 {
542  //The argument specifies the representation type
543  if(*param != '\0')
544  {
545  //ASCII type?
546  if(!osStrcasecmp(param, "A"))
547  {
548  //Format the response to the TYPE command
549  osStrcpy(connection->response, "200 Type set to A\r\n");
550  }
551  //Image type?
552  else if(!osStrcasecmp(param, "I"))
553  {
554  //Format the response to the TYPE command
555  osStrcpy(connection->response, "200 Type set to I\r\n");
556  }
557  //Unknown type?
558  else
559  {
560  //Report an error
561  osStrcpy(connection->response, "504 Unknown type\r\n");
562  }
563  }
564  else
565  {
566  //The argument is missing
567  osStrcpy(connection->response, "501 Missing parameter\r\n");
568  }
569 }
570 
571 
572 /**
573  * @brief STRU command processing
574  *
575  * The STRU command specifies the file structure
576  *
577  * @param[in] connection Pointer to the client connection
578  * @param[in] param Command line parameters
579  **/
580 
582 {
583  //The argument specifies the file structure
584  if(*param != '\0')
585  {
586  //No record structure?
587  if(!osStrcasecmp(param, "F"))
588  {
589  //Format the response to the STRU command
590  osStrcpy(connection->response, "200 Structure set to F\r\n");
591  }
592  //Unknown file structure?
593  else
594  {
595  //Report an error
596  osStrcpy(connection->response, "504 Unknown structure\r\n");
597  }
598  }
599  else
600  {
601  //The argument is missing
602  osStrcpy(connection->response, "501 Missing parameter\r\n");
603  }
604 }
605 
606 
607 /**
608  * @brief MODE command processing
609  *
610  * The MODE command specifies the data transfer mode
611  *
612  * @param[in] connection Pointer to the client connection
613  * @param[in] param Command line parameters
614  **/
615 
617 {
618  //The argument specifies the data transfer mode
619  if(*param != '\0')
620  {
621  //Stream mode?
622  if(!osStrcasecmp(param, "S"))
623  {
624  //Format the response to the MODE command
625  osStrcpy(connection->response, "200 Mode set to S\r\n");
626  }
627  //Unknown data transfer mode?
628  else
629  {
630  //Report an error
631  osStrcpy(connection->response, "504 Unknown mode\r\n");
632  }
633  }
634  else
635  {
636  //The argument is missing
637  osStrcpy(connection->response, "501 Missing parameter\r\n");
638  }
639 }
640 
641 
642 /**
643  * @brief USER command processing
644  *
645  * The USER command is used to identify the user
646  *
647  * @param[in] connection Pointer to the client connection
648  * @param[in] param Command line parameters
649  **/
650 
652 {
653  uint_t status;
654  FtpServerContext *context;
655 
656  //Point to the FTP server context
657  context = connection->context;
658 
659  //The argument specifies the user name
660  if(*param == '\0')
661  {
662  //The argument is missing
663  osStrcpy(connection->response, "501 Missing parameter\r\n");
664  //Exit immediately
665  return;
666  }
667 
668  //Check the length of the user name
670  {
671  //The specified user name is not valid...
672  osStrcpy(connection->response, "501 Invalid parameter\r\n");
673  //Exit immediately
674  return;
675  }
676 
677 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
678  //Cleartext FTP session?
679  if(connection->controlChannel.tlsContext == NULL)
680  {
681  //If the server needs AUTH, then it refuses to accept certain commands
682  //until it gets a successfully protected session (refer to RFC 4217,
683  //section 11.1)
684  if((context->settings.mode & FTP_SERVER_MODE_PLAINTEXT) == 0)
685  {
686  //Format response message
687  osStrcpy(connection->response, "421 Cleartext sessions are not accepted\r\n");
688  //Exit immediately
689  return;
690  }
691  }
692 #endif
693 
694  //Save user name
695  osStrcpy(connection->user, param);
696  //Log out the user
697  connection->userLoggedIn = FALSE;
698 
699  //Set home directory
700  pathCopy(connection->homeDir, context->settings.rootDir,
702 
703  //Set current directory
704  pathCopy(connection->currentDir, context->settings.rootDir,
706 
707  //Invoke user-defined callback, if any
708  if(context->settings.checkUserCallback != NULL)
709  {
710  status = context->settings.checkUserCallback(connection, param);
711  }
712  else
713  {
714  status = FTP_ACCESS_ALLOWED;
715  }
716 
717  //Access allowed?
718  if(status == FTP_ACCESS_ALLOWED)
719  {
720  //The user is now logged in
721  connection->userLoggedIn = TRUE;
722  //Format response message
723  osStrcpy(connection->response, "230 User logged in, proceed\r\n");
724  }
725  //Password required?
726  else if(status == FTP_PASSWORD_REQUIRED)
727  {
728  //This command must be immediately followed by a PASS command
729  connection->controlChannel.state = FTP_CHANNEL_STATE_USER;
730  //Format response message
731  osStrcpy(connection->response, "331 User name okay, need password\r\n");
732  }
733  //Access denied?
734  else
735  {
736  //Format response message
737  osStrcpy(connection->response, "530 Login authentication failed\r\n");
738  }
739 }
740 
741 
742 /**
743  * @brief PASS command processing
744  *
745  * The USER command specifies the user's password
746  *
747  * @param[in] connection Pointer to the client connection
748  * @param[in] param Command line parameters
749  **/
750 
752 {
753  uint_t status;
754  FtpServerContext *context;
755 
756  //Point to the FTP server context
757  context = connection->context;
758 
759  //This command must immediately follow a USER command
760  if(connection->controlChannel.state != FTP_CHANNEL_STATE_USER)
761  {
762  //Switch to idle state
763  connection->controlChannel.state = FTP_CHANNEL_STATE_IDLE;
764  //Report an error
765  osStrcpy(connection->response, "503 Bad sequence of commands\r\n");
766  //Exit immediately
767  return;
768  }
769 
770  //Switch to idle state
771  connection->controlChannel.state = FTP_CHANNEL_STATE_IDLE;
772 
773  //The argument specifies the password
774  if(*param == '\0')
775  {
776  //The argument is missing
777  osStrcpy(connection->response, "501 Missing parameter\r\n");
778  //Exit immediately
779  return;
780  }
781 
782  //Invoke user-defined callback, if any
783  if(context->settings.checkPasswordCallback != NULL)
784  {
785  status = context->settings.checkPasswordCallback(connection,
786  connection->user, param);
787  }
788  else
789  {
790  status = FTP_ACCESS_ALLOWED;
791  }
792 
793  //Access allowed?
794  if(status == FTP_ACCESS_ALLOWED)
795  {
796  //The user is now logged in
797  connection->userLoggedIn = TRUE;
798  //Format response message
799  osStrcpy(connection->response, "230 User logged in, proceed\r\n");
800  }
801  //Access denied?
802  else
803  {
804  //Format response message
805  osStrcpy(connection->response, "530 Login authentication failed\r\n");
806  }
807 }
808 
809 
810 /**
811  * @brief REIN command processing
812  *
813  * The REIN command is used to reinitialize a user session
814  *
815  * @param[in] connection Pointer to the client connection
816  * @param[in] param Command line parameters
817  **/
818 
820 {
821  //Close data connection
822  ftpServerCloseDataChannel(connection);
823 
824  //Release previously allocated resources
825  if(connection->file != NULL)
826  {
827  fsCloseFile(connection->file);
828  connection->file = NULL;
829  }
830 
831  if(connection->dir != NULL)
832  {
833  fsCloseDir(connection->dir);
834  connection->dir = NULL;
835  }
836 
837  //Clear account information
838  connection->userLoggedIn = FALSE;
839 
840  //Format response message
841  osStrcpy(connection->response, "220 Service ready for new user\r\n");
842 }
843 
844 
845 /**
846  * @brief QUIT command processing
847  *
848  * The QUIT command is used to terminate a user session
849  *
850  * @param[in] connection Pointer to the client connection
851  * @param[in] param Command line parameters
852  **/
853 
855 {
856  //There are two cases to consider upon receipt of this command
857  if(connection->dataChannel.state == FTP_CHANNEL_STATE_CLOSED)
858  {
859  //If the FTP service command was already completed, the server closes
860  //the data connection (if it is open)...
861  ftpServerCloseDataChannel(connection);
862 
863  //...and responds with a 221 reply
864  osStrcpy(connection->response, "221 Service closing control connection\r\n");
865  }
866  else
867  {
868  //If the FTP service command is still in progress, the server aborts
869  //the FTP service in progress and closes the data connection...
870  ftpServerCloseDataChannel(connection);
871 
872  //...returning a 426 reply to indicate that the service request
873  //terminated abnormally
874  osStrcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
875 
876  //The server then sends a 221 reply
877  osStrcat(connection->response, "221 Service closing control connection\r\n");
878  }
879 
880  //Release previously allocated resources
881  if(connection->file != NULL)
882  {
883  fsCloseFile(connection->file);
884  connection->file = NULL;
885  }
886 
887  if(connection->dir != NULL)
888  {
889  fsCloseDir(connection->dir);
890  connection->dir = NULL;
891  }
892 
893  //Clear account information
894  connection->userLoggedIn = FALSE;
895  //Gracefully disconnect from the remote host
896  connection->controlChannel.state = FTP_CHANNEL_STATE_WAIT_ACK;
897 }
898 
899 
900 /**
901  * @brief PORT command processing
902  *
903  * The PORT command specifies the data port to be used for the data connection
904  *
905  * @param[in] connection Pointer to the client connection
906  * @param[in] param Command line parameters
907  **/
908 
910 {
911  error_t error;
912  size_t i;
913  size_t j;
914  char_t *p;
915  char_t *token;
916  char_t *end;
917 
918  //Ensure the user is logged in
919  if(!connection->userLoggedIn)
920  {
921  //Format response message
922  osStrcpy(connection->response, "530 Not logged in\r\n");
923  //Exit immediately
924  return;
925  }
926 
927  //The argument is the concatenation of the IP address and the 16-bit
928  //port number
929  if(*param == '\0')
930  {
931  //The argument is missing
932  osStrcpy(connection->response, "501 Missing parameter\r\n");
933  //Exit immediately
934  return;
935  }
936 
937  //Close the data connection, if any
938  ftpServerCloseDataChannel(connection);
939 
940  //Start of exception handling block
941  do
942  {
943  //Assume an error condition...
944  error = ERROR_INVALID_SYNTAX;
945 
946  //Parse the string
947  for(i = 0, j = 1; param[i] != '\0'; i++)
948  {
949  //Change commas to dots
950  if(param[i] == ',' && j < sizeof(Ipv4Addr))
951  {
952  param[i] = '.';
953  j++;
954  }
955  }
956 
957  //Get the IP address to be used
958  token = osStrtok_r(param, ",", &p);
959  //Syntax error?
960  if(token == NULL)
961  break;
962 
963  //Convert the dot-decimal string to a binary IP address
964  error = ipStringToAddr(token, &connection->remoteIpAddr);
965  //Invalid IP address?
966  if(error)
967  break;
968 
969  //Assume an error condition...
970  error = ERROR_INVALID_SYNTAX;
971 
972  //Get the most significant byte of the port number
973  token = osStrtok_r(NULL, ",", &p);
974  //Syntax error?
975  if(token == NULL)
976  break;
977 
978  //Convert the string representation to integer
979  connection->remotePort = (uint16_t) osStrtoul(token, &end, 10) << 8;
980  //Syntax error?
981  if(*end != '\0')
982  break;
983 
984  //Get the least significant byte of the port number
985  token = osStrtok_r(NULL, ",", &p);
986  //Syntax error?
987  if(token == NULL)
988  break;
989 
990  //Convert the string representation to integer
991  connection->remotePort |= osStrtoul(token, &end, 10) & 0xFF;
992  //Syntax error?
993  if(*end != '\0')
994  break;
995 
996  //Successful processing
997  error = NO_ERROR;
998 
999  //End of exception handling block
1000  } while(0);
1001 
1002  //Any error to report?
1003  if(error)
1004  {
1005  //Re initialize data connection
1006  connection->passiveMode = FALSE;
1007  connection->remotePort = 0;
1008 
1009  //Format response message
1010  osStrcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
1011  //Exit immediately
1012  return;
1013  }
1014 
1015  //Successful processing
1016  osStrcpy(connection->response, "200 Command okay\r\n");
1017 }
1018 
1019 
1020 /**
1021  * @brief EPRT command processing
1022  *
1023  * The EPRT command allows for the specification of an extended address
1024  * for the data connection
1025  *
1026  * @param[in] connection Pointer to the client connection
1027  * @param[in] param Command line parameters
1028  **/
1029 
1031 {
1032  error_t error;
1033  uint_t protocol;
1034  char_t *p;
1035  char_t *token;
1036  char_t *end;
1037  char_t delimiter[2];
1038 
1039  //Ensure the user is logged in
1040  if(!connection->userLoggedIn)
1041  {
1042  //Format response message
1043  osStrcpy(connection->response, "530 Not logged in\r\n");
1044  //Exit immediately
1045  return;
1046  }
1047 
1048  //The extended address must consist of the network protocol
1049  //as well as the IP address and the 16-bit port number
1050  if(*param == '\0')
1051  {
1052  //The argument is missing
1053  osStrcpy(connection->response, "501 Missing parameter\r\n");
1054  //Exit immediately
1055  return;
1056  }
1057 
1058  //Close the data connection, if any
1059  ftpServerCloseDataChannel(connection);
1060 
1061  //Start of exception handling block
1062  do
1063  {
1064  //A delimiter character must be specified
1065  delimiter[0] = param[0];
1066  delimiter[1] = '\0';
1067 
1068  //Skip delimiter character
1069  param++;
1070 
1071  //Assume an error condition...
1072  error = ERROR_INVALID_SYNTAX;
1073 
1074  //Retrieve the network protocol to be used
1075  token = osStrtok_r(param, delimiter, &p);
1076  //Syntax error?
1077  if(token == NULL)
1078  break;
1079 
1080  //Convert the string representation to integer
1081  protocol = osStrtoul(token, &end, 10);
1082  //Syntax error?
1083  if(*end != '\0')
1084  break;
1085 
1086  //Get the IP address to be used
1087  token = osStrtok_r(NULL, delimiter, &p);
1088  //Syntax error?
1089  if(token == NULL)
1090  break;
1091 
1092 #if (IPV4_SUPPORT == ENABLED)
1093  //IPv4 address family?
1094  if(protocol == 1)
1095  {
1096  //IPv4 addresses are 4-byte long
1097  connection->remoteIpAddr.length = sizeof(Ipv4Addr);
1098  //Convert the string to IPv4 address
1099  error = ipv4StringToAddr(token, &connection->remoteIpAddr.ipv4Addr);
1100  //Invalid IP address?
1101  if(error)
1102  break;
1103  }
1104  else
1105 #endif
1106 #if (IPV6_SUPPORT == ENABLED)
1107  //IPv6 address family?
1108  if(protocol == 2)
1109  {
1110  //IPv6 addresses are 16-byte long
1111  connection->remoteIpAddr.length = sizeof(Ipv6Addr);
1112  //Convert the string to IPv6 address
1113  error = ipv6StringToAddr(token, &connection->remoteIpAddr.ipv6Addr);
1114  //Invalid IP address?
1115  if(error)
1116  break;
1117  }
1118  else
1119 #endif
1120  //Unknown address family?
1121  {
1122  //Report an error
1123  error = ERROR_INVALID_ADDRESS;
1124  //Exit immediately
1125  break;
1126  }
1127 
1128  //Assume an error condition...
1129  error = ERROR_INVALID_SYNTAX;
1130 
1131  //Get the port number to be used
1132  token = osStrtok_r(NULL, delimiter, &p);
1133  //Syntax error?
1134  if(token == NULL)
1135  break;
1136 
1137  //Convert the string representation to integer
1138  connection->remotePort = (uint16_t) osStrtoul(token, &end, 10);
1139  //Syntax error?
1140  if(*end != '\0')
1141  break;
1142 
1143  //Successful processing
1144  error = NO_ERROR;
1145 
1146  //End of exception handling block
1147  } while(0);
1148 
1149  //Any error to report?
1150  if(error)
1151  {
1152  //Re initialize data connection
1153  connection->passiveMode = FALSE;
1154  connection->remotePort = 0;
1155 
1156  //Format response message
1157  osStrcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
1158  //Exit immediately
1159  return;
1160  }
1161 
1162  //Successful processing
1163  osStrcpy(connection->response, "200 Command okay\r\n");
1164 }
1165 
1166 
1167 /**
1168  * @brief PASV command processing
1169  *
1170  * The PASV command requests the server to listen on a data port and
1171  * to wait for a connection rather than initiate one upon receipt of
1172  * a transfer command
1173  *
1174  * @param[in] connection Pointer to the client connection
1175  * @param[in] param Command line parameters
1176  **/
1177 
1179 {
1180  error_t error;
1181  size_t n;
1182  char_t *p;
1183  IpAddr ipAddr;
1184  uint16_t port;
1185  FtpServerContext *context;
1186 
1187  //Point to the FTP server context
1188  context = connection->context;
1189 
1190  //Ensure the user is logged in
1191  if(!connection->userLoggedIn)
1192  {
1193  //Format response message
1194  osStrcpy(connection->response, "530 Not logged in\r\n");
1195  //Exit immediately
1196  return;
1197  }
1198 
1199  //Close the data connection, if any
1200  ftpServerCloseDataChannel(connection);
1201 
1202  //Get the next passive port number to be used
1203  port = ftpServerGetPassivePort(context);
1204 
1205  //Start of exception handling block
1206  do
1207  {
1208  //Open data socket
1209  connection->dataChannel.socket = socketOpen(SOCKET_TYPE_STREAM,
1211  //Failed to open socket?
1212  if(!connection->dataChannel.socket)
1213  {
1214  //Report an error
1215  error = ERROR_OPEN_FAILED;
1216  break;
1217  }
1218 
1219  //Force the socket to operate in non-blocking mode
1220  error = socketSetTimeout(connection->dataChannel.socket, 0);
1221  //Any error to report?
1222  if(error)
1223  break;
1224 
1225  //Adjust the size of the TX buffer
1226  error = socketSetTxBufferSize(connection->dataChannel.socket,
1228  //Any error to report?
1229  if(error)
1230  break;
1231 
1232  //Adjust the size of the RX buffer
1233  error = socketSetRxBufferSize(connection->dataChannel.socket,
1235  //Any error to report?
1236  if(error)
1237  break;
1238 
1239  //Associate the socket with the relevant interface
1240  error = socketBindToInterface(connection->dataChannel.socket,
1241  connection->interface);
1242  //Unable to bind the socket to the desired interface?
1243  if(error)
1244  break;
1245 
1246  //Bind the socket to the passive port number
1247  error = socketBind(connection->dataChannel.socket, &IP_ADDR_ANY,
1248  port);
1249  //Failed to bind the socket to the desired port?
1250  if(error)
1251  break;
1252 
1253  //Place the data socket in the listening state
1254  error = socketListen(connection->dataChannel.socket, 1);
1255  //Any error to report?
1256  if(error)
1257  break;
1258 
1259  //Retrieve the IP address of the client
1260  error = socketGetRemoteAddr(connection->controlChannel.socket, &ipAddr,
1261  NULL);
1262  //Any error to report?
1263  if(error)
1264  break;
1265 
1266  //PASV command is limited to IPv4
1267  if(ipAddr.length != sizeof(Ipv4Addr))
1268  {
1269  //Report an error
1270  error = ERROR_INVALID_ADDRESS;
1271  break;
1272  }
1273 
1274  //If the server is behind a NAT router, make sure the server knows
1275  //its external IP address
1276  if(!ipv4IsOnLink(connection->interface, ipAddr.ipv4Addr) &&
1277  context->settings.publicIpv4Addr != IPV4_UNSPECIFIED_ADDR)
1278  {
1279  //The server must return the public IP address in the PASV reply
1280  ipAddr.ipv4Addr = context->settings.publicIpv4Addr;
1281  }
1282  else
1283  {
1284  //The server must return its own IP address in the PASV reply
1285  error = socketGetLocalAddr(connection->controlChannel.socket,
1286  &ipAddr, NULL);
1287  //Any error to report?
1288  if(error)
1289  break;
1290 
1291  //PASV command is limited to IPv4
1292  if(ipAddr.length != sizeof(Ipv4Addr))
1293  {
1294  //Report an error
1295  error = ERROR_INVALID_ADDRESS;
1296  break;
1297  }
1298  }
1299 
1300  //End of exception handling block
1301  } while(0);
1302 
1303  //Check status code
1304  if(!error)
1305  {
1306  //Use passive data transfer
1307  connection->passiveMode = TRUE;
1308  //Update data connection state
1309  connection->dataChannel.state = FTP_CHANNEL_STATE_LISTEN;
1310 
1311  //Format response message
1312  n = osSprintf(connection->response, "227 Entering passive mode (");
1313 
1314  //Append host address
1315  ipv4AddrToString(ipAddr.ipv4Addr, connection->response + n);
1316  //Change dots to commas
1317  strReplaceChar(connection->response, '.', ',');
1318 
1319  //Point to the end of the resulting string
1320  p = connection->response + osStrlen(connection->response);
1321  //Append port number
1322  osSprintf(p, ",%" PRIu8 ",%" PRIu8 ")\r\n", MSB(port), LSB(port));
1323  }
1324  else
1325  {
1326  //Clean up side effects
1327  ftpServerCloseDataChannel(connection);
1328 
1329  //Format response message
1330  osStrcpy(connection->response, "425 Can't enter passive mode\r\n");
1331  }
1332 }
1333 
1334 
1335 /**
1336  * @brief EPSV command processing
1337  *
1338  * The EPSV command requests that a server listen on a data port and
1339  * wait for a connection
1340  *
1341  * @param[in] connection Pointer to the client connection
1342  * @param[in] param Command line parameters
1343  **/
1344 
1346 {
1347  error_t error;
1348  uint16_t port;
1349  FtpServerContext *context;
1350 
1351  //Point to the FTP server context
1352  context = connection->context;
1353 
1354  //Ensure the user is logged in
1355  if(!connection->userLoggedIn)
1356  {
1357  //Format response message
1358  osStrcpy(connection->response, "530 Not logged in\r\n");
1359  //Exit immediately
1360  return;
1361  }
1362 
1363  //Close the data connection, if any
1364  ftpServerCloseDataChannel(connection);
1365 
1366  //Get the next passive port number to be used
1367  port = ftpServerGetPassivePort(context);
1368 
1369  //Start of exception handling block
1370  do
1371  {
1372  //Open data socket
1373  connection->dataChannel.socket = socketOpen(SOCKET_TYPE_STREAM,
1375  //Failed to open socket?
1376  if(!connection->dataChannel.socket)
1377  {
1378  //Report an error
1379  error = ERROR_OPEN_FAILED;
1380  //Exit immediately
1381  break;
1382  }
1383 
1384  //Force the socket to operate in non-blocking mode
1385  error = socketSetTimeout(connection->dataChannel.socket, 0);
1386  //Any error to report?
1387  if(error)
1388  break;
1389 
1390  //Adjust the size of the TX buffer
1391  error = socketSetTxBufferSize(connection->dataChannel.socket,
1393  //Any error to report?
1394  if(error)
1395  break;
1396 
1397  //Adjust the size of the RX buffer
1398  error = socketSetRxBufferSize(connection->dataChannel.socket,
1400  //Any error to report?
1401  if(error)
1402  break;
1403 
1404  //Associate the socket with the relevant interface
1405  error = socketBindToInterface(connection->dataChannel.socket,
1406  connection->interface);
1407  //Unable to bind the socket to the desired interface?
1408  if(error)
1409  break;
1410 
1411  //Bind the socket to the passive port number
1412  error = socketBind(connection->dataChannel.socket, &IP_ADDR_ANY, port);
1413  //Failed to bind the socket to the desired port?
1414  if(error)
1415  break;
1416 
1417  //Place the data socket in the listening state
1418  error = socketListen(connection->dataChannel.socket, 1);
1419  //Any error to report?
1420  if(error)
1421  break;
1422 
1423  //End of exception handling block
1424  } while(0);
1425 
1426  //Check status code
1427  if(!error)
1428  {
1429  //Use passive data transfer
1430  connection->passiveMode = TRUE;
1431  //Update data connection state
1432  connection->dataChannel.state = FTP_CHANNEL_STATE_LISTEN;
1433 
1434  //The response code for entering passive mode using an extended address
1435  //must be 229
1436  osSprintf(connection->response, "229 Entering extended passive mode (|||"
1437  "%" PRIu16 "|)\r\n", port);
1438  }
1439  else
1440  {
1441  //Clean up side effects
1442  ftpServerCloseDataChannel(connection);
1443 
1444  //Format response message
1445  osStrcpy(connection->response, "425 Can't enter passive mode\r\n");
1446  }
1447 }
1448 
1449 
1450 /**
1451  * @brief ABOR command processing
1452  *
1453  * The ABOR command tells the server to abort the previous FTP
1454  * service command and any associated transfer of data
1455  *
1456  * @param[in] connection Pointer to the client connection
1457  * @param[in] param Command line parameters
1458  **/
1459 
1461 {
1462  //There are two cases to consider upon receipt of this command
1463  if(connection->dataChannel.state == FTP_CHANNEL_STATE_CLOSED)
1464  {
1465  //If the FTP service command was already completed, the server closes
1466  //the data connection (if it is open)...
1467  ftpServerCloseDataChannel(connection);
1468 
1469  //...and responds with a 226 reply, indicating that the abort command
1470  //was successfully processed
1471  osStrcpy(connection->response, "226 Abort command successful\r\n");
1472  }
1473  else
1474  {
1475  //If the FTP service command is still in progress, the server aborts
1476  //the FTP service in progress and closes the data connection...
1477  ftpServerCloseDataChannel(connection);
1478 
1479  //...returning a 426 reply to indicate that the service request
1480  //terminated abnormally
1481  osStrcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
1482 
1483  //The server then sends a 226 reply, indicating that the abort command
1484  //was successfully processed
1485  osStrcat(connection->response, "226 Abort command successful\r\n");
1486  }
1487 
1488  //Release previously allocated resources
1489  if(connection->file != NULL)
1490  {
1491  fsCloseFile(connection->file);
1492  connection->file = NULL;
1493  }
1494 
1495  if(connection->dir != NULL)
1496  {
1497  fsCloseDir(connection->dir);
1498  connection->dir = NULL;
1499  }
1500 }
1501 
1502 
1503 /**
1504  * @brief PWD command processing
1505  *
1506  * The PWD command causes the name of the current working
1507  * directory to be returned in the reply
1508  *
1509  * @param[in] connection Pointer to the client connection
1510  * @param[in] param Command line parameters
1511  **/
1512 
1514 {
1515  //Ensure the user is logged in
1516  if(!connection->userLoggedIn)
1517  {
1518  //Format response message
1519  osStrcpy(connection->response, "530 Not logged in\r\n");
1520  //Exit immediately
1521  return;
1522  }
1523 
1524  //A successful PWD command uses the 257 reply code
1525  osSprintf(connection->response, "257 \"%s\" is current directory\r\n",
1526  ftpServerStripHomeDir(connection, connection->currentDir));
1527 }
1528 
1529 
1530 /**
1531  * @brief CWD command processing
1532  *
1533  * The CWD command allows the user to work with a different
1534  * directory
1535  *
1536  * @param[in] connection Pointer to the client connection
1537  * @param[in] param Command line parameters
1538  **/
1539 
1541 {
1542  error_t error;
1543  uint_t perm;
1544 
1545  //Ensure the user is logged in
1546  if(!connection->userLoggedIn)
1547  {
1548  //Format response message
1549  osStrcpy(connection->response, "530 Not logged in\r\n");
1550  //Exit immediately
1551  return;
1552  }
1553 
1554  //The argument specifies the pathname
1555  if(*param == '\0')
1556  {
1557  //The argument is missing
1558  osStrcpy(connection->response, "501 Missing parameter\r\n");
1559  //Exit immediately
1560  return;
1561  }
1562 
1563  //Retrieve the full pathname
1564  error = ftpServerGetPath(connection, param, connection->path,
1566 
1567  //Make sure the pathname is valid
1568  if(error)
1569  {
1570  //Report an error
1571  osStrcpy(connection->response, "501 Invalid parameter\r\n");
1572  //Exit immediately
1573  return;
1574  }
1575 
1576  //Retrieve permissions for the specified directory
1577  perm = ftpServerGetFilePermissions(connection, connection->path);
1578 
1579  //Insufficient access rights?
1580  if((perm & FTP_FILE_PERM_READ) == 0)
1581  {
1582  //Report an error
1583  osStrcpy(connection->response, "550 Access denied\r\n");
1584  //Exit immediately
1585  return;
1586  }
1587 
1588  //Make sure the specified directory exists
1589  if(!fsDirExists(connection->path))
1590  {
1591  //Report an error
1592  osStrcpy(connection->response, "550 Directory not found\r\n");
1593  //Exit immediately
1594  return;
1595  }
1596 
1597  //Change current working directory
1598  osStrcpy(connection->currentDir, connection->path);
1599 
1600  //A successful PWD command uses the 250 reply code
1601  osSprintf(connection->response, "250 Directory changed to %s\r\n",
1602  ftpServerStripHomeDir(connection, connection->currentDir));
1603 }
1604 
1605 
1606 /**
1607  * @brief CDUP command processing
1608  *
1609  * The CDUP command allows the user to change to the parent directory
1610  *
1611  * @param[in] connection Pointer to the client connection
1612  * @param[in] param Command line parameters
1613  **/
1614 
1616 {
1617  uint_t perm;
1618 
1619  //Ensure the user is logged in
1620  if(!connection->userLoggedIn)
1621  {
1622  //Format response message
1623  osStrcpy(connection->response, "530 Not logged in\r\n");
1624  //Exit immediately
1625  return;
1626  }
1627 
1628  //Get current directory
1629  osStrcpy(connection->path, connection->currentDir);
1630 
1631  //Change to the parent directory
1632  pathCombine(connection->path, "..", FTP_SERVER_MAX_PATH_LEN);
1633  pathCanonicalize(connection->path);
1634 
1635  //Retrieve permissions for the directory
1636  perm = ftpServerGetFilePermissions(connection, connection->path);
1637 
1638  //Check access rights
1639  if((perm & FTP_FILE_PERM_READ) != 0)
1640  {
1641  //Update current directory
1642  osStrcpy(connection->currentDir, connection->path);
1643  }
1644 
1645  //A successful PWD command uses the 250 reply code
1646  osSprintf(connection->response, "250 Directory changed to %s\r\n",
1647  ftpServerStripHomeDir(connection, connection->currentDir));
1648 }
1649 
1650 
1651 /**
1652  * @brief LIST command processing
1653  *
1654  * The LIST command is used to list the content of a directory
1655  *
1656  * @param[in] connection Pointer to the client connection
1657  * @param[in] param Command line parameters
1658  **/
1659 
1661 {
1662  error_t error;
1663  uint_t perm;
1664 
1665  //Ensure the user is logged in
1666  if(!connection->userLoggedIn)
1667  {
1668  //Format response message
1669  osStrcpy(connection->response, "530 Not logged in\r\n");
1670  //Exit immediately
1671  return;
1672  }
1673 
1674  //Any option flags
1675  while(*param == '-')
1676  {
1677  //Skip option flags
1678  while(*param != ' ' && *param != '\0')
1679  {
1680  param++;
1681  }
1682 
1683  //Skip whitespace characters
1684  while(*param == ' ')
1685  {
1686  param++;
1687  }
1688  }
1689 
1690  //The pathname is optional
1691  if(*param == '\0')
1692  {
1693  //Use current directory if no pathname is specified
1694  osStrcpy(connection->path, connection->currentDir);
1695  }
1696  else
1697  {
1698  //Retrieve the full pathname
1699  error = ftpServerGetPath(connection, param, connection->path,
1701 
1702  //Any error to report?
1703  if(error)
1704  {
1705  //The specified pathname is not valid...
1706  osStrcpy(connection->response, "501 Invalid parameter\r\n");
1707  //Exit immediately
1708  return;
1709  }
1710  }
1711 
1712  //Retrieve permissions for the specified directory
1713  perm = ftpServerGetFilePermissions(connection, connection->path);
1714 
1715  //Insufficient access rights?
1716  if((perm & FTP_FILE_PERM_READ) == 0)
1717  {
1718  //Report an error
1719  osStrcpy(connection->response, "550 Access denied\r\n");
1720  //Exit immediately
1721  return;
1722  }
1723 
1724  //Open the specified directory for reading
1725  connection->dir = fsOpenDir(connection->path);
1726 
1727  //Failed to open the directory?
1728  if(!connection->dir)
1729  {
1730  //Report an error
1731  osStrcpy(connection->response, "550 Directory not found\r\n");
1732  //Exit immediately
1733  return;
1734  }
1735 
1736  //Check current data transfer mode
1737  if(connection->passiveMode)
1738  {
1739  //Check whether the data connection is already opened
1740  if(connection->dataChannel.state == FTP_CHANNEL_STATE_IDLE)
1741  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
1742  }
1743  else
1744  {
1745  //Open the data connection
1746  error = ftpServerOpenDataChannel(connection);
1747 
1748  //Any error to report?
1749  if(error)
1750  {
1751  //Clean up side effects
1752  fsCloseDir(connection->dir);
1753  //Format response
1754  osStrcpy(connection->response, "450 Can't open data connection\r\n");
1755  //Exit immediately
1756  return;
1757  }
1758 
1759  //The data connection is ready to send data
1760  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
1761  }
1762 
1763  //Flush transmission buffer
1764  connection->bufferLength = 0;
1765  connection->bufferPos = 0;
1766 
1767  //LIST command is being processed
1768  connection->controlChannel.state = FTP_CHANNEL_STATE_LIST;
1769 
1770  //Format response message
1771  osStrcpy(connection->response, "150 Opening data connection\r\n");
1772 }
1773 
1774 
1775 /**
1776  * @brief NLST command processing
1777  *
1778  * The NLST command is used to list the content of a directory
1779  *
1780  * @param[in] connection Pointer to the client connection
1781  * @param[in] param Command line parameters
1782  **/
1783 
1785 {
1786  error_t error;
1787  uint_t perm;
1788 
1789  //Ensure the user is logged in
1790  if(!connection->userLoggedIn)
1791  {
1792  //Format response message
1793  osStrcpy(connection->response, "530 Not logged in\r\n");
1794  //Exit immediately
1795  return;
1796  }
1797 
1798  //Any option flags
1799  while(*param == '-')
1800  {
1801  //Skip option flags
1802  while(*param != ' ' && *param != '\0')
1803  {
1804  param++;
1805  }
1806 
1807  //Skip whitespace characters
1808  while(*param == ' ')
1809  {
1810  param++;
1811  }
1812  }
1813 
1814  //The pathname is optional
1815  if(*param == '\0')
1816  {
1817  //Use current directory if no pathname is specified
1818  osStrcpy(connection->path, connection->currentDir);
1819  }
1820  else
1821  {
1822  //Retrieve the full pathname
1823  error = ftpServerGetPath(connection, param, connection->path,
1825 
1826  //Any error to report?
1827  if(error)
1828  {
1829  //The specified pathname is not valid...
1830  osStrcpy(connection->response, "501 Invalid parameter\r\n");
1831  //Exit immediately
1832  return;
1833  }
1834  }
1835 
1836  //Retrieve permissions for the specified directory
1837  perm = ftpServerGetFilePermissions(connection, connection->path);
1838 
1839  //Insufficient access rights?
1840  if((perm & FTP_FILE_PERM_READ) == 0)
1841  {
1842  //Report an error
1843  osStrcpy(connection->response, "550 Access denied\r\n");
1844  //Exit immediately
1845  return;
1846  }
1847 
1848  //Open the specified directory for reading
1849  connection->dir = fsOpenDir(connection->path);
1850 
1851  //Failed to open the directory?
1852  if(!connection->dir)
1853  {
1854  //Report an error
1855  osStrcpy(connection->response, "550 Directory not found\r\n");
1856  //Exit immediately
1857  return;
1858  }
1859 
1860  //Check current data transfer mode
1861  if(connection->passiveMode)
1862  {
1863  //Check whether the data connection is already opened
1864  if(connection->dataChannel.state == FTP_CHANNEL_STATE_IDLE)
1865  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
1866  }
1867  else
1868  {
1869  //Open the data connection
1870  error = ftpServerOpenDataChannel(connection);
1871 
1872  //Any error to report?
1873  if(error)
1874  {
1875  //Clean up side effects
1876  fsCloseDir(connection->dir);
1877  //Format response
1878  osStrcpy(connection->response, "450 Can't open data connection\r\n");
1879  //Exit immediately
1880  return;
1881  }
1882 
1883  //The data connection is ready to send data
1884  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
1885  }
1886 
1887  //Flush transmission buffer
1888  connection->bufferLength = 0;
1889  connection->bufferPos = 0;
1890 
1891  //NLST command is being processed
1892  connection->controlChannel.state = FTP_CHANNEL_STATE_NLST;
1893 
1894  //Format response message
1895  osStrcpy(connection->response, "150 Opening data connection\r\n");
1896 }
1897 
1898 
1899 /**
1900  * @brief MKD command processing
1901  *
1902  * The MKD command causes the directory specified in the pathname
1903  * to be created as a directory
1904  *
1905  * @param[in] connection Pointer to the client connection
1906  * @param[in] param Command line parameters
1907  **/
1908 
1910 {
1911  error_t error;
1912  uint_t perm;
1913 
1914  //Ensure the user is logged in
1915  if(!connection->userLoggedIn)
1916  {
1917  //Format response message
1918  osStrcpy(connection->response, "530 Not logged in\r\n");
1919  //Exit immediately
1920  return;
1921  }
1922 
1923  //The argument specifies the pathname
1924  if(*param == '\0')
1925  {
1926  //The argument is missing
1927  osStrcpy(connection->response, "501 Missing parameter\r\n");
1928  //Exit immediately
1929  return;
1930  }
1931 
1932  //Retrieve the full pathname
1933  error = ftpServerGetPath(connection, param, connection->path,
1935 
1936  //Any error to report?
1937  if(error)
1938  {
1939  //The specified pathname is not valid...
1940  osStrcpy(connection->response, "501 Invalid parameter\r\n");
1941  //Exit immediately
1942  return;
1943  }
1944 
1945  //Retrieve permissions for the specified directory
1946  perm = ftpServerGetFilePermissions(connection, connection->path);
1947 
1948  //Insufficient access rights?
1949  if((perm & FTP_FILE_PERM_WRITE) == 0)
1950  {
1951  //Report an error
1952  osStrcpy(connection->response, "550 Access denied\r\n");
1953  //Exit immediately
1954  return;
1955  }
1956 
1957  //Create the specified directory
1958  error = fsCreateDir(connection->path);
1959 
1960  //Any error to report?
1961  if(error)
1962  {
1963  //The specified pathname is not valid...
1964  osStrcpy(connection->response, "550 Can't create directory\r\n");
1965  //Exit immediately
1966  return;
1967  }
1968 
1969  //The specified directory was successfully created
1970  osSprintf(connection->response, "257 \"%s\" created\r\n",
1971  ftpServerStripHomeDir(connection, connection->path));
1972 }
1973 
1974 
1975 /**
1976  * @brief RMD command processing
1977  *
1978  * The RMD command causes the directory specified in the pathname
1979  * to be removed
1980  *
1981  * @param[in] connection Pointer to the client connection
1982  * @param[in] param Command line parameters
1983  **/
1984 
1986 {
1987  error_t error;
1988  uint_t perm;
1989 
1990  //Ensure the user is logged in
1991  if(!connection->userLoggedIn)
1992  {
1993  //Format response message
1994  osStrcpy(connection->response, "530 Not logged in\r\n");
1995  //Exit immediately
1996  return;
1997  }
1998 
1999  //The argument specifies the directory to be removed
2000  if(*param == '\0')
2001  {
2002  //The argument is missing
2003  osStrcpy(connection->response, "501 Missing parameter\r\n");
2004  //Exit immediately
2005  return;
2006  }
2007 
2008  //Retrieve the full pathname of the directory
2009  error = ftpServerGetPath(connection, param, connection->path,
2011 
2012  //Any error to report?
2013  if(error)
2014  {
2015  //The specified pathname is not valid...
2016  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2017  //Exit immediately
2018  return;
2019  }
2020 
2021  //Retrieve permissions for the specified directory
2022  perm = ftpServerGetFilePermissions(connection, connection->path);
2023 
2024  //Insufficient access rights?
2025  if((perm & FTP_FILE_PERM_WRITE) == 0)
2026  {
2027  //Report an error
2028  osStrcpy(connection->response, "550 Access denied\r\n");
2029  //Exit immediately
2030  return;
2031  }
2032 
2033  //Remove the specified directory
2034  error = fsRemoveDir(connection->path);
2035 
2036  //Any error to report?
2037  if(error)
2038  {
2039  //The specified directory cannot be deleted...
2040  osStrcpy(connection->response, "550 Can't remove directory\r\n");
2041  //Exit immediately
2042  return;
2043  }
2044 
2045  //The specified directory was successfully removed
2046  osStrcpy(connection->response, "250 Directory removed\r\n");
2047 }
2048 
2049 
2050 /**
2051  * @brief SIZE command processing
2052  *
2053  * The SIZE command is used to obtain the transfer size of the specified file
2054  *
2055  * @param[in] connection Pointer to the client connection
2056  * @param[in] param Command line parameters
2057  **/
2058 
2060 {
2061  error_t error;
2062  uint_t perm;
2063  uint32_t size;
2064 
2065  //Ensure the user is logged in
2066  if(!connection->userLoggedIn)
2067  {
2068  //Format response message
2069  osStrcpy(connection->response, "530 Not logged in\r\n");
2070  //Exit immediately
2071  return;
2072  }
2073 
2074  //The argument specifies the pathname of the file
2075  if(*param == '\0')
2076  {
2077  //The argument is missing
2078  osStrcpy(connection->response, "501 Missing parameter\r\n");
2079  //Exit immediately
2080  return;
2081  }
2082 
2083  //Retrieve the full pathname
2084  error = ftpServerGetPath(connection, param, connection->path,
2086 
2087  //Any error to report?
2088  if(error)
2089  {
2090  //The specified pathname is not valid...
2091  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2092  //Exit immediately
2093  return;
2094  }
2095 
2096  //Retrieve permissions for the specified directory
2097  perm = ftpServerGetFilePermissions(connection, connection->path);
2098 
2099  //Insufficient access rights?
2100  if((perm & FTP_FILE_PERM_LIST) == 0 && (perm & FTP_FILE_PERM_READ) == 0)
2101  {
2102  //Report an error
2103  osStrcpy(connection->response, "550 Access denied\r\n");
2104  //Exit immediately
2105  return;
2106  }
2107 
2108  //Retrieve the size of the specified file
2109  error = fsGetFileSize(connection->path, &size);
2110 
2111  //Any error to report?
2112  if(error)
2113  {
2114  //Report an error
2115  osStrcpy(connection->response, "550 File not found\r\n");
2116  //Exit immediately
2117  return;
2118  }
2119 
2120  //Format response message
2121  osSprintf(connection->response, "213 %" PRIu32 "\r\n", size);
2122 }
2123 
2124 
2125 /**
2126  * @brief RETR command processing
2127  *
2128  * The RETR command is used to retrieve the content of the specified file
2129  *
2130  * @param[in] connection Pointer to the client connection
2131  * @param[in] param Command line parameters
2132  **/
2133 
2135 {
2136  error_t error;
2137  uint_t perm;
2138 
2139  //Ensure the user is logged in
2140  if(!connection->userLoggedIn)
2141  {
2142  //Format response message
2143  osStrcpy(connection->response, "530 Not logged in\r\n");
2144  //Exit immediately
2145  return;
2146  }
2147 
2148  //The argument specifies the pathname of the file to read
2149  if(*param == '\0')
2150  {
2151  //The argument is missing
2152  osStrcpy(connection->response, "501 Missing parameter\r\n");
2153  //Exit immediately
2154  return;
2155  }
2156 
2157  //Retrieve the full pathname
2158  error = ftpServerGetPath(connection, param, connection->path,
2160 
2161  //Any error to report?
2162  if(error)
2163  {
2164  //The specified pathname is not valid...
2165  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2166  //Exit immediately
2167  return;
2168  }
2169 
2170  //Retrieve permissions for the specified directory
2171  perm = ftpServerGetFilePermissions(connection, connection->path);
2172 
2173  //Insufficient access rights?
2174  if((perm & FTP_FILE_PERM_READ) == 0)
2175  {
2176  //Report an error
2177  osStrcpy(connection->response, "550 Access denied\r\n");
2178  //Exit immediately
2179  return;
2180  }
2181 
2182  //Open specified file for reading
2183  connection->file = fsOpenFile(connection->path, FS_FILE_MODE_READ);
2184 
2185  //Failed to open the file?
2186  if(!connection->file)
2187  {
2188  //Report an error
2189  osStrcpy(connection->response, "550 File not found\r\n");
2190  //Exit immediately
2191  return;
2192  }
2193 
2194  //Check current data transfer mode
2195  if(connection->passiveMode)
2196  {
2197  //Check whether the data connection is already opened
2198  if(connection->dataChannel.state == FTP_CHANNEL_STATE_IDLE)
2199  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
2200  }
2201  else
2202  {
2203  //Open the data connection
2204  error = ftpServerOpenDataChannel(connection);
2205 
2206  //Any error to report?
2207  if(error)
2208  {
2209  //Clean up side effects
2210  fsCloseFile(connection->file);
2211  //Format response
2212  osStrcpy(connection->response, "450 Can't open data connection\r\n");
2213  //Exit immediately
2214  return;
2215  }
2216 
2217  //The data connection is ready to send data
2218  connection->dataChannel.state = FTP_CHANNEL_STATE_SEND;
2219  }
2220 
2221  //Flush transmission buffer
2222  connection->bufferLength = 0;
2223  connection->bufferPos = 0;
2224 
2225  //RETR command is being processed
2226  connection->controlChannel.state = FTP_CHANNEL_STATE_RETR;
2227 
2228  //Format response message
2229  osStrcpy(connection->response, "150 Opening data connection\r\n");
2230 }
2231 
2232 
2233 /**
2234  * @brief STOR command processing
2235  *
2236  * The STOR command is used to store data to the specified file
2237  *
2238  * @param[in] connection Pointer to the client connection
2239  * @param[in] param Command line parameters
2240  **/
2241 
2243 {
2244  error_t error;
2245  uint_t perm;
2246 
2247  //Ensure the user is logged in
2248  if(!connection->userLoggedIn)
2249  {
2250  //Format response message
2251  osStrcpy(connection->response, "530 Not logged in\r\n");
2252  //Exit immediately
2253  return;
2254  }
2255 
2256  //The argument specifies the pathname of the file to written
2257  if(*param == '\0')
2258  {
2259  //The argument is missing
2260  osStrcpy(connection->response, "501 Missing parameter\r\n");
2261  //Exit immediately
2262  return;
2263  }
2264 
2265  //Retrieve the full pathname
2266  error = ftpServerGetPath(connection, param, connection->path,
2268 
2269  //Any error to report?
2270  if(error)
2271  {
2272  //The specified pathname is not valid...
2273  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2274  //Exit immediately
2275  return;
2276  }
2277 
2278  //Retrieve permissions for the specified directory
2279  perm = ftpServerGetFilePermissions(connection, connection->path);
2280 
2281  //Insufficient access rights?
2282  if((perm & FTP_FILE_PERM_WRITE) == 0)
2283  {
2284  //Report an error
2285  osStrcpy(connection->response, "550 Access denied\r\n");
2286  //Exit immediately
2287  return;
2288  }
2289 
2290  //Open specified file for writing
2291  connection->file = fsOpenFile(connection->path,
2293 
2294  //Failed to open the file?
2295  if(!connection->file)
2296  {
2297  //Report an error
2298  osStrcpy(connection->response, "550 File not found\r\n");
2299  //Exit immediately
2300  return;
2301  }
2302 
2303  //Check current data transfer mode
2304  if(connection->passiveMode)
2305  {
2306  //Check whether the data connection is already opened
2307  if(connection->dataChannel.state == FTP_CHANNEL_STATE_IDLE)
2308  connection->dataChannel.state = FTP_CHANNEL_STATE_RECEIVE;
2309  }
2310  else
2311  {
2312  //Open the data connection
2313  error = ftpServerOpenDataChannel(connection);
2314 
2315  //Any error to report?
2316  if(error)
2317  {
2318  //Clean up side effects
2319  fsCloseFile(connection->file);
2320  //Format response
2321  osStrcpy(connection->response, "450 Can't open data connection\r\n");
2322  //Exit immediately
2323  return;
2324  }
2325 
2326  //The data connection is ready to receive data
2327  connection->dataChannel.state = FTP_CHANNEL_STATE_RECEIVE;
2328  }
2329 
2330  //Flush reception buffer
2331  connection->bufferLength = 0;
2332  connection->bufferPos = 0;
2333 
2334  //STOR command is being processed
2335  connection->controlChannel.state = FTP_CHANNEL_STATE_STOR;
2336 
2337  //Format response message
2338  osStrcpy(connection->response, "150 Opening data connection\r\n");
2339 }
2340 
2341 
2342 /**
2343  * @brief APPE command processing
2344  *
2345  * The APPE command is used to append data to the specified file
2346  *
2347  * @param[in] connection Pointer to the client connection
2348  * @param[in] param Command line parameters
2349  **/
2350 
2352 {
2353  error_t error;
2354  uint_t perm;
2355 
2356  //Ensure the user is logged in
2357  if(!connection->userLoggedIn)
2358  {
2359  //Format response message
2360  osStrcpy(connection->response, "530 Not logged in\r\n");
2361  //Exit immediately
2362  return;
2363  }
2364 
2365  //The argument specifies the pathname of the file to written
2366  if(*param == '\0')
2367  {
2368  //The argument is missing
2369  osStrcpy(connection->response, "501 Missing parameter\r\n");
2370  //Exit immediately
2371  return;
2372  }
2373 
2374  //Retrieve the full pathname
2375  error = ftpServerGetPath(connection, param, connection->path,
2377 
2378  //Any error to report?
2379  if(error)
2380  {
2381  //The specified pathname is not valid...
2382  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2383  //Exit immediately
2384  return;
2385  }
2386 
2387  //Retrieve permissions for the specified directory
2388  perm = ftpServerGetFilePermissions(connection, connection->path);
2389 
2390  //Insufficient access rights?
2391  if((perm & FTP_FILE_PERM_WRITE) == 0)
2392  {
2393  //Report an error
2394  osStrcpy(connection->response, "550 Access denied\r\n");
2395  //Exit immediately
2396  return;
2397  }
2398 
2399  //Open specified file for writing
2400  connection->file = fsOpenFile(connection->path,
2402 
2403  //Failed to open the file?
2404  if(!connection->file)
2405  {
2406  //Report an error
2407  osStrcpy(connection->response, "550 File not found\r\n");
2408  //Exit immediately
2409  return;
2410  }
2411 
2412  //Move to the end of the file
2413  error = fsSeekFile(connection->file, 0, FS_SEEK_END);
2414 
2415  //Any error to report?
2416  if(error)
2417  {
2418  //Clean up side effects
2419  fsCloseFile(connection->file);
2420  //Format response
2421  osStrcpy(connection->response, "550 File unavailable\r\n");
2422  //Exit immediately
2423  return;
2424  }
2425 
2426  //Check current data transfer mode
2427  if(connection->passiveMode)
2428  {
2429  //Check whether the data connection is already opened
2430  if(connection->dataChannel.state == FTP_CHANNEL_STATE_IDLE)
2431  connection->dataChannel.state = FTP_CHANNEL_STATE_RECEIVE;
2432  }
2433  else
2434  {
2435  //Open the data connection
2436  error = ftpServerOpenDataChannel(connection);
2437 
2438  //Any error to report?
2439  if(error)
2440  {
2441  //Clean up side effects
2442  fsCloseFile(connection->file);
2443  //Format response
2444  osStrcpy(connection->response, "450 Can't open data connection\r\n");
2445  //Exit immediately
2446  return;
2447  }
2448 
2449  //The data connection is ready to receive data
2450  connection->dataChannel.state = FTP_CHANNEL_STATE_RECEIVE;
2451  }
2452 
2453  //Flush reception buffer
2454  connection->bufferLength = 0;
2455  connection->bufferPos = 0;
2456 
2457  //APPE command is being processed
2458  connection->controlChannel.state = FTP_CHANNEL_STATE_APPE;
2459 
2460  //Format response message
2461  osStrcpy(connection->response, "150 Opening data connection\r\n");
2462 }
2463 
2464 
2465 /**
2466  * @brief RNFR command processing
2467  *
2468  * The RNFR command specifies the old pathname of the file which is
2469  * to be renamed
2470  *
2471  * @param[in] connection Pointer to the client connection
2472  * @param[in] param Command line parameters
2473  **/
2474 
2476 {
2477  error_t error;
2478  uint_t perm;
2479 
2480  //Ensure the user is logged in
2481  if(!connection->userLoggedIn)
2482  {
2483  //Format response message
2484  osStrcpy(connection->response, "530 Not logged in\r\n");
2485  //Exit immediately
2486  return;
2487  }
2488 
2489  //The argument specifies the file to be renamed
2490  if(*param == '\0')
2491  {
2492  //The argument is missing
2493  osStrcpy(connection->response, "501 Missing parameter\r\n");
2494  //Exit immediately
2495  return;
2496  }
2497 
2498  //Retrieve the full pathname
2499  error = ftpServerGetPath(connection, param, connection->path,
2501 
2502  //Any error to report?
2503  if(error)
2504  {
2505  //The specified pathname is not valid...
2506  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2507  //Exit immediately
2508  return;
2509  }
2510 
2511  //Retrieve permissions for the specified directory
2512  perm = ftpServerGetFilePermissions(connection, connection->path);
2513 
2514  //Insufficient access rights?
2515  if((perm & FTP_FILE_PERM_WRITE) == 0)
2516  {
2517  //Report an error
2518  osStrcpy(connection->response, "550 Access denied\r\n");
2519  //Exit immediately
2520  return;
2521  }
2522 
2523  //Make sure the file exists
2524  if(!fsFileExists(connection->path) && !fsDirExists(connection->path))
2525  {
2526  //No such file or directory...
2527  osStrcpy(connection->response, "550 File not found\r\n");
2528  //Exit immediately
2529  return;
2530  }
2531 
2532  //This command must be immediately followed by a RNTO command
2533  connection->controlChannel.state = FTP_CHANNEL_STATE_RNFR;
2534  //Format the response message
2535  osStrcpy(connection->response, "350 File exists, ready for destination name\r\n");
2536 }
2537 
2538 
2539 /**
2540  * @brief RNTO command processing
2541  *
2542  * The RNTO command specifies the new pathname of the file specified
2543  * in the immediately preceding RNFR command
2544  *
2545  * @param[in] connection Pointer to the client connection
2546  * @param[in] param Command line parameters
2547  **/
2548 
2550 {
2551  error_t error;
2552  uint_t perm;
2554 
2555  //Ensure the user is logged in
2556  if(!connection->userLoggedIn)
2557  {
2558  //Format response message
2559  osStrcpy(connection->response, "530 Not logged in\r\n");
2560  //Exit immediately
2561  return;
2562  }
2563 
2564  //This command must immediately follow a RNFR command
2565  if(connection->controlChannel.state != FTP_CHANNEL_STATE_RNFR)
2566  {
2567  //Switch to idle state
2568  connection->controlChannel.state = FTP_CHANNEL_STATE_IDLE;
2569  //Report an error
2570  osStrcpy(connection->response, "503 Bad sequence of commands\r\n");
2571  //Exit immediately
2572  return;
2573  }
2574 
2575  //Switch to idle state
2576  connection->controlChannel.state = FTP_CHANNEL_STATE_IDLE;
2577 
2578  //The argument specifies the new pathname
2579  if(*param == '\0')
2580  {
2581  //The argument is missing
2582  osStrcpy(connection->response, "501 Missing parameter\r\n");
2583  //Exit immediately
2584  return;
2585  }
2586 
2587  //Retrieve the full pathname
2588  error = ftpServerGetPath(connection, param, newPath,
2590 
2591  //Any error to report?
2592  if(error)
2593  {
2594  //The specified pathname is not valid...
2595  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2596  //Exit immediately
2597  return;
2598  }
2599 
2600  //Retrieve permissions for the specified directory
2601  perm = ftpServerGetFilePermissions(connection, newPath);
2602 
2603  //Insufficient access rights?
2604  if((perm & FTP_FILE_PERM_WRITE) == 0)
2605  {
2606  //Report an error
2607  osStrcpy(connection->response, "550 Access denied\r\n");
2608  //Exit immediately
2609  return;
2610  }
2611 
2612  //Check whether the file name already exists
2613  if(fsFileExists(newPath) || fsDirExists(newPath))
2614  {
2615  //Report an error
2616  osStrcpy(connection->response, "550 File already exists\r\n");
2617  //Exit immediately
2618  return;
2619  }
2620 
2621  //Rename the specified file
2622  error = fsRenameFile(connection->path, newPath);
2623 
2624  //Any error to report?
2625  if(error)
2626  {
2627  //The specified file cannot be renamed
2628  osStrcpy(connection->response, "550 Can't rename file\r\n");
2629  //Exit immediately
2630  return;
2631  }
2632 
2633  //The specified file was successfully deleted
2634  osStrcpy(connection->response, "250 File renamed\r\n");
2635 }
2636 
2637 
2638 /**
2639  * @brief DELE command processing
2640  *
2641  * The DELE command causes the file specified in the pathname to be
2642  * deleted at the server site
2643  *
2644  * @param[in] connection Pointer to the client connection
2645  * @param[in] param Command line parameters
2646  **/
2647 
2649 {
2650  error_t error;
2651  uint_t perm;
2652 
2653  //Ensure the user is logged in
2654  if(!connection->userLoggedIn)
2655  {
2656  //Format response message
2657  osStrcpy(connection->response, "530 Not logged in\r\n");
2658  //Exit immediately
2659  return;
2660  }
2661 
2662  //The argument specifies the file to be deleted
2663  if(*param == '\0')
2664  {
2665  //The argument is missing
2666  osStrcpy(connection->response, "501 Missing parameter\r\n");
2667  //Exit immediately
2668  return;
2669  }
2670 
2671  //Retrieve the full pathname of the file
2672  error = ftpServerGetPath(connection, param, connection->path,
2674 
2675  //Any error to report?
2676  if(error)
2677  {
2678  //The specified pathname is not valid...
2679  osStrcpy(connection->response, "501 Invalid parameter\r\n");
2680  //Exit immediately
2681  return;
2682  }
2683 
2684  //Retrieve permissions for the specified directory
2685  perm = ftpServerGetFilePermissions(connection, connection->path);
2686 
2687  //Insufficient access rights?
2688  if((perm & FTP_FILE_PERM_WRITE) == 0)
2689  {
2690  //Report an error
2691  osStrcpy(connection->response, "550 Access denied\r\n");
2692  //Exit immediately
2693  return;
2694  }
2695 
2696  //Delete the specified file
2697  error = fsDeleteFile(connection->path);
2698 
2699  //Any error to report?
2700  if(error)
2701  {
2702  //The specified file cannot be deleted...
2703  osStrcpy(connection->response, "550 Can't delete file\r\n");
2704  //Exit immediately
2705  return;
2706  }
2707 
2708  //The specified file was successfully deleted
2709  osStrcpy(connection->response, "250 File deleted\r\n");
2710 }
2711 
2712 
2713 /**
2714  * @brief Unknown command processing
2715  * @param[in] connection Pointer to the client connection
2716  * @param[in] param Command line parameters
2717  **/
2718 
2720 {
2721  error_t error;
2722  FtpServerContext *context;
2723 
2724  //Point to the FTP server context
2725  context = connection->context;
2726 
2727  //Invoke user-defined callback, if any
2728  if(context->settings.unknownCommandCallback != NULL)
2729  {
2730  //Custom command processing
2731  error = context->settings.unknownCommandCallback(connection,
2732  connection->command, param);
2733  }
2734  else
2735  {
2736  //Report an error
2737  error = ERROR_INVALID_COMMAND;
2738  }
2739 
2740  //Invalid command received?
2741  if(error == ERROR_INVALID_COMMAND)
2742  {
2743  //Format response message
2744  osStrcpy(connection->response, "500 Command unrecognized\r\n");
2745  }
2746 }
2747 
2748 #endif
uint8_t token[]
Definition: coap_common.h:181
unsigned int uint_t
Definition: compiler_port.h:50
char char_t
Definition: compiler_port.h:48
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
uint8_t n
uint16_t port
Definition: dns_common.h:267
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_ADDRESS
Definition: error.h:103
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
@ ERROR_INVALID_COMMAND
Definition: error.h:100
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ NO_ERROR
Success.
Definition: error.h:44
@ FS_FILE_MODE_CREATE
Definition: fs_port.h:74
@ FS_FILE_MODE_READ
Definition: fs_port.h:72
@ FS_FILE_MODE_TRUNC
Definition: fs_port.h:75
@ FS_FILE_MODE_WRITE
Definition: fs_port.h:73
@ FS_SEEK_END
Definition: fs_port.h:87
error_t fsSeekFile(FsFile *file, int_t offset, uint_t origin)
Move to specified position in file.
bool_t fsFileExists(const char_t *path)
Check whether a file exists.
error_t fsRemoveDir(const char_t *path)
Remove a directory.
error_t fsDeleteFile(const char_t *path)
Delete a file.
bool_t fsDirExists(const char_t *path)
Check whether a directory exists.
error_t fsGetFileSize(const char_t *path, uint32_t *size)
Retrieve the size of the specified file.
void fsCloseFile(FsFile *file)
Close a file.
error_t fsRenameFile(const char_t *oldPath, const char_t *newPath)
Rename the specified file.
FsDir * fsOpenDir(const char_t *path)
Open a directory stream.
error_t fsCreateDir(const char_t *path)
Create a directory.
void fsCloseDir(FsDir *dir)
Close a directory stream.
FsFile * fsOpenFile(const char_t *path, uint_t mode)
Open the specified file for reading or writing.
FTP server (File Transfer Protocol)
#define FTP_SERVER_MAX_USERNAME_LEN
Definition: ftp_server.h:123
#define FTP_SERVER_MAX_LINE_LEN
Definition: ftp_server.h:95
#define FTP_SERVER_MAX_HOME_DIR_LEN
Definition: ftp_server.h:116
#define FTP_SERVER_MAX_TCP_BUFFER_SIZE
Definition: ftp_server.h:144
@ FTP_ACCESS_ALLOWED
Definition: ftp_server.h:268
@ FTP_PASSWORD_REQUIRED
Definition: ftp_server.h:269
@ FTP_SERVER_MODE_IMPLICIT_TLS
Definition: ftp_server.h:256
@ FTP_SERVER_MODE_PLAINTEXT
Definition: ftp_server.h:255
@ FTP_SERVER_MODE_EXPLICIT_TLS
Definition: ftp_server.h:257
@ FTP_CHANNEL_STATE_RECEIVE
Definition: ftp_server.h:231
@ FTP_CHANNEL_STATE_USER
Definition: ftp_server.h:235
@ FTP_CHANNEL_STATE_RNFR
Definition: ftp_server.h:241
@ FTP_CHANNEL_STATE_RETR
Definition: ftp_server.h:238
@ FTP_CHANNEL_STATE_DISCARD
Definition: ftp_server.h:232
@ FTP_CHANNEL_STATE_NLST
Definition: ftp_server.h:237
@ FTP_CHANNEL_STATE_WAIT_ACK
Definition: ftp_server.h:243
@ FTP_CHANNEL_STATE_LISTEN
Definition: ftp_server.h:228
@ FTP_CHANNEL_STATE_LIST
Definition: ftp_server.h:236
@ FTP_CHANNEL_STATE_APPE
Definition: ftp_server.h:240
@ FTP_CHANNEL_STATE_CLOSED
Definition: ftp_server.h:226
@ FTP_CHANNEL_STATE_STOR
Definition: ftp_server.h:239
@ FTP_CHANNEL_STATE_IDLE
Definition: ftp_server.h:229
@ FTP_CHANNEL_STATE_AUTH_TLS_1
Definition: ftp_server.h:233
@ FTP_CHANNEL_STATE_SEND
Definition: ftp_server.h:230
@ FTP_FILE_PERM_READ
Definition: ftp_server.h:280
@ FTP_FILE_PERM_WRITE
Definition: ftp_server.h:281
@ FTP_FILE_PERM_LIST
Definition: ftp_server.h:279
#define FtpClientConnection
Definition: ftp_server.h:212
#define FTP_SERVER_MAX_PATH_LEN
Definition: ftp_server.h:130
#define FtpServerContext
Definition: ftp_server.h:208
void ftpServerProcessPort(FtpClientConnection *connection, char_t *param)
PORT command processing.
void ftpServerProcessRmd(FtpClientConnection *connection, char_t *param)
RMD command processing.
void ftpServerProcessType(FtpClientConnection *connection, char_t *param)
TYPE command processing.
void ftpServerProcessRein(FtpClientConnection *connection, char_t *param)
REIN command processing.
void ftpServerProcessPwd(FtpClientConnection *connection, char_t *param)
PWD command processing.
void ftpServerProcessPasv(FtpClientConnection *connection, char_t *param)
PASV command processing.
void ftpServerProcessFeat(FtpClientConnection *connection, char_t *param)
FEAT command processing.
void ftpServerProcessPbsz(FtpClientConnection *connection, char_t *param)
PBSZ command processing.
void ftpServerProcessMode(FtpClientConnection *connection, char_t *param)
MODE command processing.
void ftpServerProcessCwd(FtpClientConnection *connection, char_t *param)
CWD command processing.
void ftpServerProcessUser(FtpClientConnection *connection, char_t *param)
USER command processing.
void ftpServerProcessRnfr(FtpClientConnection *connection, char_t *param)
RNFR command processing.
void ftpServerProcessCdup(FtpClientConnection *connection, char_t *param)
CDUP command processing.
void ftpServerProcessCommand(FtpClientConnection *connection)
FTP command processing.
void ftpServerProcessStor(FtpClientConnection *connection, char_t *param)
STOR command processing.
void ftpServerProcessNoop(FtpClientConnection *connection, char_t *param)
NOOP command processing.
void ftpServerProcessUnknownCmd(FtpClientConnection *connection, char_t *param)
Unknown command processing.
void ftpServerProcessQuit(FtpClientConnection *connection, char_t *param)
QUIT command processing.
void ftpServerProcessAbor(FtpClientConnection *connection, char_t *param)
ABOR command processing.
void ftpServerProcessEpsv(FtpClientConnection *connection, char_t *param)
EPSV command processing.
void ftpServerProcessPass(FtpClientConnection *connection, char_t *param)
PASS command processing.
void ftpServerProcessEprt(FtpClientConnection *connection, char_t *param)
EPRT command processing.
void ftpServerProcessAuth(FtpClientConnection *connection, char_t *param)
AUTH command processing.
void ftpServerProcessSyst(FtpClientConnection *connection, char_t *param)
SYST command processing.
void ftpServerProcessList(FtpClientConnection *connection, char_t *param)
LIST command processing.
void ftpServerProcessMkd(FtpClientConnection *connection, char_t *param)
MKD command processing.
void ftpServerProcessRnto(FtpClientConnection *connection, char_t *param)
RNTO command processing.
void ftpServerProcessDele(FtpClientConnection *connection, char_t *param)
DELE command processing.
void ftpServerProcessProt(FtpClientConnection *connection, char_t *param)
PROT command processing.
void ftpServerProcessStru(FtpClientConnection *connection, char_t *param)
STRU command processing.
void ftpServerProcessNlst(FtpClientConnection *connection, char_t *param)
NLST command processing.
void ftpServerProcessAppe(FtpClientConnection *connection, char_t *param)
APPE command processing.
void ftpServerProcessRetr(FtpClientConnection *connection, char_t *param)
RETR command processing.
void ftpServerProcessSize(FtpClientConnection *connection, char_t *param)
SIZE command processing.
FTP server (command processing)
error_t ftpServerOpenDataChannel(FtpClientConnection *connection)
Open data connection.
void ftpServerCloseDataChannel(FtpClientConnection *connection)
Close data connection.
FTP data connection.
uint_t ftpServerGetFilePermissions(FtpClientConnection *connection, const char_t *path)
Get permissions for the specified file or directory.
const char_t * ftpServerStripHomeDir(FtpClientConnection *connection, const char_t *path)
Strip home directory from specified pathname.
uint16_t ftpServerGetPassivePort(FtpServerContext *context)
Get a passive port number.
error_t ftpServerGetPath(FtpClientConnection *connection, const char_t *inputPath, char_t *outputPath, size_t maxLen)
Retrieve the full pathname.
Helper functions for FTP server.
const IpAddr IP_ADDR_ANY
Definition: ip.c:51
error_t ipStringToAddr(const char_t *str, IpAddr *ipAddr)
Convert a string representation of an IP address to a binary IP address.
Definition: ip.c:794
Ipv4Addr ipAddr
Definition: ipcp.h:105
char_t * ipv4AddrToString(Ipv4Addr ipAddr, char_t *str)
Convert a binary IPv4 address to dot-decimal notation.
Definition: ipv4.c:1636
error_t ipv4StringToAddr(const char_t *str, Ipv4Addr *ipAddr)
Convert a dot-decimal string to a binary IPv4 address.
Definition: ipv4.c:1547
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:267
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:110
uint8_t protocol
Definition: ipv4.h:296
bool_t ipv4IsOnLink(NetInterface *interface, Ipv4Addr ipAddr)
Check whether an IPv4 address is on-link.
Definition: ipv4_misc.c:433
Helper functions for IPv4.
error_t ipv6StringToAddr(const char_t *str, Ipv6Addr *ipAddr)
Convert a string representation of an IPv6 address to a binary IPv6 address.
Definition: ipv6.c:2221
Ipv6Addr
Definition: ipv6.h:251
uint8_t p
Definition: ndp.h:300
#define socketBindToInterface
Definition: net_legacy.h:193
#define osStrcasecmp(s1, s2)
Definition: os_port.h:183
#define LSB(x)
Definition: os_port.h:55
#define osStrlen(s)
Definition: os_port.h:165
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osStrcat(s1, s2)
Definition: os_port.h:219
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define osStrcpy(s1, s2)
Definition: os_port.h:207
#define osStrtoul(s, endptr, base)
Definition: os_port.h:249
#define MSB(x)
Definition: os_port.h:59
#define osStrtok_r(s, delim, last)
Definition: os_port.h:225
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:370
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:150
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:129
Path manipulation helper functions.
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:778
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:875
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:148
error_t socketSetRxBufferSize(Socket *socket, size_t size)
Specify the size of the TCP receive buffer.
Definition: socket.c:699
error_t socketGetRemoteAddr(Socket *socket, IpAddr *remoteIpAddr, uint16_t *remotePort)
Retrieve the address of the peer to which a socket is connected.
Definition: socket.c:1445
error_t socketSetTxBufferSize(Socket *socket, size_t size)
Specify the size of the TCP send buffer.
Definition: socket.c:663
error_t socketGetLocalAddr(Socket *socket, IpAddr *localIpAddr, uint16_t *localPort)
Retrieve the local address for a given socket.
Definition: socket.c:1413
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:100
@ SOCKET_TYPE_STREAM
Definition: socket.h:85
void strRemoveTrailingSpace(char_t *s)
Removes all trailing whitespace from a string.
Definition: str.c:115
void strReplaceChar(char_t *s, char_t oldChar, char_t newChar)
Replace all occurrences of the specified character.
Definition: str.c:141
String manipulation helper functions.
IP network address.
Definition: ip.h:79