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