Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ftp_server_commands.c Source File

ftp_server_commands.c

Go to the documentation of this file.
00001 /**
00002  * @file ftp_server_commands.c
00003  * @brief FTP server (command processing)
00004  *
00005  * @section License
00006  *
00007  * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
00008  *
00009  * This file is part of CycloneTCP Open.
00010  *
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software Foundation,
00023  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  *
00025  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00026  * @version 1.7.6
00027  **/
00028 
00029 //Switch to the appropriate trace level
00030 #define TRACE_LEVEL FTP_TRACE_LEVEL
00031 
00032 //Dependencies
00033 #include <stdlib.h>
00034 #include "ftp/ftp_server.h"
00035 #include "ftp/ftp_server_events.h"
00036 #include "ftp/ftp_server_commands.h"
00037 #include "ftp/ftp_server_misc.h"
00038 #include "str.h"
00039 #include "path.h"
00040 #include "error.h"
00041 #include "debug.h"
00042 
00043 //Check TCP/IP stack configuration
00044 #if (FTP_SERVER_SUPPORT == ENABLED)
00045 
00046 
00047 /**
00048  * @brief FTP command processing
00049  * @param[in] context Pointer to the FTP server context
00050  * @param[in] connection Pointer to the client connection
00051  **/
00052 
00053 void ftpServerProcessCmd(FtpServerContext *context,
00054    FtpClientConnection *connection)
00055 {
00056    size_t n;
00057    char_t *p;
00058 
00059    //The <CRLF> sequence should be used to terminate the command line
00060    for(n = 0; n < connection->commandLength; n++)
00061    {
00062       if(connection->command[n] == '\n')
00063          break;
00064    }
00065 
00066    //Any command to process?
00067    if(n < connection->commandLength)
00068    {
00069       //Properly terminate the string with a NULL character
00070       connection->command[n] = '\0';
00071       //Remove trailing whitespace from the command line
00072       strRemoveTrailingSpace(connection->command);
00073 
00074       //Debug message
00075       TRACE_DEBUG("FTP client: %s\r\n", connection->command);
00076 
00077       //Command line too long?
00078       if(connection->controlState == FTP_CONTROL_STATE_DISCARD)
00079       {
00080          //Switch to idle state
00081          connection->controlState = FTP_CONTROL_STATE_IDLE;
00082          //Format response message
00083          strcpy(connection->response, "500 Command line too long\r\n");
00084       }
00085       else
00086       {
00087          //The command name and the arguments are separated by one or more spaces
00088          for(p = connection->command; *p != '\0' && *p != ' '; p++);
00089 
00090          //Space character found?
00091          if(*p == ' ')
00092          {
00093             //Split the string at the first occurrence of the space character
00094             *(p++) = '\0';
00095             //Skip extra whitespace
00096             while(*p == ' ') p++;
00097          }
00098 
00099          //NOOP command received
00100          if(!strcasecmp(connection->command, "NOOP"))
00101             ftpServerProcessNoop(context, connection, p);
00102          //SYST command received
00103          else if(!strcasecmp(connection->command, "SYST"))
00104             ftpServerProcessSyst(context, connection, p);
00105          //FEAT command received?
00106          else if(!strcasecmp(connection->command, "FEAT"))
00107             ftpServerProcessFeat(context, connection, p);
00108          //TYPE command received?
00109          else if(!strcasecmp(connection->command, "TYPE"))
00110             ftpServerProcessType(context, connection, p);
00111          //STRU command received?
00112          else if(!strcasecmp(connection->command, "STRU"))
00113             ftpServerProcessStru(context, connection, p);
00114          //MODE command received?
00115          else if(!strcasecmp(connection->command, "MODE"))
00116             ftpServerProcessMode(context, connection, p);
00117          //USER command received?
00118          else if(!strcasecmp(connection->command, "USER"))
00119             ftpServerProcessUser(context, connection, p);
00120          //PASS command received?
00121          else if(!strcasecmp(connection->command, "PASS"))
00122             ftpServerProcessPass(context, connection, p);
00123          //REIN command received?
00124          else if(!strcasecmp(connection->command, "REIN"))
00125             ftpServerProcessRein(context, connection, p);
00126          //QUIT command received?
00127          else if(!strcasecmp(connection->command, "QUIT"))
00128             ftpServerProcessQuit(context, connection, p);
00129          //PORT command received?
00130          else if(!strcasecmp(connection->command, "PORT"))
00131             ftpServerProcessPort(context, connection, p);
00132          //EPRT command received?
00133          else if(!strcasecmp(connection->command, "EPRT"))
00134             ftpServerProcessEprt(context, connection, p);
00135          //PASV command received?
00136          else if(!strcasecmp(connection->command, "PASV"))
00137             ftpServerProcessPasv(context, connection, p);
00138          //EPSV command received?
00139          else if(!strcasecmp(connection->command, "EPSV"))
00140             ftpServerProcessEpsv(context, connection, p);
00141          //ABOR command received?
00142          else if(!strcasecmp(connection->command, "ABOR"))
00143             ftpServerProcessAbor(context, connection, p);
00144          //PWD command received?
00145          else if(!strcasecmp(connection->command, "PWD"))
00146             ftpServerProcessPwd(context, connection, p);
00147          //LIST command received?
00148          else if(!strcasecmp(connection->command, "LIST"))
00149             ftpServerProcessList(context, connection, p);
00150          //CWD command received?
00151          else if(!strcasecmp(connection->command, "CWD"))
00152             ftpServerProcessCwd(context, connection, p);
00153          //CDUP command received?
00154          else if(!strcasecmp(connection->command, "CDUP"))
00155             ftpServerProcessCdup(context, connection, p);
00156          //MKD command received?
00157          else if(!strcasecmp(connection->command, "MKD"))
00158             ftpServerProcessMkd(context, connection, p);
00159          //RMD command received?
00160          else if(!strcasecmp(connection->command, "RMD"))
00161             ftpServerProcessRmd(context, connection, p);
00162          //SIZE command received?
00163          else if(!strcasecmp(connection->command, "SIZE"))
00164             ftpServerProcessSize(context, connection, p);
00165          //RETR command received?
00166          else if(!strcasecmp(connection->command, "RETR"))
00167             ftpServerProcessRetr(context, connection, p);
00168          //STOR command received?
00169          else if(!strcasecmp(connection->command, "STOR"))
00170             ftpServerProcessStor(context, connection, p);
00171          //APPE command received?
00172          else if(!strcasecmp(connection->command, "APPE"))
00173             ftpServerProcessAppe(context, connection, p);
00174          //RNFR command received?
00175          else if(!strcasecmp(connection->command, "RNFR"))
00176             ftpServerProcessRnfr(context, connection, p);
00177          //RNTO command received?
00178          else if(!strcasecmp(connection->command, "RNTO"))
00179             ftpServerProcessRnto(context, connection, p);
00180          //DELE command received?
00181          else if(!strcasecmp(connection->command, "DELE"))
00182             ftpServerProcessDele(context, connection, p);
00183          //Unknown command received?
00184          else
00185             ftpServerProcessUnknownCmd(context, connection, p);
00186       }
00187 
00188       //Debug message
00189       TRACE_DEBUG("FTP server: %s", connection->response);
00190 
00191       //Number of bytes in the response buffer
00192       connection->responseLength = strlen(connection->response);
00193       connection->responsePos = 0;
00194 
00195       //Clear command line
00196       connection->commandLength = 0;
00197    }
00198    else if(connection->commandLength >= FTP_SERVER_MAX_LINE_LEN)
00199    {
00200       //The command line is too long...
00201       connection->controlState = FTP_CONTROL_STATE_DISCARD;
00202       //Drop incoming data
00203       connection->commandLength = 0;
00204    }
00205 }
00206 
00207 
00208 /**
00209  * @brief Unknown command processing
00210  * @param[in] context Pointer to the FTP server context
00211  * @param[in] connection Pointer to the client connection
00212  * @param[in] param Command line parameters
00213  **/
00214 
00215 void ftpServerProcessUnknownCmd(FtpServerContext *context,
00216    FtpClientConnection *connection, char_t *param)
00217 {
00218    error_t error;
00219 
00220    //Invoke user-defined callback, if any
00221    if(context->settings.unknownCommandCallback != NULL)
00222    {
00223       //Custom command processing
00224       error = context->settings.unknownCommandCallback(connection,
00225          connection->command, param);
00226    }
00227    else
00228    {
00229       //Report an error
00230       error = ERROR_INVALID_COMMAND;
00231    }
00232 
00233    //Invalid command received?
00234    if(error == ERROR_INVALID_COMMAND)
00235    {
00236       //Format response message
00237       strcpy(connection->response, "500 Command unrecognized\r\n");
00238    }
00239 }
00240 
00241 
00242 /**
00243  * @brief NOOP command processing
00244  *
00245  * The NOOP command does not affect any parameters or previously entered
00246  * commands. It specifies no action other than that the server send an OK reply
00247  *
00248  * @param[in] context Pointer to the FTP server context
00249  * @param[in] connection Pointer to the client connection
00250  * @param[in] param Command line parameters
00251  **/
00252 
00253 void ftpServerProcessNoop(FtpServerContext *context,
00254    FtpClientConnection *connection, char_t *param)
00255 {
00256    //Send an OK reply
00257    strcpy(connection->response, "200 Command okay\r\n");
00258 }
00259 
00260 
00261 /**
00262  * @brief SYST command processing
00263  *
00264  * The SYST command is used to find out the type of operating system
00265  * at the server side
00266  *
00267  * @param[in] context Pointer to the FTP server context
00268  * @param[in] connection Pointer to the client connection
00269  * @param[in] param Command line parameters
00270  **/
00271 
00272 void ftpServerProcessSyst(FtpServerContext *context,
00273    FtpClientConnection *connection, char_t *param)
00274 {
00275    //Format the response to the SYST command
00276    strcpy(connection->response, "215 UNIX Type: L8\r\n");
00277 }
00278 
00279 
00280 /**
00281  * @brief FEAT command processing
00282  *
00283  * The FEAT command allows a client to discover which optional
00284  * commands a server supports
00285  *
00286  * @param[in] context Pointer to the FTP server context
00287  * @param[in] connection Pointer to the client connection
00288  * @param[in] param Command line parameters
00289  **/
00290 
00291 void ftpServerProcessFeat(FtpServerContext *context,
00292    FtpClientConnection *connection, char_t *param)
00293 {
00294    //Format the response to the FEAT command
00295    strcpy(connection->response, "211-Features supported:\r\n");
00296    strcat(connection->response, " SIZE\r\n");
00297    strcat(connection->response, " EPRT\r\n");
00298    strcat(connection->response, " EPSV\r\n");
00299    strcat(connection->response, "211 End\r\n");
00300 }
00301 
00302 
00303 /**
00304  * @brief TYPE command processing
00305  *
00306  * The TYPE command specifies the representation type
00307  *
00308  * @param[in] context Pointer to the FTP server context
00309  * @param[in] connection Pointer to the client connection
00310  * @param[in] param Command line parameters
00311  **/
00312 
00313 void ftpServerProcessType(FtpServerContext *context,
00314    FtpClientConnection *connection, char_t *param)
00315 {
00316    //The argument specifies the representation type
00317    if(*param != '\0')
00318    {
00319       //ASCII type?
00320       if(!strcasecmp(param, "A"))
00321       {
00322          //Format the response to the TYPE command
00323          strcpy(connection->response, "200 Type set to A\r\n");
00324       }
00325       //Image type?
00326       else if(!strcasecmp(param, "I"))
00327       {
00328          //Format the response to the TYPE command
00329          strcpy(connection->response, "200 Type set to I\r\n");
00330       }
00331       //Unknown type?
00332       else
00333       {
00334          //Report an error
00335          strcpy(connection->response, "504 Unknown type\r\n");
00336       }
00337    }
00338    else
00339    {
00340       //The argument is missing...
00341       strcpy(connection->response, "501 Missing parameter\r\n");
00342    }
00343 }
00344 
00345 
00346 /**
00347  * @brief STRU command processing
00348  *
00349  * The STRU command specifies the file structure
00350  *
00351  * @param[in] context Pointer to the FTP server context
00352  * @param[in] connection Pointer to the client connection
00353  * @param[in] param Command line parameters
00354  **/
00355 
00356 void ftpServerProcessStru(FtpServerContext *context,
00357    FtpClientConnection *connection, char_t *param)
00358 {
00359    //The argument specifies the file structure
00360    if(*param != '\0')
00361    {
00362       //No record structure?
00363       if(!strcasecmp(param, "F"))
00364       {
00365          //Format the response to the STRU command
00366          strcpy(connection->response, "200 Structure set to F\r\n");
00367       }
00368       //Unknown file structure?
00369       else
00370       {
00371          //Report an error
00372          strcpy(connection->response, "504 Unknown structure\r\n");
00373       }
00374    }
00375    else
00376    {
00377       //The argument is missing...
00378       strcpy(connection->response, "501 Missing parameter\r\n");
00379    }
00380 }
00381 
00382 
00383 /**
00384  * @brief MODE command processing
00385  *
00386  * The MODE command specifies the data transfer mode
00387  *
00388  * @param[in] context Pointer to the FTP server context
00389  * @param[in] connection Pointer to the client connection
00390  * @param[in] param Command line parameters
00391  **/
00392 
00393 void ftpServerProcessMode(FtpServerContext *context,
00394    FtpClientConnection *connection, char_t *param)
00395 {
00396    //The argument specifies the data transfer mode
00397    if(*param != '\0')
00398    {
00399       //Stream mode?
00400       if(!strcasecmp(param, "S"))
00401       {
00402          //Format the response to the MODE command
00403          strcpy(connection->response, "200 Mode set to S\r\n");
00404       }
00405       //Unknown data transfer mode?
00406       else
00407       {
00408          //Report an error
00409          strcpy(connection->response, "504 Unknown mode\r\n");
00410       }
00411    }
00412    else
00413    {
00414       //The argument is missing...
00415       strcpy(connection->response, "501 Missing parameter\r\n");
00416    }
00417 }
00418 
00419 
00420 /**
00421  * @brief USER command processing
00422  *
00423  * The USER command is used to identify the user
00424  *
00425  * @param[in] context Pointer to the FTP server context
00426  * @param[in] connection Pointer to the client connection
00427  * @param[in] param Command line parameters
00428  **/
00429 
00430 void ftpServerProcessUser(FtpServerContext *context,
00431    FtpClientConnection *connection, char_t *param)
00432 {
00433    uint_t status;
00434 
00435    //The argument specifies the user name
00436    if(*param == '\0')
00437    {
00438       //The argument is missing...
00439       strcpy(connection->response, "501 Missing parameter\r\n");
00440       //Exit immediately
00441       return;
00442    }
00443 
00444    //Check the length of the user name
00445    if(strlen(param) > FTP_SERVER_MAX_USERNAME_LEN)
00446    {
00447       //The specified user name is not valid...
00448       strcpy(connection->response, "501 Invalid parameter\r\n");
00449       //Exit immediately
00450       return;
00451    }
00452 
00453    //Save user name
00454    strcpy(connection->user, param);
00455    //Log out the user
00456    connection->userLoggedIn = FALSE;
00457    //Set home directory
00458    strcpy(connection->homeDir, context->settings.rootDir);
00459    //Set current directory
00460    strcpy(connection->currentDir, context->settings.rootDir);
00461 
00462    //Invoke user-defined callback, if any
00463    if(context->settings.checkUserCallback != NULL)
00464       status = context->settings.checkUserCallback(connection, param);
00465    else
00466       status = FTP_ACCESS_ALLOWED;
00467 
00468    //Access allowed?
00469    if(status == FTP_ACCESS_ALLOWED)
00470    {
00471       //The user is now logged in
00472       connection->userLoggedIn = TRUE;
00473       //Format response message
00474       strcpy(connection->response, "230 User logged in, proceed\r\n");
00475    }
00476    //Password required?
00477    else if(status == FTP_PASSWORD_REQUIRED)
00478    {
00479       //This command must be immediately followed by a PASS command
00480       connection->controlState = FTP_CONTROL_STATE_USER;
00481       //Format response message
00482       strcpy(connection->response, "331 User name okay, need password\r\n");
00483    }
00484    //Access denied?
00485    else
00486    {
00487       //Format response message
00488       strcpy(connection->response, "530 Login authentication failed\r\n");
00489    }
00490 }
00491 
00492 
00493 /**
00494  * @brief PASS command processing
00495  *
00496  * The USER command specifies the user's password
00497  *
00498  * @param[in] context Pointer to the FTP server context
00499  * @param[in] connection Pointer to the client connection
00500  * @param[in] param Command line parameters
00501  **/
00502 
00503 void ftpServerProcessPass(FtpServerContext *context,
00504    FtpClientConnection *connection, char_t *param)
00505 {
00506    uint_t status;
00507 
00508    //This command must immediately follow a USER command
00509    if(connection->controlState != FTP_CONTROL_STATE_USER)
00510    {
00511       //Switch to idle state
00512       connection->controlState = FTP_CONTROL_STATE_IDLE;
00513       //Report an error
00514       strcpy(connection->response, "503 Bad sequence of commands\r\n");
00515       //Exit immediately
00516       return;
00517    }
00518 
00519    //Switch to idle state
00520    connection->controlState = FTP_CONTROL_STATE_IDLE;
00521 
00522    //The argument specifies the password
00523    if(*param == '\0')
00524    {
00525       //The argument is missing...
00526       strcpy(connection->response, "501 Missing parameter\r\n");
00527       //Exit immediately
00528       return;
00529    }
00530 
00531    //Invoke user-defined callback, if any
00532    if(context->settings.checkPasswordCallback != NULL)
00533       status = context->settings.checkPasswordCallback(connection, connection->user, param);
00534    else
00535       status = FTP_ACCESS_ALLOWED;
00536 
00537    //Access allowed?
00538    if(status == FTP_ACCESS_ALLOWED)
00539    {
00540       //The user is now logged in
00541       connection->userLoggedIn = TRUE;
00542       //Format response message
00543       strcpy(connection->response, "230 User logged in, proceed\r\n");
00544    }
00545    //Access denied?
00546    else
00547    {
00548       //Format response message
00549       strcpy(connection->response, "530 Login authentication failed\r\n");
00550    }
00551 }
00552 
00553 
00554 /**
00555  * @brief REIN command processing
00556  *
00557  * The REIN command is used to reinitialize a user session
00558  *
00559  * @param[in] context Pointer to the FTP server context
00560  * @param[in] connection Pointer to the client connection
00561  * @param[in] param Command line parameters
00562  **/
00563 
00564 void ftpServerProcessRein(FtpServerContext *context,
00565    FtpClientConnection *connection, char_t *param)
00566 {
00567    //Close data connection
00568    ftpServerCloseDataConnection(connection);
00569 
00570    //Release previously allocated resources
00571    if(connection->file != NULL)
00572    {
00573       fsCloseFile(connection->file);
00574       connection->file = NULL;
00575    }
00576    if(connection->dir != NULL)
00577    {
00578       fsCloseDir(connection->dir);
00579       connection->dir = NULL;
00580    }
00581 
00582    //Clear account information
00583    connection->userLoggedIn = FALSE;
00584 
00585    //Format response message
00586    strcpy(connection->response, "220 Service ready for new user\r\n");
00587 }
00588 
00589 
00590 /**
00591  * @brief QUIT command processing
00592  *
00593  * The QUIT command is used to terminate a user session
00594  *
00595  * @param[in] context Pointer to the FTP server context
00596  * @param[in] connection Pointer to the client connection
00597  * @param[in] param Command line parameters
00598  **/
00599 
00600 void ftpServerProcessQuit(FtpServerContext *context,
00601    FtpClientConnection *connection, char_t *param)
00602 {
00603    //There are two cases to consider upon receipt of this command
00604    if(connection->dataState == FTP_DATA_STATE_CLOSED)
00605    {
00606       //If the FTP service command was already completed, the server closes
00607       //the data connection (if it is open)...
00608       ftpServerCloseDataConnection(connection);
00609 
00610       //...and responds with a 221 reply
00611       strcpy(connection->response, "221 Service closing control connection\r\n");
00612    }
00613    else
00614    {
00615       //If the FTP service command is still in progress, the server aborts
00616       //the FTP service in progress and closes the data connection...
00617       ftpServerCloseDataConnection(connection);
00618 
00619       //...returning a 426 reply to indicate that the service request
00620       //terminated abnormally
00621       strcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
00622 
00623       //The server then sends a 221 reply
00624       strcat(connection->response, "221 Service closing control connection\r\n");
00625    }
00626 
00627    //Release previously allocated resources
00628    if(connection->file != NULL)
00629    {
00630       fsCloseFile(connection->file);
00631       connection->file = NULL;
00632    }
00633    if(connection->dir != NULL)
00634    {
00635       fsCloseDir(connection->dir);
00636       connection->dir = NULL;
00637    }
00638 
00639    //Clear account information
00640    connection->userLoggedIn = FALSE;
00641    //Gracefully disconnect from the remote host
00642    connection->controlState = FTP_CONTROL_STATE_WAIT_ACK;
00643 }
00644 
00645 
00646 /**
00647  * @brief PORT command processing
00648  *
00649  * The PORT command specifies the data port to be used for the data connection
00650  *
00651  * @param[in] context Pointer to the FTP server context
00652  * @param[in] connection Pointer to the client connection
00653  * @param[in] param Command line parameters
00654  **/
00655 
00656 void ftpServerProcessPort(FtpServerContext *context,
00657    FtpClientConnection *connection, char_t *param)
00658 {
00659    error_t error;
00660    size_t i;
00661    size_t j;
00662    char_t *p;
00663    char_t *token;
00664    char_t *end;
00665 
00666    //Ensure the user is logged in
00667    if(!connection->userLoggedIn)
00668    {
00669       //Format response message
00670       strcpy(connection->response, "530 Not logged in\r\n");
00671       //Exit immediately
00672       return;
00673    }
00674 
00675    //The argument is the concatenation of the IP address and the 16-bit port number
00676    if(*param == '\0')
00677    {
00678       //The argument is missing...
00679       strcpy(connection->response, "501 Missing parameter\r\n");
00680       //Exit immediately
00681       return;
00682    }
00683 
00684    //Close the data connection, if any
00685    ftpServerCloseDataConnection(connection);
00686 
00687    //Start of exception handling block
00688    do
00689    {
00690       //Assume an error condition...
00691       error = ERROR_INVALID_SYNTAX;
00692 
00693       //Parse the string
00694       for(i = 0, j = 1; param[i] != '\0'; i++)
00695       {
00696          //Change commas to dots
00697          if(param[i] == ',' && j < sizeof(Ipv4Addr))
00698          {
00699             param[i] = '.';
00700             j++;
00701          }
00702       }
00703 
00704       //Get the IP address to be used
00705       token = strtok_r(param, ",", &p);
00706       //Syntax error?
00707       if(token == NULL)
00708          break;
00709 
00710       //Convert the dot-decimal string to a binary IP address
00711       error = ipStringToAddr(token, &connection->remoteIpAddr);
00712       //Invalid IP address?
00713       if(error)
00714          break;
00715 
00716       //Assume an error condition...
00717       error = ERROR_INVALID_SYNTAX;
00718 
00719       //Get the most significant byte of the port number
00720       token = strtok_r(NULL, ",", &p);
00721       //Syntax error?
00722       if(token == NULL)
00723          break;
00724 
00725       //Convert the string representation to integer
00726       connection->remotePort = strtoul(token, &end, 10) << 8;
00727       //Syntax error?
00728       if(*end != '\0')
00729          break;
00730 
00731       //Get the least significant byte of the port number
00732       token = strtok_r(NULL, ",", &p);
00733       //Syntax error?
00734       if(token == NULL)
00735          break;
00736 
00737       //Convert the string representation to integer
00738       connection->remotePort |= strtoul(token, &end, 10) & 0xFF;
00739       //Syntax error?
00740       if(*end != '\0')
00741          break;
00742 
00743       //Successful processing
00744       error = NO_ERROR;
00745 
00746       //End of exception handling block
00747    } while(0);
00748 
00749    //Any error to report?
00750    if(error)
00751    {
00752       //Re initialize data connection
00753       connection->passiveMode = FALSE;
00754       connection->remotePort = 0;
00755 
00756       //Format response message
00757       strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
00758       //Exit immediately
00759       return;
00760    }
00761 
00762    //Successful processing
00763    strcpy(connection->response, "200 Command okay\r\n");
00764 }
00765 
00766 
00767 /**
00768  * @brief EPRT command processing
00769  *
00770  * The EPRT command allows for the specification of an extended address
00771  * for the data connection
00772  *
00773  * @param[in] context Pointer to the FTP server context
00774  * @param[in] connection Pointer to the client connection
00775  * @param[in] param Command line parameters
00776  **/
00777 
00778 void ftpServerProcessEprt(FtpServerContext *context,
00779    FtpClientConnection *connection, char_t *param)
00780 {
00781    error_t error;
00782    uint_t protocol;
00783    char_t *p;
00784    char_t *token;
00785    char_t *end;
00786    char_t delimiter[2];
00787 
00788    //Ensure the user is logged in
00789    if(!connection->userLoggedIn)
00790    {
00791       //Format response message
00792       strcpy(connection->response, "530 Not logged in\r\n");
00793       //Exit immediately
00794       return;
00795    }
00796 
00797    //The extended address must consist of the network protocol
00798    //as well as the IP address and the 16-bit port number
00799    if(*param == '\0')
00800    {
00801       //The argument is missing...
00802       strcpy(connection->response, "501 Missing parameter\r\n");
00803       //Exit immediately
00804       return;
00805    }
00806 
00807    //Close the data connection, if any
00808    ftpServerCloseDataConnection(connection);
00809 
00810    //Start of exception handling block
00811    do
00812    {
00813       //A delimiter character must be specified
00814       delimiter[0] = param[0];
00815       delimiter[1] = '\0';
00816       //Skip delimiter character
00817       param++;
00818 
00819       //Assume an error condition...
00820       error = ERROR_INVALID_SYNTAX;
00821 
00822       //Retrieve the network protocol to be used
00823       token = strtok_r(param, delimiter, &p);
00824       //Syntax error?
00825       if(token == NULL)
00826          break;
00827 
00828       //Convert the string representation to integer
00829       protocol = strtoul(token, &end, 10);
00830       //Syntax error?
00831       if(*end != '\0')
00832          break;
00833 
00834       //Get the IP address to be used
00835       token = strtok_r(NULL, delimiter, &p);
00836       //Syntax error?
00837       if(token == NULL)
00838          break;
00839 
00840 #if (IPV4_SUPPORT == ENABLED)
00841       //IPv4 address family?
00842       if(protocol == 1)
00843       {
00844          //IPv4 addresses are 4-byte long
00845          connection->remoteIpAddr.length = sizeof(Ipv4Addr);
00846          //Convert the string to IPv4 address
00847          error = ipv4StringToAddr(token, &connection->remoteIpAddr.ipv4Addr);
00848          //Invalid IP address?
00849          if(error)
00850             break;
00851       }
00852       else
00853 #endif
00854 #if (IPV6_SUPPORT == ENABLED)
00855       //IPv6 address family?
00856       if(protocol == 2)
00857       {
00858          //IPv6 addresses are 16-byte long
00859          connection->remoteIpAddr.length = sizeof(Ipv6Addr);
00860          //Convert the string to IPv6 address
00861          error = ipv6StringToAddr(token, &connection->remoteIpAddr.ipv6Addr);
00862          //Invalid IP address?
00863          if(error)
00864             break;
00865       }
00866       else
00867 #endif
00868       //Unknown address family?
00869       {
00870          //Report an error
00871          error = ERROR_INVALID_ADDRESS;
00872          //Exit immediately
00873          break;
00874       }
00875 
00876       //Assume an error condition...
00877       error = ERROR_INVALID_SYNTAX;
00878 
00879       //Get the port number to be used
00880       token = strtok_r(NULL, delimiter, &p);
00881       //Syntax error?
00882       if(token == NULL)
00883          break;
00884 
00885       //Convert the string representation to integer
00886       connection->remotePort = strtoul(token, &end, 10);
00887       //Syntax error?
00888       if(*end != '\0')
00889          break;
00890 
00891       //Successful processing
00892       error = NO_ERROR;
00893 
00894       //End of exception handling block
00895    } while(0);
00896 
00897    //Any error to report?
00898    if(error)
00899    {
00900       //Re initialize data connection
00901       connection->passiveMode = FALSE;
00902       connection->remotePort = 0;
00903 
00904       //Format response message
00905       strcpy(connection->response, "501 Syntax error in parameters or arguments\r\n");
00906       //Exit immediately
00907       return;
00908    }
00909 
00910    //Successful processing
00911    strcpy(connection->response, "200 Command okay\r\n");
00912 }
00913 
00914 
00915 /**
00916  * @brief PASV command processing
00917  *
00918  * The PASV command requests the server to listen on a data port and
00919  * to wait for a connection rather than initiate one upon receipt of
00920  * a transfer command
00921  *
00922  * @param[in] context Pointer to the FTP server context
00923  * @param[in] connection Pointer to the client connection
00924  * @param[in] param Command line parameters
00925  **/
00926 
00927 void ftpServerProcessPasv(FtpServerContext *context,
00928    FtpClientConnection *connection, char_t *param)
00929 {
00930    error_t error;
00931    size_t n;
00932    IpAddr ipAddr;
00933    uint16_t port;
00934 
00935    //Ensure the user is logged in
00936    if(!connection->userLoggedIn)
00937    {
00938       //Format response message
00939       strcpy(connection->response, "530 Not logged in\r\n");
00940       //Exit immediately
00941       return;
00942    }
00943 
00944    //Close the data connection, if any
00945    ftpServerCloseDataConnection(connection);
00946 
00947    //Get the next passive port number to be used
00948    port = ftpServerGetPassivePort(context);
00949 
00950    //Start of exception handling block
00951    do
00952    {
00953       //Open data socket
00954       connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
00955       //Failed to open socket?
00956       if(!connection->dataSocket)
00957       {
00958          //Report an error
00959          error = ERROR_OPEN_FAILED;
00960          //Exit immediately
00961          break;
00962       }
00963 
00964       //Force the socket to operate in non-blocking mode
00965       error = socketSetTimeout(connection->dataSocket, 0);
00966       //Any error to report?
00967       if(error)
00968          break;
00969 
00970       //Change the size of the TX buffer
00971       error = socketSetTxBufferSize(connection->dataSocket,
00972          FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
00973       //Any error to report?
00974       if(error)
00975          break;
00976 
00977       //Change the size of the RX buffer
00978       error = socketSetRxBufferSize(connection->dataSocket,
00979          FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
00980       //Any error to report?
00981       if(error)
00982          break;
00983 
00984       //Associate the socket with the relevant interface
00985       error = socketBindToInterface(connection->dataSocket, connection->interface);
00986       //Unable to bind the socket to the desired interface?
00987       if(error)
00988          break;
00989 
00990       //Bind the socket to the passive port number
00991       error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port);
00992       //Failed to bind the socket to the desired port?
00993       if(error)
00994          break;
00995 
00996       //Place the data socket in the listening state
00997       error = socketListen(connection->dataSocket, 1);
00998       //Any error to report?
00999       if(error)
01000          break;
01001 
01002       //Retrieve local IP address
01003       error = socketGetLocalAddr(connection->controlSocket, &ipAddr, NULL);
01004       //Any error to report?
01005       if(error)
01006          break;
01007 
01008       //The local IP address must be a valid IPv4 address
01009       if(ipAddr.length != sizeof(Ipv4Addr))
01010       {
01011          //PASV command cannot be used on IPv6 connections
01012          error = ERROR_INVALID_ADDRESS;
01013          //Exit immediately
01014          break;
01015       }
01016 
01017       //End of exception handling block
01018    } while(0);
01019 
01020    //Any error to report?
01021    if(error)
01022    {
01023       //Clean up side effects
01024       ftpServerCloseDataConnection(connection);
01025       //Format response message
01026       strcpy(connection->response, "425 Can't enter passive mode\r\n");
01027       //Exit immediately
01028       return;
01029    }
01030 
01031    //Use passive data transfer
01032    connection->passiveMode = TRUE;
01033    //Update data connection state
01034    connection->dataState = FTP_DATA_STATE_LISTEN;
01035 
01036 #if defined(FTP_SERVER_PASV_HOOK)
01037    FTP_SERVER_PASV_HOOK(connection, ipAddr);
01038 #endif
01039 
01040    //Format response message
01041    n = sprintf(connection->response, "227 Entering passive mode (");
01042    //Append host address
01043    ipAddrToString(&ipAddr, connection->response + n);
01044 
01045    //Parse the resulting string
01046    for(n = 0; connection->response[n] != '\0'; n++)
01047    {
01048       //Change dots to commas
01049       if(connection->response[n] == '.')
01050          connection->response[n] = ',';
01051    }
01052 
01053    //Append port number
01054    sprintf(connection->response + n, ",%" PRIu8 ",%" PRIu8 ")\r\n", MSB(port), LSB(port));
01055 }
01056 
01057 
01058 /**
01059  * @brief EPSV command processing
01060  *
01061  * The EPSV command requests that a server listen on a data port and
01062  * wait for a connection
01063  *
01064  * @param[in] context Pointer to the FTP server context
01065  * @param[in] connection Pointer to the client connection
01066  * @param[in] param Command line parameters
01067  **/
01068 
01069 void ftpServerProcessEpsv(FtpServerContext *context,
01070    FtpClientConnection *connection, char_t *param)
01071 {
01072    error_t error;
01073    uint16_t port;
01074 
01075    //Ensure the user is logged in
01076    if(!connection->userLoggedIn)
01077    {
01078       //Format response message
01079       strcpy(connection->response, "530 Not logged in\r\n");
01080       //Exit immediately
01081       return;
01082    }
01083 
01084    //Close the data connection, if any
01085    ftpServerCloseDataConnection(connection);
01086 
01087    //Get the next passive port number to be used
01088    port = ftpServerGetPassivePort(context);
01089 
01090    //Start of exception handling block
01091    do
01092    {
01093       //Open data socket
01094       connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
01095       //Failed to open socket?
01096       if(!connection->dataSocket)
01097       {
01098          //Report an error
01099          error = ERROR_OPEN_FAILED;
01100          //Exit immediately
01101          break;
01102       }
01103 
01104       //Force the socket to operate in non-blocking mode
01105       error = socketSetTimeout(connection->dataSocket, 0);
01106       //Any error to report?
01107       if(error)
01108          break;
01109 
01110       //Change the size of the TX buffer
01111       error = socketSetTxBufferSize(connection->dataSocket,
01112          FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
01113       //Any error to report?
01114       if(error)
01115          break;
01116 
01117       //Change the size of the RX buffer
01118       error = socketSetRxBufferSize(connection->dataSocket,
01119          FTP_SERVER_DATA_SOCKET_BUFFER_SIZE);
01120       //Any error to report?
01121       if(error)
01122          break;
01123 
01124       //Associate the socket with the relevant interface
01125       error = socketBindToInterface(connection->dataSocket, connection->interface);
01126       //Unable to bind the socket to the desired interface?
01127       if(error)
01128          break;
01129 
01130       //Bind the socket to the passive port number
01131       error = socketBind(connection->dataSocket, &IP_ADDR_ANY, port);
01132       //Failed to bind the socket to the desired port?
01133       if(error)
01134          break;
01135 
01136       //Place the data socket in the listening state
01137       error = socketListen(connection->dataSocket, 1);
01138       //Any error to report?
01139       if(error)
01140          break;
01141 
01142       //End of exception handling block
01143    } while(0);
01144 
01145    //Any error to report?
01146    if(error)
01147    {
01148       //Clean up side effects
01149       ftpServerCloseDataConnection(connection);
01150       //Format response message
01151       strcpy(connection->response, "425 Can't enter passive mode\r\n");
01152       //Exit immediately
01153       return;
01154    }
01155 
01156    //Use passive data transfer
01157    connection->passiveMode = TRUE;
01158    //Update data connection state
01159    connection->dataState = FTP_DATA_STATE_LISTEN;
01160 
01161    //The response code for entering passive mode using an extended address must be 229
01162    sprintf(connection->response, "229 Entering extended passive mode (|||%" PRIu16 "|)\r\n",
01163       port);
01164 }
01165 
01166 
01167 /**
01168  * @brief ABOR command processing
01169  *
01170  * The ABOR command tells the server to abort the previous FTP
01171  * service command and any associated transfer of data
01172  *
01173  * @param[in] context Pointer to the FTP server context
01174  * @param[in] connection Pointer to the client connection
01175  * @param[in] param Command line parameters
01176  **/
01177 
01178 void ftpServerProcessAbor(FtpServerContext *context,
01179    FtpClientConnection *connection, char_t *param)
01180 {
01181    //There are two cases to consider upon receipt of this command
01182    if(connection->dataState == FTP_DATA_STATE_CLOSED)
01183    {
01184       //If the FTP service command was already completed, the server closes
01185       //the data connection (if it is open)...
01186       ftpServerCloseDataConnection(connection);
01187 
01188       //...and responds with a 226 reply, indicating that the abort command
01189       //was successfully processed
01190       strcpy(connection->response, "226 Abort command successful\r\n");
01191    }
01192    else
01193    {
01194       //If the FTP service command is still in progress, the server aborts
01195       //the FTP service in progress and closes the data connection...
01196       ftpServerCloseDataConnection(connection);
01197 
01198       //...returning a 426 reply to indicate that the service request
01199       //terminated abnormally
01200       strcpy(connection->response, "426 Connection closed; transfer aborted\r\n");
01201 
01202       //The server then sends a 226 reply, indicating that the abort command
01203       //was successfully processed
01204       strcat(connection->response, "226 Abort command successful\r\n");
01205    }
01206 
01207    //Release previously allocated resources
01208    if(connection->file != NULL)
01209    {
01210       fsCloseFile(connection->file);
01211       connection->file = NULL;
01212    }
01213    if(connection->dir != NULL)
01214    {
01215       fsCloseDir(connection->dir);
01216       connection->dir = NULL;
01217    }
01218 }
01219 
01220 
01221 /**
01222  * @brief PWD command processing
01223  *
01224  * The PWD command causes the name of the current working
01225  * directory to be returned in the reply
01226  *
01227  * @param[in] context Pointer to the FTP server context
01228  * @param[in] connection Pointer to the client connection
01229  * @param[in] param Command line parameters
01230  **/
01231 
01232 void ftpServerProcessPwd(FtpServerContext *context,
01233    FtpClientConnection *connection, char_t *param)
01234 {
01235    //Ensure the user is logged in
01236    if(!connection->userLoggedIn)
01237    {
01238       //Format response message
01239       strcpy(connection->response, "530 Not logged in\r\n");
01240       //Exit immediately
01241       return;
01242    }
01243 
01244    //A successful PWD command uses the 257 reply code
01245    sprintf(connection->response, "257 \"%s\" is current directory\r\n",
01246       ftpServerStripHomeDir(connection, connection->currentDir));
01247 }
01248 
01249 
01250 /**
01251  * @brief CWD command processing
01252  *
01253  * The CWD command allows the user to work with a different
01254  * directory
01255  *
01256  * @param[in] context Pointer to the FTP server context
01257  * @param[in] connection Pointer to the client connection
01258  * @param[in] param Command line parameters
01259  **/
01260 
01261 void ftpServerProcessCwd(FtpServerContext *context,
01262    FtpClientConnection *connection, char_t *param)
01263 {
01264    error_t error;
01265    uint_t perm;
01266 
01267    //Ensure the user is logged in
01268    if(!connection->userLoggedIn)
01269    {
01270       //Format response message
01271       strcpy(connection->response, "530 Not logged in\r\n");
01272       //Exit immediately
01273       return;
01274    }
01275 
01276    //The argument specifies the pathname
01277    if(*param == '\0')
01278    {
01279       //The argument is missing...
01280       strcpy(connection->response, "501 Missing parameter\r\n");
01281       //Exit immediately
01282       return;
01283    }
01284 
01285    //Retrieve the full pathname
01286    error = ftpServerGetPath(connection, param,
01287       connection->path, FTP_SERVER_MAX_PATH_LEN);
01288 
01289    //Make sure the pathname is valid
01290    if(error)
01291    {
01292       //Report an error
01293       strcpy(connection->response, "501 Invalid parameter\r\n");
01294       //Exit immediately
01295       return;
01296    }
01297 
01298    //Retrieve permissions for the specified directory
01299    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01300 
01301    //Insufficient access rights?
01302    if(!(perm & FTP_FILE_PERM_READ))
01303    {
01304       //Report an error
01305       strcpy(connection->response, "550 Access denied\r\n");
01306       //Exit immediately
01307       return;
01308    }
01309 
01310    //Make sure the specified directory exists
01311    if(!fsDirExists(connection->path))
01312    {
01313       //Report an error
01314       strcpy(connection->response, "550 Directory not found\r\n");
01315       //Exit immediately
01316       return;
01317    }
01318 
01319    //Change current working directory
01320    strcpy(connection->currentDir, connection->path);
01321 
01322    //A successful PWD command uses the 250 reply code
01323    sprintf(connection->response, "250 Directory changed to %s\r\n",
01324       ftpServerStripHomeDir(connection, connection->currentDir));
01325 }
01326 
01327 
01328 /**
01329  * @brief CDUP command processing
01330  *
01331  * The CDUP command allows the user to change to the parent directory
01332  *
01333  * @param[in] context Pointer to the FTP server context
01334  * @param[in] connection Pointer to the client connection
01335  * @param[in] param Command line parameters
01336  **/
01337 
01338 void ftpServerProcessCdup(FtpServerContext *context,
01339    FtpClientConnection *connection, char_t *param)
01340 {
01341    uint_t perm;
01342 
01343    //Ensure the user is logged in
01344    if(!connection->userLoggedIn)
01345    {
01346       //Format response message
01347       strcpy(connection->response, "530 Not logged in\r\n");
01348       //Exit immediately
01349       return;
01350    }
01351 
01352    //Get current directory
01353    strcpy(connection->path, connection->currentDir);
01354 
01355    //Change to the parent directory
01356    pathCombine(connection->path, "..", FTP_SERVER_MAX_PATH_LEN);
01357    pathCanonicalize(connection->path);
01358 
01359    //Retrieve permissions for the directory
01360    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01361 
01362    //Check access rights
01363    if(perm & FTP_FILE_PERM_READ)
01364    {
01365       //Update current directory
01366       strcpy(connection->currentDir, connection->path);
01367    }
01368 
01369    //A successful PWD command uses the 250 reply code
01370    sprintf(connection->response, "250 Directory changed to %s\r\n",
01371       ftpServerStripHomeDir(connection, connection->currentDir));
01372 }
01373 
01374 
01375 /**
01376  * @brief LIST command processing
01377  *
01378  * The LIST command is used to list the content of a directory
01379  *
01380  * @param[in] context Pointer to the FTP server context
01381  * @param[in] connection Pointer to the client connection
01382  * @param[in] param Command line parameters
01383  **/
01384 
01385 void ftpServerProcessList(FtpServerContext *context,
01386    FtpClientConnection *connection, char_t *param)
01387 {
01388    error_t error;
01389    uint_t perm;
01390 
01391    //Ensure the user is logged in
01392    if(!connection->userLoggedIn)
01393    {
01394       //Format response message
01395       strcpy(connection->response, "530 Not logged in\r\n");
01396       //Exit immediately
01397       return;
01398    }
01399 
01400    //Any option flags
01401    while(*param == '-')
01402    {
01403       //Skip option flags
01404       while(*param != ' ' && *param != '\0')
01405          param++;
01406       //Skip whitespace characters
01407       while(*param == ' ')
01408          param++;
01409    }
01410 
01411    //The pathname is optional
01412    if(*param == '\0')
01413    {
01414       //Use current directory if no pathname is specified
01415       strcpy(connection->path, connection->currentDir);
01416    }
01417    else
01418    {
01419       //Retrieve the full pathname
01420       error = ftpServerGetPath(connection, param,
01421          connection->path, FTP_SERVER_MAX_PATH_LEN);
01422 
01423       //Any error to report?
01424       if(error)
01425       {
01426          //The specified pathname is not valid...
01427          strcpy(connection->response, "501 Invalid parameter\r\n");
01428          //Exit immediately
01429          return;
01430       }
01431    }
01432 
01433    //Retrieve permissions for the specified directory
01434    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01435 
01436    //Insufficient access rights?
01437    if(!(perm & FTP_FILE_PERM_READ))
01438    {
01439       //Report an error
01440       strcpy(connection->response, "550 Access denied\r\n");
01441       //Exit immediately
01442       return;
01443    }
01444 
01445    //Open specified directory for reading
01446    connection->dir = fsOpenDir(connection->path);
01447 
01448    //Failed to open the directory?
01449    if(!connection->dir)
01450    {
01451       //Report an error
01452       strcpy(connection->response, "550 Directory not found\r\n");
01453       //Exit immediately
01454       return;
01455    }
01456 
01457    //Check current data transfer mode
01458    if(connection->passiveMode)
01459    {
01460       //Check whether the data connection is already opened
01461       if(connection->dataState == FTP_DATA_STATE_IDLE)
01462          connection->dataState = FTP_DATA_STATE_SEND;
01463    }
01464    else
01465    {
01466       //Open the data connection
01467       error = ftpServerOpenDataConnection(context, connection);
01468 
01469       //Any error to report?
01470       if(error)
01471       {
01472          //Clean up side effects
01473          fsCloseDir(connection->dir);
01474          //Format response
01475          strcpy(connection->response, "450 Can't open data connection\r\n");
01476          //Exit immediately
01477          return;
01478       }
01479 
01480       //The data connection is ready to send data
01481       connection->dataState = FTP_DATA_STATE_SEND;
01482    }
01483 
01484    //Flush transmission buffer
01485    connection->bufferLength = 0;
01486    connection->bufferPos = 0;
01487 
01488    //LIST command is being processed
01489    connection->controlState = FTP_CONTROL_STATE_LIST;
01490 
01491    //Format response message
01492    strcpy(connection->response, "150 Opening data connection\r\n");
01493 }
01494 
01495 
01496 /**
01497  * @brief MKD command processing
01498  *
01499  * The MKD command causes the directory specified in the pathname
01500  * to be created as a directory
01501  *
01502  * @param[in] context Pointer to the FTP server context
01503  * @param[in] connection Pointer to the client connection
01504  * @param[in] param Command line parameters
01505  **/
01506 
01507 void ftpServerProcessMkd(FtpServerContext *context,
01508    FtpClientConnection *connection, char_t *param)
01509 {
01510    error_t error;
01511    uint_t perm;
01512 
01513    //Ensure the user is logged in
01514    if(!connection->userLoggedIn)
01515    {
01516       //Format response message
01517       strcpy(connection->response, "530 Not logged in\r\n");
01518       //Exit immediately
01519       return;
01520    }
01521 
01522    //The argument specifies the pathname
01523    if(*param == '\0')
01524    {
01525       //The argument is missing...
01526       strcpy(connection->response, "501 Missing parameter\r\n");
01527       //Exit immediately
01528       return;
01529    }
01530 
01531    //Retrieve the full pathname
01532    error = ftpServerGetPath(connection, param,
01533       connection->path, FTP_SERVER_MAX_PATH_LEN);
01534 
01535    //Any error to report?
01536    if(error)
01537    {
01538       //The specified pathname is not valid...
01539       strcpy(connection->response, "501 Invalid parameter\r\n");
01540       //Exit immediately
01541       return;
01542    }
01543 
01544    //Retrieve permissions for the specified directory
01545    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01546 
01547    //Insufficient access rights?
01548    if(!(perm & FTP_FILE_PERM_WRITE))
01549    {
01550       //Report an error
01551       strcpy(connection->response, "550 Access denied\r\n");
01552       //Exit immediately
01553       return;
01554    }
01555 
01556    //Create the specified directory
01557    error = fsCreateDir(connection->path);
01558 
01559    //Any error to report?
01560    if(error)
01561    {
01562       //The specified pathname is not valid...
01563       strcpy(connection->response, "550 Can't create directory\r\n");
01564       //Exit immediately
01565       return;
01566    }
01567 
01568    //The specified directory was successfully created
01569    sprintf(connection->response, "257 \"%s\" created\r\n",
01570       ftpServerStripHomeDir(connection, connection->path));
01571 }
01572 
01573 
01574 /**
01575  * @brief RMD command processing
01576  *
01577  * The RMD command causes the directory specified in the pathname
01578  * to be removed
01579  *
01580  * @param[in] context Pointer to the FTP server context
01581  * @param[in] connection Pointer to the client connection
01582  * @param[in] param Command line parameters
01583  **/
01584 
01585 void ftpServerProcessRmd(FtpServerContext *context,
01586    FtpClientConnection *connection, char_t *param)
01587 {
01588    error_t error;
01589    uint_t perm;
01590 
01591    //Ensure the user is logged in
01592    if(!connection->userLoggedIn)
01593    {
01594       //Format response message
01595       strcpy(connection->response, "530 Not logged in\r\n");
01596       //Exit immediately
01597       return;
01598    }
01599 
01600    //The argument specifies the directory to be removed
01601    if(*param == '\0')
01602    {
01603       //The argument is missing...
01604       strcpy(connection->response, "501 Missing parameter\r\n");
01605       //Exit immediately
01606       return;
01607    }
01608 
01609    //Retrieve the full pathname of the directory
01610    error = ftpServerGetPath(connection, param,
01611       connection->path, FTP_SERVER_MAX_PATH_LEN);
01612 
01613    //Any error to report?
01614    if(error)
01615    {
01616       //The specified pathname is not valid...
01617       strcpy(connection->response, "501 Invalid parameter\r\n");
01618       //Exit immediately
01619       return;
01620    }
01621 
01622    //Retrieve permissions for the specified directory
01623    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01624 
01625    //Insufficient access rights?
01626    if(!(perm & FTP_FILE_PERM_WRITE))
01627    {
01628       //Report an error
01629       strcpy(connection->response, "550 Access denied\r\n");
01630       //Exit immediately
01631       return;
01632    }
01633 
01634    //Remove the specified directory
01635    error = fsRemoveDir(connection->path);
01636 
01637    //Any error to report?
01638    if(error)
01639    {
01640       //The specified directory cannot be deleted...
01641       strcpy(connection->response, "550 Can't remove directory\r\n");
01642       //Exit immediately
01643       return;
01644    }
01645 
01646    //The specified directory was successfully removed
01647    strcpy(connection->response, "250 Directory removed\r\n");
01648 }
01649 
01650 
01651 /**
01652  * @brief SIZE command processing
01653  *
01654  * The SIZE command is used to obtain the transfer size of the specified file
01655  *
01656  * @param[in] context Pointer to the FTP server context
01657  * @param[in] connection Pointer to the client connection
01658  * @param[in] param Command line parameters
01659  **/
01660 
01661 void ftpServerProcessSize(FtpServerContext *context,
01662    FtpClientConnection *connection, char_t *param)
01663 {
01664    error_t error;
01665    uint_t perm;
01666    uint32_t size;
01667 
01668    //Ensure the user is logged in
01669    if(!connection->userLoggedIn)
01670    {
01671       //Format response message
01672       strcpy(connection->response, "530 Not logged in\r\n");
01673       //Exit immediately
01674       return;
01675    }
01676 
01677    //The argument specifies the pathname of the file
01678    if(*param == '\0')
01679    {
01680       //The argument is missing...
01681       strcpy(connection->response, "501 Missing parameter\r\n");
01682       //Exit immediately
01683       return;
01684    }
01685 
01686    //Retrieve the full pathname
01687    error = ftpServerGetPath(connection, param,
01688       connection->path, FTP_SERVER_MAX_PATH_LEN);
01689 
01690    //Any error to report?
01691    if(error)
01692    {
01693       //The specified pathname is not valid...
01694       strcpy(connection->response, "501 Invalid parameter\r\n");
01695       //Exit immediately
01696       return;
01697    }
01698 
01699    //Retrieve permissions for the specified directory
01700    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01701 
01702    //Insufficient access rights?
01703    if(!(perm & FTP_FILE_PERM_LIST) && !(perm & FTP_FILE_PERM_READ))
01704    {
01705       //Report an error
01706       strcpy(connection->response, "550 Access denied\r\n");
01707       //Exit immediately
01708       return;
01709    }
01710 
01711    //Retrieve the size of the specified file
01712    error = fsGetFileSize(connection->path, &size);
01713 
01714    //Any error to report?
01715    if(error)
01716    {
01717       //Report an error
01718       strcpy(connection->response, "550 File not found\r\n");
01719       //Exit immediately
01720       return;
01721    }
01722 
01723    //Format response message
01724    sprintf(connection->response, "213 %" PRIu32 "\r\n", size);
01725 }
01726 
01727 
01728 /**
01729  * @brief RETR command processing
01730  *
01731  * The RETR command is used to retrieve the content of the specified file
01732  *
01733  * @param[in] context Pointer to the FTP server context
01734  * @param[in] connection Pointer to the client connection
01735  * @param[in] param Command line parameters
01736  **/
01737 
01738 void ftpServerProcessRetr(FtpServerContext *context,
01739    FtpClientConnection *connection, char_t *param)
01740 {
01741    error_t error;
01742    uint_t perm;
01743 
01744    //Ensure the user is logged in
01745    if(!connection->userLoggedIn)
01746    {
01747       //Format response message
01748       strcpy(connection->response, "530 Not logged in\r\n");
01749       //Exit immediately
01750       return;
01751    }
01752 
01753    //The argument specifies the pathname of the file to read
01754    if(*param == '\0')
01755    {
01756       //The argument is missing...
01757       strcpy(connection->response, "501 Missing parameter\r\n");
01758       //Exit immediately
01759       return;
01760    }
01761 
01762    //Retrieve the full pathname
01763    error = ftpServerGetPath(connection, param,
01764       connection->path, FTP_SERVER_MAX_PATH_LEN);
01765 
01766    //Any error to report?
01767    if(error)
01768    {
01769       //The specified pathname is not valid...
01770       strcpy(connection->response, "501 Invalid parameter\r\n");
01771       //Exit immediately
01772       return;
01773    }
01774 
01775    //Retrieve permissions for the specified directory
01776    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01777 
01778    //Insufficient access rights?
01779    if(!(perm & FTP_FILE_PERM_READ))
01780    {
01781       //Report an error
01782       strcpy(connection->response, "550 Access denied\r\n");
01783       //Exit immediately
01784       return;
01785    }
01786 
01787    //Open specified file for reading
01788    connection->file = fsOpenFile(connection->path, FS_FILE_MODE_READ);
01789 
01790    //Failed to open the file?
01791    if(!connection->file)
01792    {
01793       //Report an error
01794       strcpy(connection->response, "550 File not found\r\n");
01795       //Exit immediately
01796       return;
01797    }
01798 
01799    //Check current data transfer mode
01800    if(connection->passiveMode)
01801    {
01802       //Check whether the data connection is already opened
01803       if(connection->dataState == FTP_DATA_STATE_IDLE)
01804          connection->dataState = FTP_DATA_STATE_SEND;
01805    }
01806    else
01807    {
01808       //Open the data connection
01809       error = ftpServerOpenDataConnection(context, connection);
01810 
01811       //Any error to report?
01812       if(error)
01813       {
01814          //Clean up side effects
01815          fsCloseFile(connection->file);
01816          //Format response
01817          strcpy(connection->response, "450 Can't open data connection\r\n");
01818          //Exit immediately
01819          return;
01820       }
01821 
01822       //The data connection is ready to send data
01823       connection->dataState = FTP_DATA_STATE_SEND;
01824    }
01825 
01826    //Flush transmission buffer
01827    connection->bufferLength = 0;
01828    connection->bufferPos = 0;
01829 
01830    //RETR command is being processed
01831    connection->controlState = FTP_CONTROL_STATE_RETR;
01832 
01833    //Format response message
01834    strcpy(connection->response, "150 Opening data connection\r\n");
01835 }
01836 
01837 
01838 /**
01839  * @brief STOR command processing
01840  *
01841  * The STOR command is used to store data to the specified file
01842  *
01843  * @param[in] context Pointer to the FTP server context
01844  * @param[in] connection Pointer to the client connection
01845  * @param[in] param Command line parameters
01846  **/
01847 
01848 void ftpServerProcessStor(FtpServerContext *context,
01849    FtpClientConnection *connection, char_t *param)
01850 {
01851    error_t error;
01852    uint_t perm;
01853 
01854    //Ensure the user is logged in
01855    if(!connection->userLoggedIn)
01856    {
01857       //Format response message
01858       strcpy(connection->response, "530 Not logged in\r\n");
01859       //Exit immediately
01860       return;
01861    }
01862 
01863    //The argument specifies the pathname of the file to written
01864    if(*param == '\0')
01865    {
01866       //The argument is missing...
01867       strcpy(connection->response, "501 Missing parameter\r\n");
01868       //Exit immediately
01869       return;
01870    }
01871 
01872    //Retrieve the full pathname
01873    error = ftpServerGetPath(connection, param,
01874       connection->path, FTP_SERVER_MAX_PATH_LEN);
01875 
01876    //Any error to report?
01877    if(error)
01878    {
01879       //The specified pathname is not valid...
01880       strcpy(connection->response, "501 Invalid parameter\r\n");
01881       //Exit immediately
01882       return;
01883    }
01884 
01885    //Retrieve permissions for the specified directory
01886    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01887 
01888    //Insufficient access rights?
01889    if(!(perm & FTP_FILE_PERM_WRITE))
01890    {
01891       //Report an error
01892       strcpy(connection->response, "550 Access denied\r\n");
01893       //Exit immediately
01894       return;
01895    }
01896 
01897    //Open specified file for writing
01898    connection->file = fsOpenFile(connection->path,
01899       FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE | FS_FILE_MODE_TRUNC);
01900 
01901    //Failed to open the file?
01902    if(!connection->file)
01903    {
01904       //Report an error
01905       strcpy(connection->response, "550 File not found\r\n");
01906       //Exit immediately
01907       return;
01908    }
01909 
01910    //Check current data transfer mode
01911    if(connection->passiveMode)
01912    {
01913       //Check whether the data connection is already opened
01914       if(connection->dataState == FTP_DATA_STATE_IDLE)
01915          connection->dataState = FTP_DATA_STATE_RECEIVE;
01916    }
01917    else
01918    {
01919       //Open the data connection
01920       error = ftpServerOpenDataConnection(context, connection);
01921 
01922       //Any error to report?
01923       if(error)
01924       {
01925          //Clean up side effects
01926          fsCloseFile(connection->file);
01927          //Format response
01928          strcpy(connection->response, "450 Can't open data connection\r\n");
01929          //Exit immediately
01930          return;
01931       }
01932 
01933       //The data connection is ready to receive data
01934       connection->dataState = FTP_DATA_STATE_RECEIVE;
01935    }
01936 
01937    //Flush reception buffer
01938    connection->bufferLength = 0;
01939    connection->bufferPos = 0;
01940 
01941    //STOR command is being processed
01942    connection->controlState = FTP_CONTROL_STATE_STOR;
01943 
01944    //Format response message
01945    strcpy(connection->response, "150 Opening data connection\r\n");
01946 }
01947 
01948 
01949 /**
01950  * @brief APPE command processing
01951  *
01952  * The APPE command is used to append data to the specified file
01953  *
01954  * @param[in] context Pointer to the FTP server context
01955  * @param[in] connection Pointer to the client connection
01956  * @param[in] param Command line parameters
01957  **/
01958 
01959 void ftpServerProcessAppe(FtpServerContext *context,
01960    FtpClientConnection *connection, char_t *param)
01961 {
01962    error_t error;
01963    uint_t perm;
01964 
01965    //Ensure the user is logged in
01966    if(!connection->userLoggedIn)
01967    {
01968       //Format response message
01969       strcpy(connection->response, "530 Not logged in\r\n");
01970       //Exit immediately
01971       return;
01972    }
01973 
01974    //The argument specifies the pathname of the file to written
01975    if(*param == '\0')
01976    {
01977       //The argument is missing...
01978       strcpy(connection->response, "501 Missing parameter\r\n");
01979       //Exit immediately
01980       return;
01981    }
01982 
01983    //Retrieve the full pathname
01984    error = ftpServerGetPath(connection, param,
01985       connection->path, FTP_SERVER_MAX_PATH_LEN);
01986 
01987    //Any error to report?
01988    if(error)
01989    {
01990       //The specified pathname is not valid...
01991       strcpy(connection->response, "501 Invalid parameter\r\n");
01992       //Exit immediately
01993       return;
01994    }
01995 
01996    //Retrieve permissions for the specified directory
01997    perm = ftpServerGetFilePermissions(context, connection, connection->path);
01998 
01999    //Insufficient access rights?
02000    if(!(perm & FTP_FILE_PERM_WRITE))
02001    {
02002       //Report an error
02003       strcpy(connection->response, "550 Access denied\r\n");
02004       //Exit immediately
02005       return;
02006    }
02007 
02008    //Open specified file for writing
02009    connection->file = fsOpenFile(connection->path,
02010       FS_FILE_MODE_WRITE | FS_FILE_MODE_CREATE);
02011 
02012    //Failed to open the file?
02013    if(!connection->file)
02014    {
02015       //Report an error
02016       strcpy(connection->response, "550 File not found\r\n");
02017       //Exit immediately
02018       return;
02019    }
02020 
02021    //Move to the end of the file
02022    error = fsSeekFile(connection->file, 0, FS_SEEK_END);
02023 
02024    //Any error to report?
02025    if(error)
02026    {
02027       //Clean up side effects
02028       fsCloseFile(connection->file);
02029       //Format response
02030       strcpy(connection->response, "550 File unavailable\r\n");
02031    }
02032 
02033    //Check current data transfer mode
02034    if(connection->passiveMode)
02035    {
02036       //Check whether the data connection is already opened
02037       if(connection->dataState == FTP_DATA_STATE_IDLE)
02038          connection->dataState = FTP_DATA_STATE_RECEIVE;
02039    }
02040    else
02041    {
02042       //Open the data connection
02043       error = ftpServerOpenDataConnection(context, connection);
02044 
02045       //Any error to report?
02046       if(error)
02047       {
02048          //Clean up side effects
02049          fsCloseFile(connection->file);
02050          //Format response
02051          strcpy(connection->response, "450 Can't open data connection\r\n");
02052          //Exit immediately
02053          return;
02054       }
02055 
02056       //The data connection is ready to receive data
02057       connection->dataState = FTP_DATA_STATE_RECEIVE;
02058    }
02059 
02060    //Flush reception buffer
02061    connection->bufferLength = 0;
02062    connection->bufferPos = 0;
02063 
02064    //APPE command is being processed
02065    connection->controlState = FTP_CONTROL_STATE_APPE;
02066 
02067    //Format response message
02068    strcpy(connection->response, "150 Opening data connection\r\n");
02069 }
02070 
02071 
02072 /**
02073  * @brief RNFR command processing
02074  *
02075  * The RNFR command specifies the old pathname of the file which is
02076  * to be renamed
02077  *
02078  * @param[in] context Pointer to the FTP server context
02079  * @param[in] connection Pointer to the client connection
02080  * @param[in] param Command line parameters
02081  **/
02082 
02083 void ftpServerProcessRnfr(FtpServerContext *context,
02084    FtpClientConnection *connection, char_t *param)
02085 {
02086    error_t error;
02087    uint_t perm;
02088 
02089    //Ensure the user is logged in
02090    if(!connection->userLoggedIn)
02091    {
02092       //Format response message
02093       strcpy(connection->response, "530 Not logged in\r\n");
02094       //Exit immediately
02095       return;
02096    }
02097 
02098    //The argument specifies the file to be renamed
02099    if(*param == '\0')
02100    {
02101       //The argument is missing...
02102       strcpy(connection->response, "501 Missing parameter\r\n");
02103       //Exit immediately
02104       return;
02105    }
02106 
02107    //Retrieve the full pathname
02108    error = ftpServerGetPath(connection, param,
02109       connection->path, FTP_SERVER_MAX_PATH_LEN);
02110 
02111    //Any error to report?
02112    if(error)
02113    {
02114       //The specified pathname is not valid...
02115       strcpy(connection->response, "501 Invalid parameter\r\n");
02116       //Exit immediately
02117       return;
02118    }
02119 
02120    //Retrieve permissions for the specified directory
02121    perm = ftpServerGetFilePermissions(context, connection, connection->path);
02122 
02123    //Insufficient access rights?
02124    if(!(perm & FTP_FILE_PERM_WRITE))
02125    {
02126       //Report an error
02127       strcpy(connection->response, "550 Access denied\r\n");
02128       //Exit immediately
02129       return;
02130    }
02131 
02132    //Make sure the file exists
02133    if(!fsFileExists(connection->path) && !fsDirExists(connection->path))
02134    {
02135       //No such file or directory...
02136       strcpy(connection->response, "550 File not found\r\n");
02137       //Exit immediately
02138       return;
02139    }
02140 
02141    //This command must be immediately followed by a RNTO command
02142    connection->controlState = FTP_CONTROL_STATE_RNFR;
02143    //Format the response message
02144    strcpy(connection->response, "350 File exists, ready for destination name\r\n");
02145 }
02146 
02147 
02148 /**
02149  * @brief RNTO command processing
02150  *
02151  * The RNTO command specifies the new pathname of the file specified
02152  * in the immediately preceding RNFR command
02153  *
02154  * @param[in] context Pointer to the FTP server context
02155  * @param[in] connection Pointer to the client connection
02156  * @param[in] param Command line parameters
02157  **/
02158 
02159 void ftpServerProcessRnto(FtpServerContext *context,
02160    FtpClientConnection *connection, char_t *param)
02161 {
02162    error_t error;
02163    uint_t perm;
02164    char_t newPath[FTP_SERVER_MAX_PATH_LEN];
02165 
02166    //Ensure the user is logged in
02167    if(!connection->userLoggedIn)
02168    {
02169       //Format response message
02170       strcpy(connection->response, "530 Not logged in\r\n");
02171       //Exit immediately
02172       return;
02173    }
02174 
02175    //This command must immediately follow a RNFR command
02176    if(connection->controlState != FTP_CONTROL_STATE_RNFR)
02177    {
02178       //Switch to idle state
02179       connection->controlState = FTP_CONTROL_STATE_IDLE;
02180       //Report an error
02181       strcpy(connection->response, "503 Bad sequence of commands\r\n");
02182       //Exit immediately
02183       return;
02184    }
02185 
02186    //Switch to idle state
02187    connection->controlState = FTP_CONTROL_STATE_IDLE;
02188 
02189    //The argument specifies the new pathname
02190    if(*param == '\0')
02191    {
02192       //The argument is missing...
02193       strcpy(connection->response, "501 Missing parameter\r\n");
02194       //Exit immediately
02195       return;
02196    }
02197 
02198    //Retrieve the full pathname
02199    error = ftpServerGetPath(connection, param,
02200       newPath, FTP_SERVER_MAX_PATH_LEN);
02201 
02202    //Any error to report?
02203    if(error)
02204    {
02205       //The specified pathname is not valid...
02206       strcpy(connection->response, "501 Invalid parameter\r\n");
02207       //Exit immediately
02208       return;
02209    }
02210 
02211    //Retrieve permissions for the specified directory
02212    perm = ftpServerGetFilePermissions(context, connection, newPath);
02213 
02214    //Insufficient access rights?
02215    if(!(perm & FTP_FILE_PERM_WRITE))
02216    {
02217       //Report an error
02218       strcpy(connection->response, "550 Access denied\r\n");
02219       //Exit immediately
02220       return;
02221    }
02222 
02223    //Check whether the file name already exists
02224    if(fsFileExists(newPath) || fsDirExists(newPath))
02225    {
02226       //Report an error
02227       strcpy(connection->response, "550 File already exists\r\n");
02228       //Exit immediately
02229       return;
02230    }
02231 
02232    //Rename the specified file
02233    error = fsRenameFile(connection->path, newPath);
02234 
02235    //Any error to report?
02236    if(error)
02237    {
02238       //The specified file cannot be renamed
02239       strcpy(connection->response, "550 Can't rename file\r\n");
02240       //Exit immediately
02241       return;
02242    }
02243 
02244    //The specified file was successfully deleted
02245    strcpy(connection->response, "250 File renamed\r\n");
02246 }
02247 
02248 
02249 /**
02250  * @brief DELE command processing
02251  *
02252  * The DELE command causes the file specified in the pathname to be
02253  * deleted at the server site
02254  *
02255  * @param[in] context Pointer to the FTP server context
02256  * @param[in] connection Pointer to the client connection
02257  * @param[in] param Command line parameters
02258  **/
02259 
02260 void ftpServerProcessDele(FtpServerContext *context,
02261    FtpClientConnection *connection, char_t *param)
02262 {
02263    error_t error;
02264    uint_t perm;
02265 
02266    //Ensure the user is logged in
02267    if(!connection->userLoggedIn)
02268    {
02269       //Format response message
02270       strcpy(connection->response, "530 Not logged in\r\n");
02271       //Exit immediately
02272       return;
02273    }
02274 
02275    //The argument specifies the file to be deleted
02276    if(*param == '\0')
02277    {
02278       //The argument is missing...
02279       strcpy(connection->response, "501 Missing parameter\r\n");
02280       //Exit immediately
02281       return;
02282    }
02283 
02284    //Retrieve the full pathname of the file
02285    error = ftpServerGetPath(connection, param,
02286       connection->path, FTP_SERVER_MAX_PATH_LEN);
02287 
02288    //Any error to report?
02289    if(error)
02290    {
02291       //The specified pathname is not valid...
02292       strcpy(connection->response, "501 Invalid parameter\r\n");
02293       //Exit immediately
02294       return;
02295    }
02296 
02297    //Retrieve permissions for the specified directory
02298    perm = ftpServerGetFilePermissions(context, connection, connection->path);
02299 
02300    //Insufficient access rights?
02301    if(!(perm & FTP_FILE_PERM_WRITE))
02302    {
02303       //Report an error
02304       strcpy(connection->response, "550 Access denied\r\n");
02305       //Exit immediately
02306       return;
02307    }
02308 
02309    //Delete the specified file
02310    error = fsDeleteFile(connection->path);
02311 
02312    //Any error to report?
02313    if(error)
02314    {
02315       //The specified file cannot be deleted...
02316       strcpy(connection->response, "550 Can't delete file\r\n");
02317       //Exit immediately
02318       return;
02319    }
02320 
02321    //The specified file was successfully deleted
02322    strcpy(connection->response, "250 File deleted\r\n");
02323 }
02324 
02325 #endif
02326