Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ftp_server_events.c Source File

ftp_server_events.c

Go to the documentation of this file.
00001 /**
00002  * @file ftp_server_events.c
00003  * @brief FTP server (event handlers)
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 "ftp/ftp_server.h"
00034 #include "ftp/ftp_server_events.h"
00035 #include "ftp/ftp_server_commands.h"
00036 #include "ftp/ftp_server_misc.h"
00037 #include "str.h"
00038 #include "path.h"
00039 #include "error.h"
00040 #include "debug.h"
00041 
00042 //Check TCP/IP stack configuration
00043 #if (FTP_SERVER_SUPPORT == ENABLED)
00044 
00045 //Abbreviated months
00046 static const char months[13][4] =
00047 {
00048    "   ",
00049    "Jan",
00050    "Feb",
00051    "Mar",
00052    "Apr",
00053    "May",
00054    "Jun",
00055    "Jul",
00056    "Aug",
00057    "Sep",
00058    "Oct",
00059    "Nov",
00060    "Dec"
00061 };
00062 
00063 
00064 /**
00065  * @brief Control connection event handler
00066  * @param[in] context Pointer to the FTP server context
00067  * @param[in] connection Pointer to the client connection
00068  * @param[in] eventFlags Event to be processed
00069  **/
00070 
00071 void ftpServerControlEventHandler(FtpServerContext *context,
00072    FtpClientConnection * connection, uint_t eventFlags)
00073 {
00074    error_t error;
00075    size_t n;
00076 
00077    //Send buffer is available for writing?
00078    if(eventFlags == SOCKET_EVENT_TX_READY)
00079    {
00080       //Send data back to the client
00081       error = socketSend(connection->controlSocket, connection->response +
00082          connection->responsePos, connection->responseLength, &n, 0);
00083 
00084       //Failed to send data?
00085       if(error != NO_ERROR && error != ERROR_TIMEOUT)
00086       {
00087          //Close connection with the client
00088          ftpServerCloseConnection(context, connection);
00089          //Exit immediately
00090          return;
00091       }
00092 
00093       //Advance data pointer
00094       connection->responsePos += n;
00095       //Number of bytes still available in the response buffer
00096       connection->responseLength -= n;
00097    }
00098    //Data is pending in the receive buffer?
00099    else if(eventFlags == SOCKET_EVENT_RX_READY)
00100    {
00101       //Read data from the client
00102       error = socketReceive(connection->controlSocket,
00103          connection->command + connection->commandLength,
00104          FTP_SERVER_MAX_LINE_LEN - connection->commandLength, &n, 0);
00105 
00106       //Failed to receive data?
00107       if(error == ERROR_END_OF_STREAM)
00108       {
00109          //Gracefully disconnect from the remote host
00110          connection->controlState = FTP_CONTROL_STATE_WAIT_ACK;
00111          //Exit immediately
00112          return;
00113       }
00114       else if(error)
00115       {
00116          //Close connection with the client
00117          ftpServerCloseConnection(context, connection);
00118          //Exit immediately
00119          return;
00120       }
00121 
00122       //Number of bytes available in the command buffer
00123       connection->commandLength += n;
00124       //Process incoming command
00125       ftpServerProcessCmd(context, connection);
00126    }
00127    //Data are transmitted and acknowledged?
00128    else if(eventFlags == SOCKET_EVENT_TX_ACKED)
00129    {
00130       //Disable transmission
00131       socketShutdown(connection->controlSocket, SOCKET_SD_SEND);
00132       //Next state
00133       connection->controlState = FTP_CONTROL_STATE_SHUTDOWN_TX;
00134    }
00135    //Transmission is shut down?
00136    else if(eventFlags == SOCKET_EVENT_TX_SHUTDOWN)
00137    {
00138       //Disable reception
00139       socketShutdown(connection->controlSocket, SOCKET_SD_RECEIVE);
00140       //Next state
00141       connection->controlState = FTP_CONTROL_STATE_SHUTDOWN_RX;
00142    }
00143    //Reception is shut down?
00144    else if(eventFlags == SOCKET_EVENT_RX_SHUTDOWN)
00145    {
00146       //Properly close connection
00147       ftpServerCloseConnection(context, connection);
00148    }
00149 }
00150 
00151 
00152 /**
00153  * @brief Data connection event handler
00154  * @param[in] context Pointer to the FTP server context
00155  * @param[in] connection Pointer to the client connection
00156  * @param[in] eventFlags Event to be processed
00157  **/
00158 
00159 void ftpServerDataEventHandler(FtpServerContext *context,
00160    FtpClientConnection * connection, uint_t eventFlags)
00161 {
00162    //Any connection attempt?
00163    if(connection->dataState == FTP_DATA_STATE_LISTEN)
00164    {
00165       //Accept data connection
00166       ftpServerAcceptDataConnection(connection);
00167    }
00168    //Ready to send data?
00169    else if(connection->dataState == FTP_DATA_STATE_SEND)
00170    {
00171       //Send more data to the remote host
00172       ftpServerSendData(context, connection);
00173    }
00174    //Any data pending in the receive buffer?
00175    else if(connection->dataState == FTP_DATA_STATE_RECEIVE)
00176    {
00177       //Process incoming data
00178       ftpServerReceiveData(context, connection);
00179    }
00180    //Data are transmitted and acknowledged?
00181    else if(connection->dataState == FTP_DATA_STATE_WAIT_ACK)
00182    {
00183       //Disable transmission
00184       socketShutdown(connection->dataSocket, SOCKET_SD_SEND);
00185       //Next state
00186       connection->dataState = FTP_DATA_STATE_SHUTDOWN_TX;
00187    }
00188    //Transmission is shut down?
00189    else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_TX)
00190    {
00191       //Disable reception
00192       socketShutdown(connection->dataSocket, SOCKET_SD_RECEIVE);
00193       //Next state
00194       connection->dataState = FTP_DATA_STATE_SHUTDOWN_RX;
00195    }
00196    //Reception is shut down?
00197    else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_RX)
00198    {
00199       //Close the data connection
00200       ftpServerCloseDataConnection(connection);
00201 
00202       //Back to idle state
00203       connection->controlState = FTP_CONTROL_STATE_IDLE;
00204 
00205       //Transfer status
00206       strcpy(connection->response, "226 Transfer complete\r\n");
00207       //Debug message
00208       TRACE_DEBUG("FTP server: %s", connection->response);
00209 
00210       //Number of bytes in the response buffer
00211       connection->responseLength = strlen(connection->response);
00212       connection->responsePos = 0;
00213    }
00214 }
00215 
00216 
00217 /**
00218  * @brief Send data on the data connection
00219  * @param[in] context Pointer to the FTP server context
00220  * @param[in] connection Pointer to the client connection
00221  **/
00222 
00223 void ftpServerSendData(FtpServerContext *context, FtpClientConnection *connection)
00224 {
00225    error_t error;
00226    size_t n;
00227 
00228    //Any data waiting for transmission?
00229    if(connection->bufferLength > 0)
00230    {
00231       //Send more data
00232       error = socketSend(connection->dataSocket, connection->buffer +
00233          connection->bufferPos, connection->bufferLength, &n, 0);
00234 
00235       //Failed to send data?
00236       if(error != NO_ERROR && error != ERROR_TIMEOUT)
00237       {
00238          //Close the data connection
00239          ftpServerCloseDataConnection(connection);
00240 
00241          //Release previously allocated resources
00242          if(connection->file != NULL)
00243          {
00244             fsCloseFile(connection->file);
00245             connection->file = NULL;
00246          }
00247          if(connection->dir != NULL)
00248          {
00249             fsCloseDir(connection->dir);
00250             connection->dir = NULL;
00251          }
00252 
00253          //Back to idle state
00254          connection->controlState = FTP_CONTROL_STATE_IDLE;
00255 
00256          //Transfer status
00257          strcpy(connection->response, "451 Transfer aborted\r\n");
00258          //Debug message
00259          TRACE_DEBUG("FTP server: %s", connection->response);
00260 
00261          //Number of bytes in the response buffer
00262          connection->responseLength = strlen(connection->response);
00263          connection->responsePos = 0;
00264 
00265          //Exit immediately
00266          return;
00267       }
00268 
00269       //Advance data pointer
00270       connection->bufferPos += n;
00271       //Number of bytes still available in the buffer
00272       connection->bufferLength -= n;
00273    }
00274 
00275    //Empty transmission buffer?
00276    if(connection->bufferLength == 0)
00277    {
00278       //File transfer in progress?
00279       if(connection->controlState == FTP_CONTROL_STATE_RETR)
00280       {
00281          //Read more data
00282          error = fsReadFile(connection->file,
00283             connection->buffer, FTP_SERVER_BUFFER_SIZE, &n);
00284 
00285          //End of stream?
00286          if(error)
00287          {
00288             //Close file
00289             fsCloseFile(connection->file);
00290             connection->file = NULL;
00291 
00292             //Wait for all the data to be transmitted and acknowledged
00293             connection->dataState = FTP_DATA_STATE_WAIT_ACK;
00294 
00295             //Exit immediately
00296             return;
00297          }
00298       }
00299       //Directory listing in progress?
00300       else if(connection->controlState == FTP_CONTROL_STATE_LIST)
00301       {
00302          uint_t perm;
00303          time_t currentTime;
00304          time_t modified;
00305          char_t *path;
00306          FsDirEntry dirEntry;
00307 
00308          //Read a new entry in the directory
00309          error = fsReadDir(connection->dir, &dirEntry);
00310 
00311          //End of stream?
00312          if(error)
00313          {
00314             //Close directory
00315             fsCloseDir(connection->dir);
00316             connection->dir = NULL;
00317 
00318             //Wait for all the data to be transmitted and acknowledged
00319             connection->dataState = FTP_DATA_STATE_WAIT_ACK;
00320 
00321             //Exit immediately
00322             return;
00323          }
00324 
00325          //Point to the scratch buffer
00326          path = connection->buffer;
00327 
00328          //Get the pathname of the directory being listed
00329          strcpy(path, connection->path);
00330          //Retrieve the full pathname
00331          pathCombine(path, dirEntry.name, FTP_SERVER_MAX_PATH_LEN);
00332          pathCanonicalize(path);
00333 
00334          //Get permissions for the specified file
00335          perm = ftpServerGetFilePermissions(context, connection, path);
00336 
00337          //Enforce access rights
00338          if(perm & FTP_FILE_PERM_LIST)
00339          {
00340             //Format links, owner, group and size fields
00341             n = sprintf(connection->buffer, "----------   1 owner    group    %10" PRIu32,
00342                dirEntry.size);
00343 
00344             //Check whether the current entry is a directory
00345             if(dirEntry.attributes & FS_FILE_ATTR_DIRECTORY)
00346                connection->buffer[0] = 'd';
00347 
00348             //Read access permitted?
00349             if(perm & FTP_FILE_PERM_READ)
00350             {
00351                connection->buffer[1] = 'r';
00352                connection->buffer[4] = 'r';
00353                connection->buffer[7] = 'r';
00354             }
00355 
00356             //Write access permitted?
00357             if(perm & FTP_FILE_PERM_WRITE)
00358             {
00359                //Make sure the file is not marked as read-only
00360                if(!(dirEntry.attributes & FS_FILE_ATTR_READ_ONLY))
00361                {
00362                   connection->buffer[2] = 'w';
00363                   connection->buffer[5] = 'w';
00364                   connection->buffer[8] = 'w';
00365                }
00366             }
00367 
00368             //Get current time
00369             currentTime = getCurrentUnixTime();
00370             //Get modification time
00371             modified = convertDateToUnixTime(&dirEntry.modified);
00372 
00373             //Check whether the modification time is within the previous 180 days
00374             if(currentTime > modified && currentTime < (modified + FTP_SERVER_180_DAYS))
00375             {
00376                //The format of the date/time field is Mmm dd hh:mm
00377                n += sprintf(connection->buffer + n, " %s %02" PRIu8 " %02" PRIu8 ":%02" PRIu8,
00378                   months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
00379                   dirEntry.modified.hours, dirEntry.modified.minutes);
00380             }
00381             else
00382             {
00383                //The format of the date/time field is Mmm dd  yyyy
00384                n += sprintf(connection->buffer + n, " %s %02" PRIu8 "  %04" PRIu16,
00385                   months[MIN(dirEntry.modified.month, 12)], dirEntry.modified.day,
00386                   dirEntry.modified.year);
00387             }
00388 
00389             //Append filename
00390             n += sprintf(connection->buffer + n, " %s\r\n", dirEntry.name);
00391             //Debug message
00392             TRACE_DEBUG("FTP server: %s", connection->buffer);
00393          }
00394          else
00395          {
00396             //Insufficient access rights
00397             n = 0;
00398          }
00399       }
00400       //Invalid state?
00401       else
00402       {
00403          //The FTP server has encountered a critical error
00404          ftpServerCloseConnection(context, connection);
00405          //Exit immediately
00406          return;
00407       }
00408 
00409       //Number of bytes in the buffer
00410       connection->bufferPos = 0;
00411       connection->bufferLength = n;
00412    }
00413 }
00414 
00415 
00416 /**
00417  * @brief Receive data on the data connection
00418  * @param[in] context Pointer to the FTP server context
00419  * @param[in] connection Pointer to the client connection
00420  **/
00421 
00422 void ftpServerReceiveData(FtpServerContext *context, FtpClientConnection *connection)
00423 {
00424    error_t error;
00425    bool_t eof;
00426    size_t n;
00427 
00428    //File transfer in progress?
00429    if(connection->controlState == FTP_CONTROL_STATE_STOR ||
00430       connection->controlState == FTP_CONTROL_STATE_APPE)
00431    {
00432       //Read incoming data
00433       error = socketReceive(connection->dataSocket,
00434          connection->buffer + connection->bufferPos,
00435          FTP_SERVER_BUFFER_SIZE - connection->bufferLength, &n, 0);
00436 
00437       //Any error to report?
00438       if(error)
00439       {
00440          //Cannot read more data
00441          eof = TRUE;
00442       }
00443       else
00444       {
00445          //Successful read operation
00446          eof = FALSE;
00447 
00448          //Advance data pointer
00449          connection->bufferPos += n;
00450          connection->bufferLength += n;
00451       }
00452 
00453       //Read data until the buffer is full or the end of the file is reached
00454       if(eof || connection->bufferLength >= FTP_SERVER_BUFFER_SIZE)
00455       {
00456          //Any data to be written?
00457          if(connection->bufferLength > 0)
00458          {
00459             //Write data to the specified file
00460             error = fsWriteFile(connection->file,
00461                connection->buffer, connection->bufferLength);
00462 
00463             //Any error to report?
00464             if(error)
00465             {
00466                //Close the data connection
00467                ftpServerCloseDataConnection(connection);
00468 
00469                //Release previously allocated resources
00470                fsCloseFile(connection->file);
00471                connection->file = NULL;
00472 
00473                //Back to idle state
00474                connection->controlState = FTP_CONTROL_STATE_IDLE;
00475 
00476                //Transfer status
00477                strcpy(connection->response, "451 Transfer aborted\r\n");
00478                //Debug message
00479                TRACE_DEBUG("FTP server: %s", connection->response);
00480 
00481                //Number of bytes in the response buffer
00482                connection->responseLength = strlen(connection->response);
00483                connection->responsePos = 0;
00484 
00485                //Exit immediately
00486                return;
00487             }
00488          }
00489 
00490          //Flush reception buffer
00491          connection->bufferLength = 0;
00492          connection->bufferPos = 0;
00493       }
00494 
00495       //End of stream?
00496       if(eof)
00497       {
00498          //Close file
00499          fsCloseFile(connection->file);
00500          connection->file = NULL;
00501 
00502          //Graceful shutdown sequence
00503          connection->dataState = FTP_DATA_STATE_WAIT_ACK;
00504       }
00505    }
00506    //Invalid state?
00507    else
00508    {
00509       //The FTP server has encountered a critical error
00510       ftpServerCloseConnection(context, connection);
00511    }
00512 }
00513 
00514 #endif
00515