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