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