Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ftp_client.c Source File

ftp_client.c

Go to the documentation of this file.
00001 /**
00002  * @file ftp_client.c
00003  * @brief FTP client (File Transfer Protocol)
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  * @section Description
00026  *
00027  * File Transfer Protocol (FTP) is a standard network protocol used to
00028  * transfer files from one host to another host over a TCP-based network.
00029  * Refer to the following RFCs for complete details:
00030  * - RFC 959: File Transfer Protocol (FTP)
00031  * - RFC 2428: FTP Extensions for IPv6 and NATs
00032  *
00033  * @author Oryx Embedded SARL (www.oryx-embedded.com)
00034  * @version 1.7.6
00035  **/
00036 
00037 //Switch to the appropriate trace level
00038 #define TRACE_LEVEL FTP_TRACE_LEVEL
00039 
00040 //Dependencies
00041 #include <stdlib.h>
00042 #include <ctype.h>
00043 #include "ftp/ftp_client.h"
00044 #include "str.h"
00045 #include "error.h"
00046 #include "debug.h"
00047 
00048 //Check TCP/IP stack configuration
00049 #if (FTP_CLIENT_SUPPORT == ENABLED)
00050 
00051 
00052 /**
00053  * @brief Set the port to be used in data connection
00054  * @param[in] context Pointer to the FTP client context
00055  * @param[in] ipAddr Host address
00056  * @param[in] port Port number
00057  * @return Error code
00058  **/
00059 
00060 error_t ftpSetPort(FtpClientContext *context, const IpAddr *ipAddr, uint16_t port)
00061 {
00062    error_t error;
00063    uint_t replyCode;
00064    char_t *p;
00065 
00066    //Invalid context?
00067    if(context == NULL)
00068       return ERROR_INVALID_PARAMETER;
00069 
00070 #if (IPV4_SUPPORT == ENABLED)
00071    //IPv4 FTP client?
00072    if(ipAddr->length == sizeof(Ipv4Addr))
00073    {
00074       //Format the PORT command
00075       strcpy(context->buffer, "PORT ");
00076 
00077       //Append host address
00078       ipv4AddrToString(ipAddr->ipv4Addr, context->buffer + 5);
00079 
00080       //Parse the resulting string
00081       for(p = context->buffer; *p != '\0'; p++)
00082       {
00083          //Change dots to commas
00084          if(*p == '.') *p = ',';
00085       }
00086 
00087       //Append port number
00088       sprintf(p, ",%" PRIu8 ",%" PRIu8 "\r\n", MSB(port), LSB(port));
00089    }
00090    else
00091 #endif
00092 #if (IPV6_SUPPORT == ENABLED)
00093    //IPv6 FTP client?
00094    if(ipAddr->length == sizeof(Ipv6Addr))
00095    {
00096       //Format the EPRT command
00097       strcpy(context->buffer, "EPRT |2|");
00098 
00099       //Append host address
00100       ipv6AddrToString(&ipAddr->ipv6Addr, context->buffer + 8);
00101 
00102       //Point to the end of the resulting string
00103       p = context->buffer + strlen(context->buffer);
00104       //Append port number
00105       sprintf(p, "|%" PRIu16 "|\r\n", port);
00106    }
00107    else
00108 #endif
00109    //Invalid IP address?
00110    {
00111       //Report an error
00112       return ERROR_INVALID_ADDRESS;
00113    }
00114 
00115    //Send the command to the server
00116    error = ftpSendCommand(context, context->buffer, &replyCode);
00117    //Any error to report?
00118    if(error)
00119       return error;
00120 
00121    //Check FTP response code
00122    if(!FTP_REPLY_CODE_2YZ(replyCode))
00123       return ERROR_UNEXPECTED_RESPONSE;
00124 
00125    //Successful processing
00126    return NO_ERROR;
00127 }
00128 
00129 
00130 /**
00131  * @brief Enter passive mode
00132  * @param[in] context Pointer to the FTP client context
00133  * @param[out] port The port number the server is listening on
00134  * @return Error code
00135  **/
00136 
00137 error_t ftpSetPassiveMode(FtpClientContext *context, uint16_t *port)
00138 {
00139    error_t error;
00140    uint_t replyCode;
00141    char_t delimiter;
00142    char_t *p;
00143 
00144    //Invalid context?
00145    if(context == NULL)
00146       return ERROR_INVALID_PARAMETER;
00147 
00148 #if (IPV4_SUPPORT == ENABLED)
00149    //IPv4 FTP server?
00150    if(context->serverIpAddr.length == sizeof(Ipv4Addr))
00151    {
00152       //Send the command to the server
00153       error = ftpSendCommand(context, "PASV\r\n", &replyCode);
00154       //Any error to report?
00155       if(error)
00156          return error;
00157 
00158       //Check FTP response code
00159       if(!FTP_REPLY_CODE_2YZ(replyCode))
00160          return ERROR_UNEXPECTED_RESPONSE;
00161 
00162       //Delimiter character
00163       delimiter = ',';
00164 
00165       //Retrieve the low byte of the port number
00166       p = strrchr(context->buffer, delimiter);
00167       //Failed to parse the response?
00168       if(!p)
00169          return ERROR_INVALID_SYNTAX;
00170 
00171       //Convert the resulting string
00172       *port = atoi(p + 1);
00173       //Split the string
00174       *p = '\0';
00175 
00176       //Retrieve the high byte of the port number
00177       p = strrchr(context->buffer, delimiter);
00178       //Failed to parse the response?
00179       if(!p)
00180          return ERROR_INVALID_SYNTAX;
00181 
00182       //Convert the resulting string
00183       *port |= atoi(p + 1) << 8;
00184    }
00185    else
00186 #endif
00187 #if (IPV6_SUPPORT == ENABLED)
00188    //IPv6 FTP server?
00189    if(context->serverIpAddr.length == sizeof(Ipv6Addr))
00190    {
00191       //Send the command to the server
00192       error = ftpSendCommand(context, "EPSV\r\n", &replyCode);
00193       //Any error to report?
00194       if(error)
00195          return error;
00196 
00197       //Check FTP response code
00198       if(!FTP_REPLY_CODE_2YZ(replyCode))
00199          return ERROR_UNEXPECTED_RESPONSE;
00200 
00201       //Search for the opening parenthesis
00202       p = strrchr(context->buffer, '(');
00203       //Failed to parse the response?
00204       if(!p || p[1] == '\0')
00205          return ERROR_INVALID_SYNTAX;
00206 
00207       //Retrieve the delimiter character
00208       delimiter = p[1];
00209 
00210       //Search for the last delimiter character
00211       p = strrchr(context->buffer, delimiter);
00212       //Failed to parse the response?
00213       if(!p)
00214          return ERROR_INVALID_SYNTAX;
00215 
00216       //Split the string
00217       *p = '\0';
00218 
00219       //Search for the last but one delimiter character
00220       p = strrchr(context->buffer, delimiter);
00221       //Failed to parse the response?
00222       if(!p)
00223          return ERROR_INVALID_SYNTAX;
00224 
00225       //Convert the resulting string
00226       *port = atoi(p + 1);
00227    }
00228    else
00229 #endif
00230    //Invalid IP address?
00231    {
00232       //Report an error
00233       return ERROR_INVALID_ADDRESS;
00234    }
00235 
00236    //Successful processing
00237    return NO_ERROR;
00238 }
00239 
00240 
00241 /**
00242  * @brief Set representation type
00243  * @param[in] context Pointer to the FTP client context
00244  * @param[in] type Single character identifying the desired type
00245  * @return Error code
00246  **/
00247 
00248 error_t ftpSetType(FtpClientContext *context, char_t type)
00249 {
00250    error_t error;
00251    uint_t replyCode;
00252 
00253    //Invalid context?
00254    if(context == NULL)
00255       return ERROR_INVALID_PARAMETER;
00256 
00257    //Format the TYPE command
00258    sprintf(context->buffer, "TYPE %c\r\n", type);
00259 
00260    //Send the command to the server
00261    error = ftpSendCommand(context, context->buffer, &replyCode);
00262    //Any error to report?
00263    if(error)
00264       return error;
00265 
00266    //Check FTP response code
00267    if(!FTP_REPLY_CODE_2YZ(replyCode))
00268       return ERROR_UNEXPECTED_RESPONSE;
00269 
00270    //Successful processing
00271    return NO_ERROR;
00272 }
00273 
00274 
00275 /**
00276  * @brief Set protection buffer size
00277  * @param[in] context Pointer to the FTP client context
00278  * @param[in] size Protection buffer size
00279  * @return Error code
00280  **/
00281 
00282 error_t ftpSetProtectionBufferSize(FtpClientContext *context, uint32_t size)
00283 {
00284    error_t error;
00285    uint_t replyCode;
00286 
00287    //Invalid context?
00288    if(context == NULL)
00289       return ERROR_INVALID_PARAMETER;
00290 
00291    //Format the PBSZ command
00292    sprintf(context->buffer, "PBSZ %" PRIu32 "\r\n", size);
00293 
00294    //Send the command to the server
00295    error = ftpSendCommand(context, context->buffer, &replyCode);
00296    //Any error to report?
00297    if(error)
00298       return error;
00299 
00300    //Check FTP response code
00301    if(!FTP_REPLY_CODE_2YZ(replyCode))
00302       return ERROR_UNEXPECTED_RESPONSE;
00303 
00304    //Successful processing
00305    return NO_ERROR;
00306 }
00307 
00308 
00309 /**
00310  * @brief Set data channel protection level
00311  * @param[in] context Pointer to the FTP client context
00312  * @param[in] level Character specifying the data channel protection level
00313  * @return Error code
00314  **/
00315 
00316 error_t ftpSetDataChannelProtectionLevel(FtpClientContext *context, char_t level)
00317 {
00318    error_t error;
00319    uint_t replyCode;
00320 
00321    //Invalid context?
00322    if(context == NULL)
00323       return ERROR_INVALID_PARAMETER;
00324 
00325    //Format the PROT command
00326    sprintf(context->buffer, "PROT %c\r\n", level);
00327 
00328    //Send the command to the server
00329    error = ftpSendCommand(context, context->buffer, &replyCode);
00330    //Any error to report?
00331    if(error)
00332       return error;
00333 
00334    //Check FTP response code
00335    if(!FTP_REPLY_CODE_2YZ(replyCode))
00336       return ERROR_UNEXPECTED_RESPONSE;
00337 
00338    //Successful processing
00339    return NO_ERROR;
00340 }
00341 
00342 
00343 /**
00344  * @brief Establish a connection with the specified FTP server
00345  * @param[in] context Pointer to the FTP client context
00346  * @param[in] interface Underlying network interface (optional parameter)
00347  * @param[in] serverIpAddr IP address of the FTP server
00348  * @param[in] serverPort Port number
00349  * @param[in] flags Connection options
00350  * @return Error code
00351  **/
00352 
00353 error_t ftpConnect(FtpClientContext *context, NetInterface *interface,
00354    const IpAddr *serverIpAddr, uint16_t serverPort, uint_t flags)
00355 {
00356    error_t error;
00357    uint_t replyCode;
00358 
00359    //Invalid context?
00360    if(context == NULL)
00361       return ERROR_INVALID_PARAMETER;
00362 
00363    //Initialize context
00364    context->passiveMode = FALSE;
00365    context->controlSocket = NULL;
00366    context->dataSocket = NULL;
00367 
00368 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00369    context->controlTlsContext = NULL;
00370    context->dataTlsContext = NULL;
00371 #endif
00372 
00373    //Underlying network interface
00374    context->interface = interface;
00375    //Save the IP address of the FTP server
00376    context->serverIpAddr = *serverIpAddr;
00377 
00378    //Use passive mode?
00379    if(flags & FTP_PASSIVE_MODE)
00380       context->passiveMode = TRUE;
00381 
00382    //Open control socket
00383    context->controlSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
00384    //Failed to open socket?
00385    if(!context->controlSocket)
00386       return ERROR_OPEN_FAILED;
00387 
00388    //Start of exception handling block
00389    do
00390    {
00391       //Bind the socket to a particular network interface?
00392       if(context->interface != NULL)
00393       {
00394          //Associate the socket with the relevant interface
00395          error = socketBindToInterface(context->controlSocket, context->interface);
00396          //Any error to report?
00397          if(error)
00398             break;
00399       }
00400 
00401       //Set timeout for blocking operations
00402       error = socketSetTimeout(context->controlSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
00403       //Any error to report?
00404       if(error)
00405          break;
00406 
00407       //Specify the size of the send buffer
00408       error = socketSetTxBufferSize(context->controlSocket,
00409          FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE);
00410       //Any error to report?
00411       if(error)
00412          break;
00413 
00414       //Specify the size of the receive buffer
00415       error = socketSetRxBufferSize(context->controlSocket,
00416          FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE);
00417       //Any error to report?
00418       if(error)
00419          break;
00420 
00421       //Connect to the FTP server
00422       error = socketConnect(context->controlSocket, serverIpAddr, serverPort);
00423       //Connection to server failed?
00424       if(error)
00425          break;
00426 
00427 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00428    //Use implicit FTPS?
00429    if(flags & FTP_IMPLICIT_SECURITY)
00430    {
00431       //SSL initialization
00432       error = ftpInitControlTlsContext(context);
00433       //Any error to report?
00434       if(error)
00435          break;
00436    }
00437 #endif
00438 
00439    //Wait for the connection greeting reply
00440    error = ftpSendCommand(context, NULL, &replyCode);
00441    //Any communication error to report?
00442    if(error)
00443       break;
00444 
00445    //Check FTP response code
00446    if(!FTP_REPLY_CODE_2YZ(replyCode))
00447       error = ERROR_UNEXPECTED_RESPONSE;
00448 
00449 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00450    //Use explicit FTPS?
00451    if(flags & FTP_EXPLICIT_SECURITY)
00452    {
00453       //Sanity check
00454       if(context->controlTlsContext == NULL)
00455       {
00456          //The client issues an AUTH TLS command
00457          error = ftpAuth(context);
00458          //Any error to report?
00459          if(error)
00460             break;
00461 
00462          //SSL initialization
00463          error = ftpInitControlTlsContext(context);
00464          //Any error to report?
00465          if(error)
00466             break;
00467       }
00468    }
00469 #endif
00470 
00471       //End of exception handling block
00472    } while(0);
00473 
00474    //Any error to report?
00475    if(error)
00476    {
00477 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00478       if(context->controlTlsContext != NULL)
00479       {
00480          //Release SSL context
00481          tlsFree(context->controlTlsContext);
00482          context->controlTlsContext = NULL;
00483       }
00484 #endif
00485 
00486       //Close socket
00487       socketClose(context->controlSocket);
00488       context->controlSocket = NULL;
00489    }
00490 
00491    //Return status code
00492    return error;
00493 }
00494 
00495 
00496 /**
00497  * @brief Request authentication
00498  * @param[in] context Pointer to the FTP client context
00499  * @return Error code
00500  **/
00501 
00502 error_t ftpAuth(FtpClientContext *context)
00503 {
00504    error_t error;
00505    uint_t replyCode;
00506 
00507    //Invalid context?
00508    if(context == NULL)
00509       return ERROR_INVALID_PARAMETER;
00510 
00511    //Issue the AUTH command
00512    error = ftpSendCommand(context, "AUTH TLS\r\n", &replyCode);
00513    //Any error to report?
00514    if(error)
00515       return error;
00516 
00517    //Check FTP response code
00518    if(!FTP_REPLY_CODE_2YZ(replyCode))
00519       return ERROR_UNEXPECTED_RESPONSE;
00520 
00521    //Successful processing
00522    return NO_ERROR;
00523 }
00524 
00525 
00526 /**
00527  * @brief Login to the FTP server using the provided username and password
00528  * @param[in] context Pointer to the FTP client context
00529  * @param[in] username The username to login under
00530  * @param[in] password The password to use
00531  * @param[in] account Account name
00532  * @return Error code
00533  **/
00534 
00535 error_t ftpLogin(FtpClientContext *context, const char_t *username,
00536    const char_t *password, const char_t *account)
00537 {
00538    error_t error;
00539    uint_t replyCode;
00540 
00541    //Invalid context?
00542    if(context == NULL)
00543       return ERROR_INVALID_PARAMETER;
00544 
00545    //Format the USER command
00546    sprintf(context->buffer, "USER %s\r\n", username);
00547 
00548    //Send the command to the server
00549    error = ftpSendCommand(context, context->buffer, &replyCode);
00550    //Any error to report?
00551    if(error)
00552       return error;
00553 
00554    //Check FTP response code
00555    if(FTP_REPLY_CODE_2YZ(replyCode))
00556       return NO_ERROR;
00557    else if(!FTP_REPLY_CODE_3YZ(replyCode))
00558       return ERROR_UNEXPECTED_RESPONSE;
00559 
00560    //Format the PASS command
00561    sprintf(context->buffer, "PASS %s\r\n", password);
00562 
00563    //Send the command to the server
00564    error = ftpSendCommand(context, context->buffer, &replyCode);
00565    //Any error to report?
00566    if(error)
00567       return error;
00568 
00569    //Check FTP response code
00570    if(FTP_REPLY_CODE_2YZ(replyCode))
00571       return NO_ERROR;
00572    else if(!FTP_REPLY_CODE_3YZ(replyCode))
00573       return ERROR_UNEXPECTED_RESPONSE;
00574 
00575    //Format the ACCT command
00576    sprintf(context->buffer, "ACCT %s\r\n", account);
00577 
00578    //Send the command to the server
00579    error = ftpSendCommand(context, context->buffer, &replyCode);
00580    //Any error to report?
00581    if(error)
00582       return error;
00583 
00584    //Check FTP response code
00585    if(!FTP_REPLY_CODE_2YZ(replyCode))
00586       return ERROR_UNEXPECTED_RESPONSE;
00587 
00588    //Successful processing
00589    return NO_ERROR;
00590 }
00591 
00592 
00593 /**
00594  * @brief Get the working directory from the FTP server
00595  * @param[in] context Pointer to the FTP client context
00596  * @param[out] path Output buffer where to store the current directory
00597  * @param[in] size Size of the output buffer
00598  * @return Error code
00599  **/
00600 
00601 error_t ftpGetWorkingDir(FtpClientContext *context, char_t *path, size_t size)
00602 {
00603    error_t error;
00604    size_t length;
00605    uint_t replyCode;
00606    char_t *p;
00607 
00608    //Invalid context?
00609    if(context == NULL)
00610       return ERROR_INVALID_PARAMETER;
00611    //Check parameters
00612    if(path == NULL || size == 0)
00613       return ERROR_INVALID_PARAMETER;
00614 
00615    //Send the command to the server
00616    error = ftpSendCommand(context, "PWD\r\n", &replyCode);
00617    //Any error to report?
00618    if(error)
00619       return error;
00620 
00621    //Check FTP response code
00622    if(!FTP_REPLY_CODE_2YZ(replyCode))
00623       return ERROR_UNEXPECTED_RESPONSE;
00624 
00625    //Search for the last double quote
00626    p = strrchr(context->buffer, '\"');
00627    //Failed to parse the response?
00628    if(!p)
00629       return ERROR_INVALID_SYNTAX;
00630 
00631    //Split the string
00632    *p = '\0';
00633 
00634    //Search for the first double quote
00635    p = strchr(context->buffer, '\"');
00636    //Failed to parse the response?
00637    if(!p)
00638       return ERROR_INVALID_SYNTAX;
00639 
00640    //Retrieve the length of the working directory
00641    length = strlen(p + 1);
00642    //Limit the number of characters to copy
00643    length = MIN(length, size - 1);
00644 
00645    //Copy the string
00646    strncpy(path, p + 1, length);
00647    //Properly terminate the string with a NULL character
00648    path[length] = '\0';
00649 
00650    //Successful processing
00651    return NO_ERROR;
00652 }
00653 
00654 
00655 /**
00656  * @brief Change the current working directory of the FTP session
00657  * @param[in] context Pointer to the FTP client context
00658  * @param[in] path The new current working directory
00659  * @return Error code
00660  **/
00661 
00662 error_t ftpChangeWorkingDir(FtpClientContext *context, const char_t *path)
00663 {
00664    error_t error;
00665    uint_t replyCode;
00666 
00667    //Invalid context?
00668    if(context == NULL)
00669       return ERROR_INVALID_PARAMETER;
00670 
00671    //Format the CWD command
00672    sprintf(context->buffer, "CWD %s\r\n", path);
00673 
00674    //Send the command to the server
00675    error = ftpSendCommand(context, context->buffer, &replyCode);
00676    //Any error to report?
00677    if(error)
00678       return error;
00679 
00680    //Check FTP response code
00681    if(!FTP_REPLY_CODE_2YZ(replyCode))
00682       return ERROR_UNEXPECTED_RESPONSE;
00683 
00684    //Successful processing
00685    return NO_ERROR;
00686 }
00687 
00688 
00689 /**
00690  * @brief Change the current working directory to the parent directory
00691  * @param[in] context Pointer to the FTP client context
00692  * @return Error code
00693  **/
00694 
00695 error_t ftpChangeToParentDir(FtpClientContext *context)
00696 {
00697    error_t error;
00698    uint_t replyCode;
00699 
00700    //Invalid context?
00701    if(context == NULL)
00702       return ERROR_INVALID_PARAMETER;
00703 
00704    //Send the command to the server
00705    error = ftpSendCommand(context, "CDUP\r\n", &replyCode);
00706    //Any error to report?
00707    if(error)
00708       return error;
00709 
00710    //Check FTP response code
00711    if(!FTP_REPLY_CODE_2YZ(replyCode))
00712       return ERROR_UNEXPECTED_RESPONSE;
00713 
00714    //Successful processing
00715    return NO_ERROR;
00716 }
00717 
00718 
00719 /**
00720  * @brief Create a new directory
00721  * @param[in] context Pointer to the FTP client context
00722  * @param[in] path The name of the new directory
00723  * @return Error code
00724  **/
00725 
00726 error_t ftpMakeDir(FtpClientContext *context, const char_t *path)
00727 {
00728    error_t error;
00729    uint_t replyCode;
00730 
00731    //Invalid context?
00732    if(context == NULL)
00733       return ERROR_INVALID_PARAMETER;
00734 
00735    //Format the MKD command
00736    sprintf(context->buffer, "MKD %s\r\n", path);
00737 
00738    //Send the command to the server
00739    error = ftpSendCommand(context, context->buffer, &replyCode);
00740    //Any error to report?
00741    if(error)
00742       return error;
00743 
00744    //Check FTP response code
00745    if(!FTP_REPLY_CODE_2YZ(replyCode))
00746       return ERROR_UNEXPECTED_RESPONSE;
00747 
00748    //Successful processing
00749    return NO_ERROR;
00750 }
00751 
00752 
00753 /**
00754  * @brief Remove a directory on the FTP server
00755  * @param[in] context Pointer to the FTP client context
00756  * @param[in] path Path to the directory to be removed
00757  * @return Error code
00758  **/
00759 
00760 error_t ftpRemoveDir(FtpClientContext *context, const char_t *path)
00761 {
00762    error_t error;
00763    uint_t replyCode;
00764 
00765    //Invalid context?
00766    if(context == NULL)
00767       return ERROR_INVALID_PARAMETER;
00768 
00769    //Format the RMD command
00770    sprintf(context->buffer, "RMD %s\r\n", path);
00771 
00772    //Send the command to the server
00773    error = ftpSendCommand(context, context->buffer, &replyCode);
00774    //Any error to report?
00775    if(error)
00776       return error;
00777 
00778    //Check FTP response code
00779    if(!FTP_REPLY_CODE_2YZ(replyCode))
00780       return ERROR_UNEXPECTED_RESPONSE;
00781 
00782    //Successful processing
00783    return NO_ERROR;
00784 }
00785 
00786 
00787 /**
00788  * @brief Open a file for reading, writing, or appending
00789  * @param[in] context Pointer to the FTP client context
00790  * @param[in] path Path to the file to be be opened
00791  * @param[in] flags Access mode
00792  * @return Error code
00793  **/
00794 
00795 error_t ftpOpenFile(FtpClientContext *context, const char_t *path, uint_t flags)
00796 {
00797    error_t error;
00798    uint_t replyCode;
00799    IpAddr ipAddr;
00800    uint16_t port;
00801 
00802    //Invalid context?
00803    if(context == NULL)
00804       return ERROR_INVALID_PARAMETER;
00805 
00806    //Open data socket
00807    context->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
00808    //Failed to open socket?
00809    if(!context->dataSocket)
00810       return ERROR_OPEN_FAILED;
00811 
00812    //Start of exception handling block
00813    do
00814    {
00815       //Bind the socket to a particular network interface?
00816       if(context->interface != NULL)
00817       {
00818          //Associate the socket with the relevant interface
00819          error = socketBindToInterface(context->dataSocket, context->interface);
00820          //Any error to report?
00821          if(error)
00822             break;
00823       }
00824 
00825       //Set timeout for blocking operations
00826       error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
00827       //Any error to report?
00828       if(error)
00829          break;
00830 
00831       //Check data transfer direction
00832       if(flags & (FTP_FOR_WRITING | FTP_FOR_APPENDING))
00833       {
00834          //Maximize transmission throughput by using a large buffer
00835          error = socketSetTxBufferSize(context->dataSocket,
00836             FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE);
00837          //Any error to report?
00838          if(error)
00839             break;
00840 
00841          //Use a small buffer for the reception path
00842          error = socketSetRxBufferSize(context->dataSocket,
00843             FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE);
00844          //Any error to report?
00845          if(error)
00846             break;
00847       }
00848       else
00849       {
00850          //Use a small buffer for the transmission path
00851          error = socketSetTxBufferSize(context->dataSocket,
00852             FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE);
00853          //Any error to report?
00854          if(error)
00855             break;
00856 
00857          //Maximize reception throughput by using a large buffer
00858          error = socketSetRxBufferSize(context->dataSocket,
00859             FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE);
00860          //Any error to report?
00861          if(error)
00862             break;
00863       }
00864 
00865       //Set representation type
00866       if(flags & FTP_TEXT_TYPE)
00867       {
00868          //Use ASCII type
00869          error = ftpSetType(context, 'A');
00870          //Any error to report?
00871          if(error)
00872             break;
00873       }
00874       else
00875       {
00876          //Use image type
00877          error = ftpSetType(context, 'I');
00878          //Any error to report?
00879          if(error)
00880             break;
00881       }
00882 
00883 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00884       //Use SSL?
00885       if(context->controlTlsContext != NULL)
00886       {
00887          //A PBSZ command must be issued, but must have a parameter of '0' to
00888          //indicate that no buffering is taking place and the data connection
00889          //should not be encapsulated
00890          error = ftpSetProtectionBufferSize(context, 0);
00891          //Any communication error to report?
00892          if(error)
00893             break;
00894 
00895          //If the data connection security level is 'Private', then an SSL
00896          //negotiation must take place on the data connection
00897          error = ftpSetDataChannelProtectionLevel(context, 'P');
00898          //Any communication error to report?
00899          if(error)
00900             break;
00901       }
00902 #endif
00903 
00904       //Check transfer mode
00905       if(!context->passiveMode)
00906       {
00907          //Place the data socket in the listening state
00908          error = socketListen(context->dataSocket, 1);
00909          //Any error to report?
00910          if(error)
00911             break;
00912 
00913          //Retrieve local IP address
00914          error = socketGetLocalAddr(context->controlSocket, &ipAddr, NULL);
00915          //Any error to report?
00916          if(error)
00917             break;
00918 
00919          //Retrieve local port number
00920          error = socketGetLocalAddr(context->dataSocket, NULL, &port);
00921          //Any error to report?
00922          if(error)
00923             break;
00924 
00925          //Set the port to be used in data connection
00926          error = ftpSetPort(context, &ipAddr, port);
00927          //Any error to report?
00928          if(error)
00929             break;
00930       }
00931       else
00932       {
00933          //Enter passive mode
00934          error = ftpSetPassiveMode(context, &port);
00935          //Any error to report?
00936          if(error)
00937             break;
00938 
00939          //Establish data connection
00940          error = socketConnect(context->dataSocket, &context->serverIpAddr, port);
00941          //Connection to server failed?
00942          if(error)
00943             break;
00944       }
00945 
00946       //Format the command
00947       if(flags & FTP_FOR_WRITING)
00948          sprintf(context->buffer, "STOR %s\r\n", path);
00949       else if(flags & FTP_FOR_APPENDING)
00950          sprintf(context->buffer, "APPE %s\r\n", path);
00951       else
00952          sprintf(context->buffer, "RETR %s\r\n", path);
00953 
00954       //Send the command to the server
00955       error = ftpSendCommand(context, context->buffer, &replyCode);
00956       //Any error to report?
00957       if(error)
00958          break;
00959 
00960       //Check FTP response code
00961       if(!FTP_REPLY_CODE_1YZ(replyCode))
00962       {
00963          //Report an error
00964          error = ERROR_UNEXPECTED_RESPONSE;
00965          break;
00966       }
00967 
00968       //Check transfer mode
00969       if(!context->passiveMode)
00970       {
00971          //Wait for the server to connect back to the client's data port
00972          Socket *socket = socketAccept(context->dataSocket, NULL, NULL);
00973 
00974          //No connection request?
00975          if(socket == NULL)
00976          {
00977             //Report an error
00978             error = ERROR_FAILURE;
00979             break;
00980          }
00981 
00982          //Close the listening socket
00983          socketClose(context->dataSocket);
00984          //Save socket handle
00985          context->dataSocket = socket;
00986 
00987          //Set timeout for blocking operations
00988          error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT);
00989          //Any error to report?
00990          if(error)
00991             break;
00992       }
00993 
00994 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
00995       //Use SSL?
00996       if(context->controlTlsContext != NULL)
00997       {
00998          //SSL initialization
00999          error = ftpInitDataTlsContext(context);
01000          //Any error to report?
01001          if(error)
01002             break;
01003       }
01004 #endif
01005 
01006       //End of exception handling block
01007    } while(0);
01008 
01009    //Any error to report?
01010    if(error)
01011    {
01012 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01013       if(context->dataTlsContext != NULL)
01014       {
01015          //Release SSL context
01016          tlsFree(context->dataTlsContext);
01017          context->dataTlsContext = NULL;
01018       }
01019 #endif
01020 
01021       //Close socket
01022       socketClose(context->dataSocket);
01023       context->dataSocket = NULL;
01024    }
01025 
01026    //Return status code
01027    return error;
01028 }
01029 
01030 
01031 /**
01032  * @brief Write to a remote file
01033  * @param[in] context Pointer to the FTP client context
01034  * @param[in] data Pointer to a buffer containing the data to be written
01035  * @param[in] length Number of data bytes to write
01036  * @param[in] flags Set of flags that influences the behavior of this function
01037  * @return Error code
01038  **/
01039 
01040 error_t ftpWriteFile(FtpClientContext *context,
01041    const void *data, size_t length, uint_t flags)
01042 {
01043    error_t error;
01044 
01045    //Invalid context?
01046    if(context == NULL)
01047       return ERROR_INVALID_PARAMETER;
01048 
01049 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01050    if(context->dataTlsContext != NULL)
01051    {
01052       //Transmit data to the FTP server
01053       error = tlsWrite(context->dataTlsContext, data, length, NULL, flags);
01054    }
01055    else
01056 #endif
01057    {
01058       //Transmit data to the FTP server
01059       error = socketSend(context->dataSocket, data, length, NULL, flags);
01060    }
01061 
01062    //Return status code
01063    return error;
01064 }
01065 
01066 
01067 /**
01068  * @brief Read from a remote file
01069  * @param[in] context Pointer to the FTP client context
01070  * @param[out] data Buffer where to store the incoming data
01071  * @param[in] size Maximum number of bytes that can be read
01072  * @param[out] length Actual number of bytes that have been read
01073  * @param[in] flags Set of flags that influences the behavior of this function
01074  * @return Error code
01075  **/
01076 
01077 error_t ftpReadFile(FtpClientContext *context,
01078    void *data, size_t size, size_t *length, uint_t flags)
01079 {
01080    error_t error;
01081 
01082    //Invalid context?
01083    if(context == NULL)
01084       return ERROR_INVALID_PARAMETER;
01085 
01086 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01087    if(context->dataTlsContext != NULL)
01088    {
01089       //Receive data from the FTP server
01090       error = tlsRead(context->dataTlsContext, data, size, length, flags);
01091    }
01092    else
01093 #endif
01094    {
01095       //Receive data from the FTP server
01096       error = socketReceive(context->dataSocket, data, size, length, flags);
01097    }
01098 
01099    //Return status code
01100    return error;
01101 }
01102 
01103 
01104 /**
01105  * @brief Close file
01106  * @param[in] context Pointer to the FTP client context
01107  * @return Error code
01108  **/
01109 
01110 error_t ftpCloseFile(FtpClientContext *context)
01111 {
01112    error_t error;
01113    uint_t replyCode;
01114 
01115    //Invalid context?
01116    if(context == NULL)
01117       return ERROR_INVALID_PARAMETER;
01118 
01119 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01120    if(context->dataTlsContext != NULL)
01121    {
01122       //Gracefully close the data connection
01123       tlsShutdown(context->dataTlsContext);
01124 
01125       //Release SSL context (data connection)
01126       tlsFree(context->dataTlsContext);
01127       context->dataTlsContext = NULL;
01128    }
01129 #endif
01130 
01131    //Graceful shutdown
01132    socketShutdown(context->dataSocket, SOCKET_SD_BOTH);
01133 
01134    //Close the data socket
01135    socketClose(context->dataSocket);
01136    context->dataSocket = NULL;
01137 
01138    //Check the transfer status
01139    error = ftpSendCommand(context, NULL, &replyCode);
01140    //Any error to report?
01141    if(error)
01142       return error;
01143 
01144    //Check FTP response code
01145    if(!FTP_REPLY_CODE_2YZ(replyCode))
01146       return ERROR_UNEXPECTED_RESPONSE;
01147 
01148    //Successful processing
01149    return NO_ERROR;
01150 }
01151 
01152 
01153 /**
01154  * @brief Rename a remote file
01155  * @param[in] context Pointer to the FTP client context
01156  * @param[in] oldName The name of the remote file to rename
01157  * @param[in] newName The new name of the remote file
01158  * @return Error code
01159  **/
01160 
01161 error_t ftpRenameFile(FtpClientContext *context,
01162    const char_t *oldName, const char_t *newName)
01163 {
01164    error_t error;
01165    uint_t replyCode;
01166 
01167    //Invalid context?
01168    if(context == NULL)
01169       return ERROR_INVALID_PARAMETER;
01170 
01171    //Format the RNFR command
01172    sprintf(context->buffer, "RNFR %s\r\n", oldName);
01173 
01174    //Send the command to the server
01175    error = ftpSendCommand(context, context->buffer, &replyCode);
01176    //Any error to report?
01177    if(error)
01178       return error;
01179 
01180    //Check FTP response code
01181    if(!FTP_REPLY_CODE_3YZ(replyCode))
01182       return ERROR_UNEXPECTED_RESPONSE;
01183 
01184    //Format the RNTO command
01185    sprintf(context->buffer, "RNTO %s\r\n", newName);
01186 
01187    //Send the command to the server
01188    error = ftpSendCommand(context, context->buffer, &replyCode);
01189    //Any error to report?
01190    if(error)
01191       return error;
01192 
01193    //Check FTP response code
01194    if(!FTP_REPLY_CODE_2YZ(replyCode))
01195       return ERROR_UNEXPECTED_RESPONSE;
01196 
01197    //Successful processing
01198    return NO_ERROR;
01199 }
01200 
01201 
01202 /**
01203  * @brief Delete a file
01204  * @param[in] context Pointer to the FTP client context
01205  * @param[in] path Path to the file to be be deleted
01206  * @return Error code
01207  **/
01208 
01209 error_t ftpDeleteFile(FtpClientContext *context, const char_t *path)
01210 {
01211    error_t error;
01212    uint_t replyCode;
01213 
01214    //Invalid context?
01215    if(context == NULL)
01216       return ERROR_INVALID_PARAMETER;
01217 
01218    //Format the DELE command
01219    sprintf(context->buffer, "DELE %s\r\n", path);
01220 
01221    //Send the command to the server
01222    error = ftpSendCommand(context, context->buffer, &replyCode);
01223    //Any error to report?
01224    if(error)
01225       return error;
01226 
01227    //Check FTP response code
01228    if(!FTP_REPLY_CODE_2YZ(replyCode))
01229       return ERROR_UNEXPECTED_RESPONSE;
01230 
01231    //Successful processing
01232    return NO_ERROR;
01233 }
01234 
01235 
01236 /**
01237  * @brief Close the connection with the FTP server
01238  * @param[in] context Pointer to the FTP client context
01239  * @return Error code
01240  **/
01241 
01242 error_t ftpClose(FtpClientContext *context)
01243 {
01244    //Invalid context?
01245    if(context == NULL)
01246       return ERROR_INVALID_PARAMETER;
01247 
01248 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01249    if(context->dataTlsContext != NULL)
01250    {
01251       //Gracefully close the data connection
01252       tlsShutdown(context->dataTlsContext);
01253 
01254       //Release SSL context (data connection)
01255       tlsFree(context->dataTlsContext);
01256       context->dataTlsContext = NULL;
01257    }
01258 #endif
01259 
01260    //Close data socket
01261    if(context->dataSocket)
01262    {
01263       socketClose(context->dataSocket);
01264       context->dataSocket = NULL;
01265    }
01266 
01267 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01268    if(context->controlTlsContext != NULL)
01269    {
01270       //Gracefully close the control connection
01271       tlsShutdown(context->controlTlsContext);
01272 
01273       //Release SSL context (control connection)
01274       tlsFree(context->controlTlsContext);
01275       context->controlTlsContext = NULL;
01276    }
01277 #endif
01278 
01279    //Close control socket
01280    if(context->controlSocket)
01281    {
01282       socketClose(context->controlSocket);
01283       context->controlSocket = NULL;
01284    }
01285 
01286    //Successful processing
01287    return NO_ERROR;
01288 }
01289 
01290 
01291 /**
01292  * @brief Send FTP command and wait for a reply
01293  * @param[in] context Pointer to the FTP client context
01294  * @param[in] command Command line
01295  * @param[out] replyCode Response code from the FTP server
01296  * @return Error code
01297  **/
01298 
01299 error_t ftpSendCommand(FtpClientContext *context,
01300    const char_t *command, uint_t *replyCode)
01301 {
01302    error_t error;
01303    size_t length;
01304    char_t *p;
01305 
01306    //Any command line to send?
01307    if(command)
01308    {
01309       //Debug message
01310       TRACE_DEBUG("FTP client: %s", command);
01311 
01312 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01313       if(context->controlTlsContext != NULL)
01314       {
01315          //Send the command to the FTP server
01316          error = tlsWrite(context->controlTlsContext, command,
01317             strlen(command), NULL, SOCKET_FLAG_WAIT_ACK);
01318       }
01319       else
01320 #endif
01321       {
01322          //Send the command to the FTP server
01323          error = socketSend(context->controlSocket, command,
01324             strlen(command), NULL, SOCKET_FLAG_WAIT_ACK);
01325       }
01326 
01327       //Failed to send command?
01328       if(error)
01329          return error;
01330    }
01331 
01332    //Multiline replies are allowed for any command
01333    while(1)
01334    {
01335 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01336       if(context->controlTlsContext != NULL)
01337       {
01338          //Wait for a response from the server
01339          error = tlsRead(context->controlTlsContext, context->buffer,
01340             FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
01341       }
01342       else
01343 #endif
01344       {
01345          //Wait for a response from the server
01346          error = socketReceive(context->controlSocket, context->buffer,
01347             FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF);
01348       }
01349 
01350       //The remote server did not respond as expected?
01351       if(error)
01352          return error;
01353 
01354       //Point to the beginning of the buffer
01355       p = context->buffer;
01356       //Properly terminate the string with a NULL character
01357       p[length] = '\0';
01358 
01359       //Remove trailing whitespace from the response
01360       strRemoveTrailingSpace(p);
01361 
01362       //Debug message
01363       TRACE_DEBUG("FTP server: %s\r\n", p);
01364 
01365       //Check the length of the response
01366       if(strlen(p) >= 3)
01367       {
01368          //All replies begin with a three digit numeric code
01369          if(isdigit((uint8_t) p[0]) && isdigit((uint8_t) p[1]) && isdigit((uint8_t) p[2]))
01370          {
01371             //A space character follows the response code for the last line
01372             if(p[3] == ' ' || p[3] == '\0')
01373             {
01374                //Get the server response code
01375                *replyCode = strtoul(p, NULL, 10);
01376                //Exit immediately
01377                break;
01378             }
01379          }
01380       }
01381    }
01382 
01383    //Successful processing
01384    return NO_ERROR;
01385 }
01386 
01387 
01388 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED)
01389 
01390 /**
01391  * @brief Register SSL initialization callback function
01392  * @param[in] context Pointer to the FTP client context
01393  * @param[in] callback SSL initialization callback function
01394  * @return Error code
01395  **/
01396 
01397 error_t ftpRegisterTlsInitCallback(FtpClientContext *context,
01398    FtpClientTlsInitCallback callback)
01399 {
01400    //Check parameters
01401    if(context == NULL || callback == NULL)
01402       return ERROR_INVALID_PARAMETER;
01403 
01404    //Save callback function
01405    context->tlsInitCallback = callback;
01406 
01407    //Successful processing
01408    return NO_ERROR;
01409 }
01410 
01411 
01412 /**
01413  * @brief SSL initialization (control connection)
01414  * @param[in] context Pointer to the FTP client context
01415  * @return Error code
01416  **/
01417 
01418 error_t ftpInitControlTlsContext(FtpClientContext *context)
01419 {
01420    error_t error;
01421 
01422    //Debug message
01423    TRACE_DEBUG("FTP Client: Initializing SSL session (control)...\r\n");
01424 
01425    //Allocate SSL context
01426    context->controlTlsContext = tlsInit();
01427 
01428    //Initialization failed?
01429    if(context->controlTlsContext == NULL)
01430       return ERROR_OUT_OF_MEMORY;
01431 
01432    //Start of exception handling block
01433    do
01434    {
01435       //Select the relevant operation mode
01436       error = tlsSetConnectionEnd(context->controlTlsContext, TLS_CONNECTION_END_CLIENT);
01437       //Any error to report?
01438       if(error)
01439          break;
01440 
01441       //Bind SSL to the relevant socket
01442       error = tlsSetSocket(context->controlTlsContext, context->controlSocket);
01443       //Any error to report?
01444       if(error)
01445          break;
01446 
01447       //Check whether the SSL initialization callback has been
01448       //properly registered?
01449       if(context->tlsInitCallback != NULL)
01450       {
01451          //Perform SSL related initialization
01452          error = context->tlsInitCallback(context, context->controlTlsContext);
01453          //Any error to report?
01454          if(error)
01455             break;
01456       }
01457       else
01458       {
01459          //No callback function registered
01460          error = ERROR_NOT_CONFIGURED;
01461          break;
01462       }
01463 
01464       //Establish a secure session
01465       error = tlsConnect(context->controlTlsContext);
01466       //Any error to report?
01467       if(error)
01468          break;
01469 
01470       //Save SSL session
01471       error = tlsSaveSession(context->controlTlsContext, &context->tlsSession);
01472       //Any error to report?
01473       if(error)
01474          break;
01475 
01476       //End of exception handling block
01477    } while(0);
01478 
01479    //Any error to report?
01480    if(error)
01481    {
01482       //Clean up side effects
01483       tlsFree(context->controlTlsContext);
01484       context->controlTlsContext = NULL;
01485    }
01486 
01487    //Return status code
01488    return error;
01489 }
01490 
01491 
01492 /**
01493  * @brief SSL initialization (data connection)
01494  * @param[in] context Pointer to the FTP client context
01495  * @return Error code
01496  **/
01497 
01498 error_t ftpInitDataTlsContext(FtpClientContext *context)
01499 {
01500    error_t error;
01501 
01502    //Debug message
01503    TRACE_DEBUG("FTP Client: Initializing SSL session (data)...\r\n");
01504 
01505    //Allocate SSL context
01506    context->dataTlsContext = tlsInit();
01507 
01508    //Initialization failed?
01509    if(context->dataTlsContext == NULL)
01510       return ERROR_OUT_OF_MEMORY;
01511 
01512    //Start of exception handling block
01513    do
01514    {
01515       //Select the relevant operation mode
01516       error = tlsSetConnectionEnd(context->dataTlsContext, TLS_CONNECTION_END_CLIENT);
01517       //Any error to report?
01518       if(error)
01519          break;
01520 
01521       //Bind SSL to the relevant socket
01522       error = tlsSetSocket(context->dataTlsContext, context->dataSocket);
01523       //Any error to report?
01524       if(error)
01525          break;
01526 
01527       //Check whether the SSL initialization callback has been
01528       //properly registered?
01529       if(context->tlsInitCallback != NULL)
01530       {
01531          //Perform SSL related initialization
01532          error = context->tlsInitCallback(context, context->dataTlsContext);
01533          //Any error to report?
01534          if(error)
01535             break;
01536       }
01537       else
01538       {
01539          //No callback function registered
01540          error = ERROR_NOT_CONFIGURED;
01541          break;
01542       }
01543 
01544       //Restore SSL session
01545       error = tlsRestoreSession(context->dataTlsContext, &context->tlsSession);
01546       //Any error to report?
01547       if(error)
01548          break;
01549 
01550       //Establish a secure session
01551       error = tlsConnect(context->dataTlsContext);
01552       //Any error to report?
01553       if(error)
01554          break;
01555 
01556       //End of exception handling block
01557    } while(0);
01558 
01559    //Any error to report?
01560    if(error)
01561    {
01562       //Clean up side effects
01563       tlsFree(context->dataTlsContext);
01564       context->dataTlsContext = NULL;
01565    }
01566 
01567    //Return status code
01568    return error;
01569 }
01570 
01571 #endif
01572 
01573 #endif
01574