2 * FreeRTOS+TCP V2.0.3
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
26 /* Standard includes. */
\r
32 /* FreeRTOS includes. */
\r
33 #include "FreeRTOS.h"
\r
35 #include "portmacro.h"
\r
37 /* FreeRTOS+TCP includes. */
\r
38 #include "FreeRTOS_IP.h"
\r
39 #include "FreeRTOS_TCP_IP.h"
\r
40 #include "FreeRTOS_Sockets.h"
\r
41 #include "FreeRTOS_Stream_Buffer.h"
\r
43 /* FreeRTOS Protocol includes. */
\r
44 #include "FreeRTOS_FTP_commands.h"
\r
45 #include "FreeRTOS_TCP_server.h"
\r
46 #include "FreeRTOS_server_private.h"
\r
48 /* Remove the whole file if FTP is not supported. */
\r
49 #if( ipconfigUSE_FTP == 1 )
\r
51 #ifndef HTTP_SERVER_BACKLOG
\r
52 #define HTTP_SERVER_BACKLOG ( 12 )
\r
55 #if !defined( ARRAY_SIZE )
\r
56 #define ARRAY_SIZE( x ) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
\r
59 #if defined(__WIN32__) && !defined(ipconfigFTP_FS_USES_BACKSLAH)
\r
60 #define ipconfigFTP_FS_USES_BACKSLAH 1
\r
63 /* Some defines to make the code more readbale */
\r
64 #define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
\r
65 #define pcNEW_DIR pxClient->pxParent->pcNewDir
\r
66 #define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
\r
68 /* This FTP server will only do binary transfers */
\r
69 #define TMODE_BINARY 1
\r
70 #define TMODE_ASCII 2
\r
71 #define TMODE_7BITS 3
\r
72 #define TMODE_8BITS 4
\r
74 /* Ascii character definitions. */
\r
75 #define ftpASCII_CR 13
\r
76 #define ftpASCII_LF 10
\r
78 #if defined( FTP_WRITES_ALIGNED ) || defined( ipconfigFTP_WRITES_ALIGNED )
\r
79 #error Name change : please rename the define to the new name 'ipconfigFTP_ZERO_COPY_ALIGNED_WRITES'
\r
83 * ipconfigFTP_ZERO_COPY_ALIGNED_WRITES : experimental optimisation option.
\r
84 * If non-zero, receiving data will be done with the zero-copy method and also
\r
85 * writes to disk will be done with sector-alignment as much as possible.
\r
87 #ifndef ipconfigFTP_ZERO_COPY_ALIGNED_WRITES
\r
88 #define ipconfigFTP_ZERO_COPY_ALIGNED_WRITES 0
\r
92 * This module only has 2 public functions:
\r
94 BaseType_t xFTPClientWork( TCPClient_t *pxClient );
\r
95 void vFTPClientDelete( TCPClient_t *pxClient );
\r
98 * Process a single command.
\r
100 static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand );
\r
103 * Create a socket for a data connection to the FTP client.
\r
105 static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen );
\r
108 * Either call listen() or connect() to start the transfer connection.
\r
110 static BaseType_t prvTransferStart( FTPClient_t *pxClient );
\r
113 * See if the socket has got connected or disconnected. Close the socket if
\r
116 static void prvTransferCheck( FTPClient_t *pxClient );
\r
119 * Close the data socket and issue some informative logging.
\r
121 static void prvTransferCloseSocket( FTPClient_t *pxClient );
\r
124 * Close the file handle (pxReadHandle or pxWriteHandle).
\r
126 static void prvTransferCloseFile( FTPClient_t *pxClient );
\r
129 * Close a directory (-handle).
\r
131 static void prvTransferCloseDir( FTPClient_t *pxClient );
\r
134 * Translate a string (indicating a transfer type) to a number.
\r
136 static BaseType_t prvGetTransferType( const char *pcType );
\r
138 #if( ipconfigHAS_PRINTF != 0 )
\r
140 * For nice logging: write an amount (number of bytes), e.g. 3512200 as
\r
143 static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize );
\r
146 #if( ipconfigHAS_PRINTF != 0 )
\r
148 * Calculate the average as bytes-per-second, when amount and milliseconds
\r
151 static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs );
\r
155 * A port command looks like: PORT h1,h2,h3,h4,p1,p2. Parse it and translate it
\r
156 * to an IP-address and a port number.
\r
158 static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress );
\r
161 * CWD: Change current working directory.
\r
164 static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory );
\r
167 * RNFR: Rename from ...
\r
169 static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName );
\r
172 * RNTO: Rename to ...
\r
174 static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName );
\r
177 * SITE: Change file permissions.
\r
179 static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand );
\r
182 * DELE: Delete a file.
\r
184 static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName );
\r
187 * SIZE: get the size of a file (xSendDate = 0).
\r
188 * MDTM: get data and time properties (xSendDate = 1).
\r
190 static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate );
\r
193 * MKD: Make / create a directory (xDoRemove = 0).
\r
194 * RMD: Remove a directory (xDoRemove = 1).
\r
196 static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove );
\r
199 * The next three commands: LIST, RETR and STOR all require a data socket.
\r
200 * The data connection is either started with a 'PORT' or a 'PASV' command.
\r
201 * Each of the commands has a prepare- (Prep) and a working- (Work) function.
\r
202 * The Work function should be called as long as the data socket is open, and
\r
203 * there is data to be transmitted.
\r
207 * LIST: Send a directory listing in Unix style.
\r
209 static BaseType_t prvListSendPrep( FTPClient_t *pxClient );
\r
210 static BaseType_t prvListSendWork( FTPClient_t *pxClient );
\r
213 * RETR: Send a file to the FTP client.
\r
215 static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName );
\r
216 static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient );
\r
219 * STOR: Receive a file from the FTP client and store it.
\r
221 static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName );
\r
222 static BaseType_t prvStoreFileWork( FTPClient_t *pxClient );
\r
225 * Print/format a single directory entry in Unix style.
\r
227 static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength );
\r
230 * Send a reply to a socket, either the command- or the data-socket.
\r
232 static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength );
\r
235 * Prepend the root directory (if any), plus the current working directory
\r
236 * (always), to get an absolute path.
\r
238 BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcPath );
\r
242 ####### ##### ###### # # ##
\r
243 # ## # # # # # # # #
\r
245 # # # # # # # #### ### ## # #
\r
246 ##### # ##### # # # # # # # # # #
\r
247 # # # # # # # # # ## # ####
\r
248 # # # ## ## # # # # #
\r
249 # # # ## ## # # # # #
\r
250 #### #### #### ## ## #### #### ## ##
\r
253 * will be called by FreeRTOS_TCPServerWork(), after select has expired().
\r
254 * FD_ISSET will not be used. This work function will always be called at
\r
255 * regular intervals, and also after a select() event has occurred.
\r
257 BaseType_t xFTPClientWork( TCPClient_t *pxTCPClient )
\r
259 FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;
\r
262 if( pxClient->bits.bHelloSent == pdFALSE_UNSIGNED )
\r
264 BaseType_t xLength;
\r
266 pxClient->bits.bHelloSent = pdTRUE_UNSIGNED;
\r
268 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
269 "220 Welcome to the FreeRTOS+TCP FTP server\r\n" );
\r
270 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
273 /* Call recv() in a non-blocking way, to see if there is an FTP command
\r
274 sent to this server. */
\r
275 xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
\r
280 const FTPCommand_t *pxCommand;
\r
281 char *pcRestCommand;
\r
283 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
285 pcCOMMAND_BUFFER[ xRc ] = '\0';
\r
288 while( xRc && ( ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_CR ) || ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_LF ) ) )
\r
290 pcCOMMAND_BUFFER[ --xRc ] = '\0';
\r
293 /* Now iterate through a list of FTP commands, and look for a match. */
\r
294 pxCommand = xFTPCommands;
\r
295 pcRestCommand = pcCOMMAND_BUFFER;
\r
296 for( xIndex = 0; xIndex < FTP_CMD_COUNT - 1; xIndex++, pxCommand++ )
\r
298 BaseType_t xLength;
\r
300 /* The length of each command is stored as well, just to be a bit
\r
302 xLength = pxCommand->xCommandLength;
\r
304 if( ( xRc >= xLength ) && ( memcmp( ( const void * ) pxCommand->pcCommandName, ( const void * ) pcCOMMAND_BUFFER, xLength ) == 0 ) )
\r
306 /* A match with an existing command is found. Skip any
\r
307 whitespace to get the first parameter. */
\r
308 pcRestCommand += xLength;
\r
309 while( ( *pcRestCommand == ' ' ) || ( *pcRestCommand == '\t' ) )
\r
317 /* If the command received was not recognised, xIndex will point to a
\r
318 fake entry called 'ECMD_UNKNOWN'. */
\r
319 prvProcessCommand( pxClient, xIndex, pcRestCommand );
\r
323 /* The connection will be closed and the client will be deleted. */
\r
324 FreeRTOS_printf( ( "xFTPClientWork: xRc = %ld\n", xRc ) );
\r
327 /* Does it have an open data connection? */
\r
328 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
\r
330 /* See if the connection has changed. */
\r
331 prvTransferCheck( pxClient );
\r
333 /* "pcConnectionAck" contains a string like:
\r
334 "Response: 150 Accepted data connection from 192.168.2.3:6789"
\r
335 The socket can only be used once this acknowledgement has been sent. */
\r
336 if( ( pxClient->xTransferSocket != FREERTOS_NO_SOCKET ) && ( pxClient->pcConnectionAck[ 0 ] == '\0' ) )
\r
338 BaseType_t xClientRc = 0;
\r
340 if( pxClient->bits1.bDirHasEntry )
\r
342 /* Still listing a directory. */
\r
343 xClientRc = prvListSendWork( pxClient );
\r
345 else if( pxClient->pxReadHandle != NULL )
\r
347 /* Sending a file. */
\r
348 xClientRc = prvRetrieveFileWork( pxClient );
\r
350 else if( pxClient->pxWriteHandle != NULL )
\r
352 /* Receiving a file. */
\r
353 xClientRc = prvStoreFileWork( pxClient );
\r
356 if( xClientRc < 0 )
\r
358 prvTransferCloseSocket( pxClient );
\r
359 prvTransferCloseFile( pxClient );
\r
366 /*-----------------------------------------------------------*/
\r
368 static void prvTransferCloseDir( FTPClient_t *pxClient )
\r
370 /* Nothing to close for +FAT. */
\r
373 /*-----------------------------------------------------------*/
\r
375 void vFTPClientDelete( TCPClient_t *pxTCPClient )
\r
377 FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;
\r
379 /* Close any directory-listing-handles (not used by +FAT ). */
\r
380 prvTransferCloseDir( pxClient );
\r
381 /* Close the data-socket. */
\r
382 prvTransferCloseSocket( pxClient );
\r
383 /* Close any open file handle. */
\r
384 prvTransferCloseFile( pxClient );
\r
386 /* Close the FTP command socket */
\r
387 if( pxClient->xSocket != FREERTOS_NO_SOCKET )
\r
389 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
390 FreeRTOS_closesocket( pxClient->xSocket );
\r
391 pxClient->xSocket = FREERTOS_NO_SOCKET;
\r
394 /*-----------------------------------------------------------*/
\r
396 static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand )
\r
398 const FTPCommand_t *pxFTPCommand = &( xFTPCommands[ xIndex ] );
\r
399 const char *pcMyReply = NULL;
\r
400 BaseType_t xResult = 0;
\r
402 if( ( pxFTPCommand->ucCommandType != ECMD_PASS ) && ( pxFTPCommand->ucCommandType != ECMD_PORT ) )
\r
404 FreeRTOS_printf( ( " %s %s\n", pxFTPCommand->pcCommandName, pcRestCommand ) );
\r
407 if( ( pxFTPCommand->checkLogin != pdFALSE ) && ( pxClient->bits.bLoggedIn == pdFALSE_UNSIGNED ) )
\r
409 pcMyReply = REPL_530; /* Please first log in. */
\r
411 else if( ( pxFTPCommand->checkNullArg != pdFALSE ) && ( ( pcRestCommand == NULL ) || ( pcRestCommand[ 0 ] == '\0' ) ) )
\r
413 pcMyReply = REPL_501; /* Command needs a parameter. */
\r
416 if( pcMyReply == NULL )
\r
418 switch( pxFTPCommand->ucCommandType )
\r
420 case ECMD_USER: /* User. */
\r
421 /* User name has been entered, expect password. */
\r
422 pxClient->bits.bStatusUser = pdTRUE_UNSIGNED;
\r
424 #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */
\r
426 /* Save the user name in 'pcFileName'. */
\r
427 snprintf( pxClient->pcFileName, sizeof( pxClient->pcFileName ), "%s", pcRestCommand );
\r
429 /* The USER name is presented to the application. The function
\r
430 may return a const string like "331 Please enter your
\r
432 pcMyReply = pcApplicationFTPUserHook( pxClient->pcFileName );
\r
433 if( pcMyReply == NULL )
\r
435 pcMyReply = REPL_331_ANON;
\r
440 /* No password checks, any password will be accepted. */
\r
441 pcMyReply = REPL_331_ANON;
\r
443 #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 */
\r
445 #if( ipconfigFTP_HAS_USER_PROPERTIES_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */
\r
447 FTPUserProperties_t xProperties;
\r
449 xProperties.pcRootDir = pxClient->pcRootDir;
\r
450 xProperties.xReadOnly = pdFALSE;
\r
451 xProperties.usPortNumber = pxClient->usClientPort;
\r
452 vApplicationFTPUserPropertiesHook( pxClient->pcFileName, &( xProperties ) );
\r
454 if( xProperties.pcRootDir != NULL )
\r
456 pxClient->pcRootDir = xProperties.pcRootDir;
\r
458 pxClient->bits.bReadOnly = ( xProperties.xReadOnly != pdFALSE_UNSIGNED );
\r
460 #endif /* ipconfigFTP_HAS_USER_PROPERTIES_HOOK */
\r
463 case ECMD_PASS: /* Password. */
\r
464 pxClient->ulRestartOffset = 0;
\r
465 if( pxClient->bits.bStatusUser == pdFALSE_UNSIGNED )
\r
467 pcMyReply = REPL_503; /* "503 Bad sequence of commands.\r\n". */
\r
473 pxClient->bits.bStatusUser = pdFALSE_UNSIGNED;
\r
474 #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )
\r
476 xAllow = xApplicationFTPPasswordHook( pxClient->pcFileName, pcRestCommand );
\r
482 #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */
\r
486 pxClient->bits.bLoggedIn = pdTRUE_UNSIGNED; /* Client has now logged in. */
\r
487 pcMyReply = "230 OK. Current directory is /\r\n";
\r
491 pcMyReply = "530 Login incorrect\r\n"; /* 530 Login incorrect. */
\r
494 strcpy( pxClient->pcCurrentDir, ( const char * ) "/" );
\r
498 case ECMD_SYST: /* System. */
\r
499 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "215 UNIX Type: L8\r\n" );
\r
500 pcMyReply = pcCOMMAND_BUFFER;
\r
503 case ECMD_PWD: /* Get working directory. */
\r
504 xMakeRelative( pxClient, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), pxClient->pcCurrentDir );
\r
505 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_257_PWD, pcFILE_BUFFER );
\r
506 pcMyReply = pcCOMMAND_BUFFER;
\r
510 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
512 pcMyReply = REPL_553_READ_ONLY;
\r
516 const char *pcPtr = pcRestCommand;
\r
518 while( *pcPtr == ' ' )
\r
523 if( ( *pcPtr >= '0' ) && ( *pcPtr <= '9' ) )
\r
525 sscanf( pcPtr, "%lu", &pxClient->ulRestartOffset );
\r
526 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
527 "350 Restarting at %lu. Send STORE or RETRIEVE\r\n", pxClient->ulRestartOffset );
\r
528 pcMyReply = pcCOMMAND_BUFFER;
\r
532 pcMyReply = REPL_500; /* 500 Syntax error, command unrecognised. */
\r
537 case ECMD_NOOP: /* NOP operation */
\r
538 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
\r
540 pcMyReply = REPL_200_PROGRESS;
\r
544 pcMyReply = REPL_200;
\r
548 case ECMD_TYPE: /* Ask or set transfer type. */
\r
550 /* e.g. "TYPE I" for Images (binary). */
\r
551 BaseType_t xType = prvGetTransferType( pcRestCommand );
\r
555 /* TYPE not recognised. */
\r
556 pcMyReply = REPL_500;
\r
560 pxClient->xTransType = xType;
\r
561 pcMyReply = REPL_200;
\r
566 case ECMD_PASV: /* Enter passive mode. */
\r
567 /* Connect passive: Server will listen() and wait for a connection.
\r
568 Start up a new data connection with 'xDoListen' set to true. */
\r
569 if( prvTransferConnect( pxClient, pdTRUE ) != pdTRUE )
\r
571 pcMyReply = REPL_502;
\r
577 struct freertos_sockaddr xLocalAddress;
\r
578 struct freertos_sockaddr xRemoteAddress;
\r
580 FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );
\r
581 FreeRTOS_GetRemoteAddress( pxClient->xSocket, &xRemoteAddress );
\r
583 ulIP = FreeRTOS_ntohl( xLocalAddress.sin_addr );
\r
584 pxClient->ulClientIP = FreeRTOS_ntohl( xRemoteAddress.sin_addr );
\r
585 ulPort = FreeRTOS_ntohs( xLocalAddress.sin_port );
\r
587 pxClient->usClientPort = FreeRTOS_ntohs( xRemoteAddress.sin_port );
\r
589 /* REPL_227_D "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d). */
\r
590 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_227_D,
\r
591 ( unsigned )ulIP >> 24,
\r
592 ( unsigned )( ulIP >> 16 ) & 0xFF,
\r
593 ( unsigned )( ulIP >> 8 ) & 0xFF,
\r
594 ( unsigned )ulIP & 0xFF,
\r
595 ( unsigned )ulPort >> 8,
\r
596 ( unsigned )ulPort & 0xFF );
\r
598 pcMyReply = pcCOMMAND_BUFFER;
\r
602 case ECMD_PORT: /* Active connection to the client. */
\r
603 /* The client uses this command to tell the server to what
\r
604 client-side port the server should contact; use of this command
\r
605 indicates an active data transfer. e.g. PORT 192,168,1,2,4,19. */
\r
607 uint32_t ulIPAddress = 0;
\r
608 UBaseType_t uxPort;
\r
610 uxPort = prvParsePortData( pcRestCommand, &ulIPAddress );
\r
611 FreeRTOS_printf( (" PORT %lxip:%ld\n", ulIPAddress, uxPort ) );
\r
615 pcMyReply = REPL_501;
\r
617 else if( prvTransferConnect( pxClient, pdFALSE ) != pdTRUE )
\r
619 /* Call prvTransferConnect() with 'xDoListen' = false for an
\r
620 active connect(). */
\r
621 pcMyReply = REPL_501;
\r
625 pxClient->usClientPort = ( uint16_t ) uxPort;
\r
626 pxClient->ulClientIP = ulIPAddress;
\r
627 FreeRTOS_printf( ("Client address %lxip:%lu\n", ulIPAddress, uxPort ) );
\r
628 pcMyReply = REPL_200;
\r
633 case ECMD_CWD: /* Change current working directory. */
\r
634 prvChangeDir( pxClient, pcRestCommand );
\r
638 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
640 pcMyReply = REPL_553_READ_ONLY;
\r
644 prvRenameFrom( pxClient, pcRestCommand );
\r
649 if( pxClient->bits.bInRename == pdFALSE_UNSIGNED )
\r
651 pcMyReply = REPL_503; /* "503 Bad sequence of commands. */
\r
655 prvRenameTo( pxClient, pcRestCommand );
\r
659 case ECMD_SITE: /* Set file permissions */
\r
660 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
662 pcMyReply = REPL_553_READ_ONLY;
\r
664 else if( prvSiteCmd( pxClient, pcRestCommand ) == pdFALSE )
\r
666 pcMyReply = REPL_202;
\r
671 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
673 pcMyReply = REPL_553_READ_ONLY;
\r
677 prvDeleteFile( pxClient, pcRestCommand );
\r
682 prvSizeDateFile( pxClient, pcRestCommand, pdTRUE );
\r
686 if( pxClient->pxWriteHandle != NULL )
\r
688 /* This SIZE query is probably about a file which is now being
\r
689 received. If so, return the value of pxClient->ulRecvBytes,
\r
690 pcRestCommand points to 'pcCommandBuffer', make it free by
\r
691 copying it to pcNewDir. */
\r
693 xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcRestCommand );
\r
695 if( strcmp( pcNEW_DIR, pcRestCommand ) == 0 )
\r
698 for( xCount = 0; xCount < 3 && pxClient->pxWriteHandle; xCount++ )
\r
700 prvStoreFileWork( pxClient );
\r
702 if( pxClient->pxWriteHandle != NULL )
\r
704 /* File being queried is still open, return number of
\r
705 bytes received until now. */
\r
706 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", pxClient->ulRecvBytes );
\r
707 pcMyReply = pcCOMMAND_BUFFER;
\r
708 } /* otherwise, do a normal stat(). */
\r
710 strcpy( pcRestCommand, pcNEW_DIR );
\r
712 if( pcMyReply == NULL )
\r
714 prvSizeDateFile( pxClient, pcRestCommand, pdFALSE );
\r
719 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
721 pcMyReply = REPL_553_READ_ONLY;
\r
725 prvMakeRemoveDir( pxClient, pcRestCommand, pxFTPCommand->ucCommandType == ECMD_RMD );
\r
729 prvChangeDir( pxClient, ".." );
\r
733 prvSendReply( pxClient->xSocket, REPL_221, 0 );
\r
734 pxClient->bits.bLoggedIn = pdFALSE_UNSIGNED;
\r
739 if( ( pxClient->xTransferSocket == FREERTOS_NO_SOCKET ) &&
\r
740 ( ( pxFTPCommand->ucCommandType != ECMD_STOR ) ||
\r
741 ( pxClient->bits1.bEmptyFile == pdFALSE_UNSIGNED ) ) )
\r
743 /* Sending "425 Can't open data connection." :
\r
744 Before receiving any of these commands, there must have been a
\r
745 PORT or PASV command, which causes the creation of a data socket. */
\r
746 /* There is one exception: a STOR command is received while the
\r
747 data connection has already been closed. This is tested with the
\r
748 'bEmptyFile' flag. */
\r
749 pcMyReply = REPL_425;
\r
753 /* In case an empty file was received ( bits1.bEmptyFile ), the
\r
754 transfer socket never delivered any data. Check if the transfer
\r
755 socket is still open: */
\r
756 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
\r
758 prvTransferCheck( pxClient );
\r
760 switch( pxFTPCommand->ucCommandType )
\r
763 prvListSendPrep( pxClient );
\r
766 prvRetrieveFilePrep( pxClient, pcRestCommand );
\r
769 if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
\r
771 pcMyReply = REPL_553_READ_ONLY;
\r
775 prvStoreFilePrep( pxClient, pcRestCommand );
\r
776 if( pxClient->bits1.bEmptyFile != pdFALSE_UNSIGNED )
\r
778 /* Although the 'xTransferSocket' is closed already,
\r
779 call this function just for the logging. */
\r
780 prvTransferCloseSocket( pxClient );
\r
782 /* Close an empty file. */
\r
783 prvTransferCloseFile( pxClient );
\r
793 static const char pcFeatAnswer[] =
\r
794 "211-Features:\x0a"
\r
795 /* The MDTM command is only allowed when
\r
796 there is support for date&time. */
\r
797 #if( ffconfigTIME_SUPPORT != 0 )
\r
803 pcMyReply = pcFeatAnswer;
\r
808 FreeRTOS_printf( ("ftp::processCmd: Cmd %s unknown\n", pcRestCommand ) );
\r
809 pcMyReply = REPL_500;
\r
813 if( pxFTPCommand->ucCommandType != ECMD_RNFR )
\r
815 pxClient->bits.bInRename = pdFALSE_UNSIGNED;
\r
818 if( pcMyReply != NULL )
\r
820 xResult = prvSendReply( pxClient->xSocket, pcMyReply, strlen( pcMyReply ) );
\r
825 /*-----------------------------------------------------------*/
\r
827 static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen )
\r
830 BaseType_t xResult;
\r
832 /* Open a socket for a data connection with the FTP client.
\r
833 Happens after a PORT or a PASV command. */
\r
835 /* Make sure the previous socket is deleted and flags reset */
\r
836 prvTransferCloseSocket( pxClient );
\r
838 pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
\r
840 xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
\r
842 if( ( xSocket != FREERTOS_NO_SOCKET ) && ( xSocket != FREERTOS_INVALID_SOCKET ) )
\r
844 BaseType_t xSmallTimeout = pdMS_TO_TICKS( 100 );
\r
845 struct freertos_sockaddr xAddress;
\r
847 #if( ipconfigFTP_TX_BUFSIZE > 0 )
\r
848 WinProperties_t xWinProps;
\r
850 xAddress.sin_addr = FreeRTOS_GetIPAddress( ); /* Single NIC, currently not used */
\r
851 xAddress.sin_port = FreeRTOS_htons( 0 ); /* Bind to any available port number */
\r
853 BaseType_t xBindResult;
\r
854 xBindResult = FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
\r
855 if ( xBindResult != 0 )
\r
857 FreeRTOS_printf( ( "FreeRTOS_bind() failed\n" ) );
\r
858 return xBindResult;
\r
861 #if( ipconfigFTP_TX_BUFSIZE > 0 )
\r
863 /* Fill in the buffer and window sizes that will be used by the
\r
865 xWinProps.lTxBufSize = ipconfigFTP_TX_BUFSIZE;
\r
866 xWinProps.lTxWinSize = ipconfigFTP_TX_WINSIZE;
\r
867 xWinProps.lRxBufSize = ipconfigFTP_RX_BUFSIZE;
\r
868 xWinProps.lRxWinSize = ipconfigFTP_RX_WINSIZE;
\r
870 /* Set the window and buffer sizes. */
\r
871 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) );
\r
875 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );
\r
876 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );
\r
878 /* The same instance of the socket will be used for the connection and
\r
880 if( xDoListen != pdFALSE )
\r
882 BaseType_t xTrueValue = pdTRUE;
\r
883 FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_REUSE_LISTEN_SOCKET, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
\r
885 pxClient->bits1.bIsListen = xDoListen;
\r
886 pxClient->xTransferSocket = xSocket;
\r
888 if( xDoListen != pdFALSE )
\r
890 FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ );
\r
891 /* Calling FreeRTOS_listen( ) */
\r
892 xResult = prvTransferStart( pxClient );
\r
900 FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ | eSELECT_WRITE );
\r
906 FreeRTOS_printf( ( "FreeRTOS_socket() failed\n" ) );
\r
907 xResult = -pdFREERTOS_ERRNO_ENOMEM;
\r
910 /* An active socket (PORT) should connect() later. */
\r
913 /*-----------------------------------------------------------*/
\r
915 static BaseType_t prvTransferStart( FTPClient_t *pxClient )
\r
917 BaseType_t xResult;
\r
919 /* A transfer socket has been opened, now either call listen() for 'PASV'
\r
920 or connect() for the 'PORT' command. */
\r
921 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
\r
923 xResult = FreeRTOS_listen( pxClient->xTransferSocket, 1 );
\r
927 struct freertos_sockaddr xAddress;
\r
929 xAddress.sin_addr = FreeRTOS_htonl( pxClient->ulClientIP );
\r
930 xAddress.sin_port = FreeRTOS_htons( pxClient->usClientPort );
\r
931 /* Start an active connection for this data socket */
\r
932 xResult = FreeRTOS_connect( pxClient->xTransferSocket, &xAddress, sizeof( xAddress ) );
\r
937 /*-----------------------------------------------------------*/
\r
939 static void prvTransferCheck( FTPClient_t *pxClient )
\r
941 BaseType_t xRxSize;
\r
943 /* A data transfer is busy. Check if there are changes in connectedness. */
\r
944 xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );
\r
946 if( pxClient->bits1.bClientConnected == pdFALSE_UNSIGNED )
\r
948 /* The time to receive a small file can be so short, that we don't even
\r
949 see that the socket gets connected and disconnected. Therefore, check
\r
950 the sizeof of the RX buffer. */
\r
952 struct freertos_sockaddr xAddress;
\r
953 Socket_t xNexSocket;
\r
954 socklen_t xSocketLength = sizeof( xAddress );
\r
956 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
\r
958 xNexSocket = FreeRTOS_accept( pxClient->xTransferSocket, &xAddress, &xSocketLength);
\r
959 if( ( ( xNexSocket != FREERTOS_NO_SOCKET ) && ( xNexSocket != FREERTOS_INVALID_SOCKET ) ) ||
\r
962 pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;
\r
967 if( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) > 0 ||
\r
970 pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;
\r
973 if( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
\r
975 pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
\r
976 #if( ipconfigHAS_PRINTF != 0 )
\r
978 struct freertos_sockaddr xRemoteAddress, xLocalAddress;
\r
979 FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );
\r
980 FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );
\r
981 FreeRTOS_printf( ( "%s Connected from %u to %u\n",
\r
982 pxClient->bits1.bIsListen != pdFALSE_UNSIGNED ? "PASV" : "PORT",
\r
983 ( unsigned ) FreeRTOS_ntohs( xLocalAddress.sin_port ),
\r
984 ( unsigned ) FreeRTOS_ntohs( xRemoteAddress.sin_port ) ) );
\r
986 #endif /* ipconfigHAS_PRINTF */
\r
987 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
988 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_READ|eSELECT_EXCEPT );
\r
993 if ( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
\r
995 if( pxClient->pcConnectionAck[ 0 ] != '\0' )
\r
997 BaseType_t xLength;
\r
998 BaseType_t xRemotePort;
\r
999 struct freertos_sockaddr xRemoteAddress;
\r
1001 FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );
\r
1002 xRemotePort = FreeRTOS_ntohs( xRemoteAddress.sin_port );
\r
1004 /* Tell on the command port 21 we have a data connection */
\r
1005 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
1006 pxClient->pcConnectionAck, pxClient->ulClientIP, xRemotePort );
\r
1008 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1009 pxClient->pcConnectionAck[ 0 ] = '\0';
\r
1012 if( ( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) == pdFALSE ) && FreeRTOS_rx_size( pxClient->xTransferSocket ) == 0 )
\r
1014 prvTransferCloseSocket( pxClient );
\r
1015 prvTransferCloseFile( pxClient );
\r
1019 /*-----------------------------------------------------------*/
\r
1021 static void prvTransferCloseSocket( FTPClient_t *pxClient )
\r
1023 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
\r
1025 /* DEBUGGING ONLY */
\r
1026 BaseType_t xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );
\r
1029 BaseType_t xRxSize2;
\r
1030 BaseType_t xStatus;
\r
1031 prvStoreFileWork( pxClient );
\r
1032 xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );
\r
1033 xRxSize2 = FreeRTOS_rx_size( pxClient->xTransferSocket );
\r
1034 FreeRTOS_printf( ( "FTP: WARNING: %s: RX size = %ld -> %ld (%s)\n",
\r
1035 FreeRTOS_GetTCPStateName( xStatus ),
\r
1036 xRxSize, xRxSize2, pxClient->pcFileName ) );
\r
1037 if( xRxSize2 > 1 )
\r
1042 /* Remove compiler warnings in case FreeRTOS_printf() is not
\r
1048 if( ( pxClient->pxWriteHandle != NULL ) || ( pxClient->pxReadHandle != NULL ) )
\r
1050 BaseType_t xLength;
\r
1051 char pcStrBuf[ 32 ];
\r
1053 if( pxClient->bits1.bHadError == pdFALSE_UNSIGNED )
\r
1055 xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
\r
1056 "226 Closing connection %d bytes transmitted\r\n", ( int ) pxClient->ulRecvBytes );
\r
1060 xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
\r
1061 "451 Requested action aborted after %d bytes\r\n", ( int ) pxClient->ulRecvBytes );
\r
1064 /* Tell on the command socket the data connection is now closed. */
\r
1065 prvSendReply( pxClient->xSocket, pxClient->pcClientAck, xLength );
\r
1067 #if( ipconfigHAS_PRINTF != 0 )
\r
1069 TickType_t xDelta;
\r
1070 uint32_t ulAverage;
\r
1071 xDelta = xTaskGetTickCount( ) - pxClient->xStartTime;
\r
1072 ulAverage = ulGetAverage( pxClient->ulRecvBytes, xDelta );
\r
1074 FreeRTOS_printf( ("FTP: %s: '%s' %lu Bytes (%s/sec)\n",
\r
1075 pxClient->pxReadHandle ? "sent" : "recv",
\r
1076 pxClient->pcFileName,
\r
1077 pxClient->ulRecvBytes,
\r
1078 pcMkSize( ulAverage, pcStrBuf, sizeof( pcStrBuf ) ) ) );
\r
1083 if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
\r
1085 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
1086 FreeRTOS_closesocket( pxClient->xTransferSocket );
\r
1087 pxClient->xTransferSocket = FREERTOS_NO_SOCKET;
\r
1088 if( pxClient->ulRecvBytes == 0ul )
\r
1090 /* Received zero bytes: an empty file */
\r
1091 pxClient->bits1.bEmptyFile = pdTRUE_UNSIGNED;
\r
1095 pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
\r
1098 pxClient->bits1.bIsListen = pdFALSE_UNSIGNED;
\r
1099 pxClient->bits1.bDirHasEntry = pdFALSE_UNSIGNED;
\r
1100 pxClient->bits1.bClientConnected = pdFALSE_UNSIGNED;
\r
1101 pxClient->bits1.bHadError = pdFALSE_UNSIGNED;
\r
1103 /*-----------------------------------------------------------*/
\r
1105 static void prvTransferCloseFile( FTPClient_t *pxClient )
\r
1107 if( pxClient->pxWriteHandle != NULL )
\r
1109 ff_fclose( pxClient->pxWriteHandle );
\r
1110 pxClient->pxWriteHandle = NULL;
\r
1111 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )
\r
1113 vApplicationFTPReceivedHook( pxClient->pcFileName, pxClient->ulRecvBytes, pxClient );
\r
1118 if( pxClient->pxReadHandle != NULL )
\r
1120 ff_fclose( pxClient->pxReadHandle );
\r
1121 pxClient->pxReadHandle = NULL;
\r
1123 /* These two field are only used for logging / file-statistics */
\r
1124 pxClient->ulRecvBytes = 0ul;
\r
1125 pxClient->xStartTime = 0ul;
\r
1127 /*-----------------------------------------------------------*/
\r
1130 * Guess the transfer type, given the client requested type.
\r
1131 * Actually in unix there is no difference between binary and
\r
1132 * ascii mode when we work with file descriptors.
\r
1133 * If #type is not recognized as a valid client request, -1 is returned.
\r
1135 static BaseType_t prvGetTransferType( const char *pcType )
\r
1137 BaseType_t xResult = -1;
\r
1139 if( pcType != NULL )
\r
1141 BaseType_t xLength = strlen( pcType );
\r
1142 if( xLength == 0 )
\r
1146 switch( pcType[ 0 ] ) {
\r
1148 xResult = TMODE_BINARY;
\r
1151 xResult = TMODE_ASCII;
\r
1154 if( xLength >= 3 )
\r
1156 if( pcType[ 2 ] == '7' )
\r
1158 xResult = TMODE_7BITS;
\r
1160 else if( pcType[ 2 ] == '8' )
\r
1162 xResult = TMODE_7BITS;
\r
1170 /*-----------------------------------------------------------*/
\r
1172 #if( ipconfigHAS_PRINTF != 0 )
\r
1173 #define SIZE_1_GB ( 1024ul * 1024ul * 1024ul )
\r
1174 #define SIZE_1_MB ( 1024ul * 1024ul )
\r
1175 #define SIZE_1_KB ( 1024ul )
\r
1177 static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize )
\r
1179 uint32_t ulGB, ulMB, ulKB, ulByte;
\r
1181 ulGB = ( ulAmount / SIZE_1_GB );
\r
1182 ulAmount -= ( ulGB * SIZE_1_GB );
\r
1183 ulMB = ( ulAmount / SIZE_1_MB );
\r
1184 ulAmount -= ( ulMB * SIZE_1_MB );
\r
1185 ulKB = ( ulAmount / SIZE_1_KB );
\r
1186 ulAmount -= ( ulKB * SIZE_1_KB );
\r
1187 ulByte = ( ulAmount );
\r
1191 snprintf( pcBuffer, xBufferSize, "%lu.%02lu GB", ulGB, (100 * ulMB) / SIZE_1_KB );
\r
1193 else if( ulMB != 0ul )
\r
1195 snprintf( pcBuffer, xBufferSize, "%lu.%02lu MB", ulMB, (100 * ulKB) / SIZE_1_KB );
\r
1197 else if( ulKB != 0ul )
\r
1199 snprintf(pcBuffer, xBufferSize, "%lu.%02lu KB", ulKB, (100 * ulByte) / SIZE_1_KB );
\r
1203 snprintf( pcBuffer, xBufferSize, "%lu bytes", ulByte );
\r
1208 /*-----------------------------------------------------------*/
\r
1209 #endif /* ipconfigHAS_PRINTF != 0 */
\r
1211 #if( ipconfigHAS_PRINTF != 0 )
\r
1212 static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs )
\r
1214 uint32_t ulAverage;
\r
1216 /* Get the average amount of bytes per seconds. Ideally this is
\r
1217 calculated by Multiplying with 1000 and dividing by milliseconds:
\r
1218 ulAverage = ( 1000ul * ulAmount ) / xDeltaMs;
\r
1219 Now get a maximum precision, while avoiding an arithmetic overflow:
\r
1221 if( xDeltaMs == 0ul )
\r
1223 /* Time is zero, there is no average */
\r
1226 else if( ulAmount >= ( ~0ul / 10ul ) )
\r
1228 /* More than 409 MB has been transferred, do not multiply. */
\r
1229 ulAverage = ( ulAmount / ( xDeltaMs / 1000ul ) );
\r
1231 else if( ulAmount >= ( ~0ul / 100ul ) )
\r
1233 /* Between 409 and 41 MB has been transferred, can multiply by 10. */
\r
1234 ulAverage = ( ( ulAmount * 10ul ) / ( xDeltaMs / 100ul ) );
\r
1236 else if( ulAmount >= ( ~0ul / 1000ul ) )
\r
1238 /* Between 4.1 MB and 41 has been transferred, can multiply by 100. */
\r
1239 ulAverage = ( ( ulAmount * 100ul ) / ( xDeltaMs / 10ul ) );
\r
1243 /* Less than 4.1 MB: can multiply by 1000. */
\r
1244 ulAverage = ( ( ulAmount * 1000ul ) / xDeltaMs );
\r
1249 /*-----------------------------------------------------------*/
\r
1250 #endif /* ipconfigHAS_PRINTF != 0 */
\r
1252 static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress )
\r
1254 /*_HT_ Using 'unsigned' here because when sscanf() sees '%u', it expects a pointer to 'unsigned'.
\r
1255 Not sure about the sscanf() format for UBaseType_t ? */
\r
1256 unsigned h1, h2, h3, h4, p1, p2;
\r
1258 UBaseType_t uxResult;
\r
1260 /* Expect PORT h1,h2,h3,h4,p1,p2 */
\r
1261 if (sscanf (pcCommand, "%u%c%u%c%u%c%u%c%u%c%u", &h1, &sep, &h2, &sep, &h3, &sep, &h4, &sep, &p1, &sep, &p2) != 11)
\r
1267 /* Put in network byte order. */
\r
1269 ( ( uint32_t ) h1 << 24 ) |
\r
1270 ( ( uint32_t ) h2 << 16 ) |
\r
1271 ( ( uint32_t ) h3 << 8 ) |
\r
1272 ( ( uint32_t ) h4 );
\r
1273 uxResult = ( p1 << 8 ) | p2;
\r
1277 /*-----------------------------------------------------------*/
\r
1281 #### ####### # ###
\r
1284 # ###### #### ### ## #### # # ### # ####
\r
1285 ## # # # # # # # # ##### # # # #
\r
1286 ## # # # ## # ###### # # # # ######
\r
1287 # # # # # # # # # # #
\r
1288 # # # ## # # # # ## # # # # ##
\r
1289 #### ## #### #### #### #### ##### ##### ####
\r
1293 static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName )
\r
1295 BaseType_t xResult;
\r
1296 FF_FILE *pxNewHandle;
\r
1297 size_t uxFileSize = 0ul;
\r
1300 /* Close previous handle (if any) and reset file transfer parameters. */
\r
1301 prvTransferCloseFile( pxClient );
\r
1303 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
\r
1305 pxNewHandle = NULL;
\r
1307 if( pxClient->ulRestartOffset != 0 )
\r
1309 size_t uxOffset = pxClient->ulRestartOffset;
\r
1312 pxClient->ulRestartOffset = 0ul; /* Only use 1 time. */
\r
1313 pxNewHandle = ff_fopen( pxClient->pcFileName, "ab" );
\r
1315 if( pxNewHandle != NULL )
\r
1317 uxFileSize = pxNewHandle->ulFileSize;
\r
1319 if( uxOffset <= uxFileSize )
\r
1321 lRc = ff_fseek( pxNewHandle, uxOffset, FF_SEEK_SET );
\r
1325 /* Won't even try to seek after EOF */
\r
1326 lRc = -pdFREERTOS_ERRNO_EINVAL;
\r
1330 BaseType_t xLength;
\r
1332 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
1333 "450 Seek invalid %u length %u\r\n",
\r
1334 ( unsigned ) uxOffset, ( unsigned ) uxFileSize );
\r
1336 /* "Requested file action not taken". */
\r
1337 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1339 FreeRTOS_printf( ( "ftp::storeFile: create %s: Seek %u length %u\n",
\r
1340 pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );
\r
1342 ff_fclose( pxNewHandle );
\r
1343 pxNewHandle = NULL;
\r
1349 pxNewHandle = ff_fopen( pxClient->pcFileName, "wb" );
\r
1352 if( pxNewHandle == NULL )
\r
1354 iErrorNo = stdioGET_ERRNO();
\r
1355 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )
\r
1357 prvSendReply( pxClient->xSocket, REPL_552, 0 );
\r
1361 /* "Requested file action not taken". */
\r
1362 prvSendReply( pxClient->xSocket, REPL_450, 0 );
\r
1364 FreeRTOS_printf( ( "ftp::storeFile: create %s: %s (errno %d)\n",
\r
1365 pxClient->pcFileName,
\r
1366 ( const char* ) strerror( iErrorNo ), iErrorNo ) );
\r
1368 xResult = pdFALSE;
\r
1372 if( pxClient->bits1.bIsListen )
\r
1374 /* True if PASV is used. */
\r
1375 snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
\r
1376 "150 Accepted data connection from %%xip:%%u\r\n" );
\r
1377 prvTransferCheck( pxClient );
\r
1381 BaseType_t xLength;
\r
1383 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150 Opening BIN connection to store file\r\n" );
\r
1384 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1385 pxClient->pcConnectionAck[ 0 ] = '\0';
\r
1386 prvTransferStart( pxClient ); /* Now active connect. */
\r
1389 pxClient->pxWriteHandle = pxNewHandle;
\r
1391 /* To get some statistics about the performance. */
\r
1392 pxClient->xStartTime = xTaskGetTickCount( );
\r
1399 /*-----------------------------------------------------------*/
\r
1401 #if( ipconfigFTP_ZERO_COPY_ALIGNED_WRITES == 0 )
\r
1403 static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
\r
1405 BaseType_t xRc, xWritten;
\r
1407 /* Read from the data socket until all has been read or until a negative value
\r
1413 /* The "zero-copy" method: */
\r
1414 xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
\r
1415 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
\r
1420 pxClient->ulRecvBytes += xRc;
\r
1421 xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );
\r
1422 FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );
\r
1423 if( xWritten != xRc )
\r
1426 /* bHadError: a transfer got aborted because of an error. */
\r
1427 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
\r
1434 #else /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES != 0 */
\r
1436 #if !defined( ipconfigFTP_PREFERRED_WRITE_SIZE )
\r
1437 /* If you store data on flash, it may be profitable to give 'ipconfigFTP_PREFERRED_WRITE_SIZE'
\r
1438 the same size as the size of the flash' erase blocks, e.g. 4KB */
\r
1439 #define ipconfigFTP_PREFERRED_WRITE_SIZE 512ul
\r
1442 static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
\r
1444 BaseType_t xRc, xWritten;
\r
1446 /* Read from the data socket until all has been read or until a negative
\r
1447 value is returned. */
\r
1451 UBaseType_t xStatus;
\r
1453 /* The "zero-copy" method: */
\r
1454 xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
\r
1455 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
\r
1459 /* There are no data or the connection is closed. */
\r
1462 xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );
\r
1463 if( xStatus != eESTABLISHED )
\r
1465 /* The connection is not established (any more), therefore
\r
1466 accept any amount of bytes, probably the last few bytes. */
\r
1470 if( xRc >= ipconfigFTP_PREFERRED_WRITE_SIZE )
\r
1472 /* More than a sector to write, round down to a multiple of
\r
1473 PREFERRED_WRITE_SIZE bytes. */
\r
1474 xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;
\r
1478 const StreamBuffer_t *pxBuffer = FreeRTOS_get_rx_buf( pxClient->xTransferSocket );
\r
1479 size_t uxSpace = pxBuffer->LENGTH - pxBuffer->uxTail;
\r
1481 if( uxSpace >= ipconfigFTP_PREFERRED_WRITE_SIZE )
\r
1483 /* At this moment there are les than PREFERRED_WRITE_SIZE bytes in the RX
\r
1484 buffer, but there is space for more. Just return and
\r
1490 /* Now reading beyond the end of the circular buffer,
\r
1491 use a normal read. */
\r
1492 pcBuffer = pcFILE_BUFFER;
\r
1493 xRc = FreeRTOS_recvcount( pxClient->xTransferSocket );
\r
1494 xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;
\r
1497 xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) pcBuffer,
\r
1498 sizeof( pcFILE_BUFFER ), FREERTOS_MSG_DONTWAIT );
\r
1507 pxClient->ulRecvBytes += xRc;
\r
1509 xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );
\r
1510 if( pcBuffer != pcFILE_BUFFER )
\r
1512 FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );
\r
1514 if( xWritten != xRc )
\r
1517 /* bHadError: a transfer got aborted because of an error. */
\r
1518 pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
\r
1525 #endif /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES */
\r
1526 /*-----------------------------------------------------------*/
\r
1529 ###### # ####### # ###
\r
1532 # # #### ###### ### ## ### #### # # #### # # ### # ####
\r
1533 ###### # # # # # # # # # # # # # ##### # # # #
\r
1534 # ## ###### # ## # # ###### # # ###### # # # # ######
\r
1535 # # # # # # # # # # # # # #
\r
1536 # # # ## # ## # # # ## # # # ## # # # # ##
\r
1537 ### ## #### ## #### ##### #### ## #### #### ##### ##### ####
\r
1539 static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName )
\r
1541 BaseType_t xResult = pdTRUE;
\r
1542 size_t uxFileSize;
\r
1544 /* Close previous handle (if any) and reset file transfer parameters */
\r
1545 prvTransferCloseFile( pxClient );
\r
1547 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
\r
1549 pxClient->pxReadHandle = ff_fopen( pxClient->pcFileName, "rb" );
\r
1550 if( pxClient->pxReadHandle == NULL )
\r
1552 int iErrno = stdioGET_ERRNO();
\r
1553 /* "Requested file action not taken". */
\r
1554 prvSendReply( pxClient->xSocket, REPL_450, 0 );
\r
1555 FreeRTOS_printf( ("prvRetrieveFilePrep: open '%s': errno %d: %s\n",
\r
1556 pxClient->pcFileName, iErrno, ( const char * ) strerror( iErrno ) ) );
\r
1558 xResult = pdFALSE;
\r
1562 uxFileSize = pxClient->pxReadHandle->ulFileSize;
\r
1563 pxClient->uxBytesLeft = uxFileSize;
\r
1564 if( pxClient->ulRestartOffset != 0ul )
\r
1566 size_t uxOffset = pxClient->ulRestartOffset;
\r
1569 /* Only use 1 time. */
\r
1570 pxClient->ulRestartOffset = 0;
\r
1572 if( uxOffset < uxFileSize )
\r
1574 iRc = ff_fseek( pxClient->pxReadHandle, uxOffset, FF_SEEK_SET );
\r
1578 iRc = -pdFREERTOS_ERRNO_EINVAL;
\r
1582 BaseType_t xLength;
\r
1584 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
1585 "450 Seek invalid %u length %u\r\n", ( unsigned ) uxOffset, ( unsigned ) uxFileSize );
\r
1587 /* "Requested file action not taken". */
\r
1588 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1590 FreeRTOS_printf( ( "prvRetrieveFilePrep: create %s: Seek %u length %u\n",
\r
1591 pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );
\r
1593 ff_fclose( pxClient->pxReadHandle );
\r
1594 pxClient->pxReadHandle = NULL;
\r
1595 xResult = pdFALSE;
\r
1599 pxClient->uxBytesLeft = uxFileSize - pxClient->ulRestartOffset;
\r
1603 if( xResult != pdFALSE )
\r
1605 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
\r
1607 /* True if PASV is used. */
\r
1608 snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
\r
1609 "150%cAccepted data connection from %%xip:%%u\r\n%s",
\r
1610 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',
\r
1611 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );
\r
1613 BaseType_t xLength;
\r
1615 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150%cOpening data connection to %lxip:%u\r\n%s",
\r
1616 pxClient->xTransType == TMODE_ASCII ? '-' : ' ',
\r
1617 pxClient->ulClientIP,
\r
1618 pxClient->usClientPort,
\r
1619 pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );
\r
1620 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1621 pxClient->pcConnectionAck[ 0 ] = '\0';
\r
1622 prvTransferStart( pxClient );
\r
1625 /* Prepare the ACK which will be sent when all data has been sent. */
\r
1626 snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ), "%s", REPL_226 );
\r
1628 /* To get some statistics about the performance. */
\r
1629 pxClient->xStartTime = xTaskGetTickCount( );
\r
1630 if( uxFileSize == 0ul )
\r
1632 FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
\r
1638 /*-----------------------------------------------------------*/
\r
1640 static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient )
\r
1643 size_t uxCount, uxItemsRead;
\r
1644 BaseType_t xRc = 0;
\r
1645 BaseType_t xSetEvent = pdFALSE;
\r
1649 #if( ipconfigFTP_TX_ZERO_COPY != 0 )
\r
1651 BaseType_t xBufferLength;
\r
1652 #endif /* ipconfigFTP_TX_ZERO_COPY */
\r
1654 /* Take the lesser of the two: tx_space (number of bytes that can be
\r
1655 queued for transmission) and uxBytesLeft (the number of bytes left to
\r
1656 read from the file) */
\r
1657 uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
\r
1659 if( uxSpace == 0 )
\r
1661 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE | eSELECT_EXCEPT );
\r
1662 xRc = FreeRTOS_select( pxClient->pxParent->xSocketSet, 200 );
\r
1663 uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
\r
1666 uxCount = FreeRTOS_min_uint32( pxClient->uxBytesLeft, uxSpace );
\r
1668 if( uxCount == 0 )
\r
1673 #if( ipconfigFTP_TX_ZERO_COPY == 0 )
\r
1675 if( uxCount > sizeof( pcFILE_BUFFER ) )
\r
1677 uxCount = sizeof( pcFILE_BUFFER );
\r
1679 uxItemsRead = ff_fread( pcFILE_BUFFER, 1, uxCount, pxClient->pxReadHandle );
\r
1680 if( uxItemsRead != uxCount )
\r
1682 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned ) uxCount ) );
\r
1683 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
\r
1684 pxClient->uxBytesLeft = 0u;
\r
1687 pxClient->uxBytesLeft -= uxCount;
\r
1689 if( pxClient->uxBytesLeft == 0u )
\r
1691 BaseType_t xTrueValue = 1;
\r
1693 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
\r
1696 xRc = FreeRTOS_send( pxClient->xTransferSocket, pcFILE_BUFFER, uxCount, 0 );
\r
1698 #else /* ipconfigFTP_TX_ZERO_COPY != 0 */
\r
1700 /* Use zero-copy transmission:
\r
1701 FreeRTOS_get_tx_head() returns a direct pointer to the TX stream and
\r
1702 set xBufferLength to know how much space there is left. */
\r
1703 pcBuffer = ( char * )FreeRTOS_get_tx_head( pxClient->xTransferSocket, &xBufferLength );
\r
1704 if( ( pcBuffer != NULL ) && ( xBufferLength >= 512 ) )
\r
1706 /* Will read disk data directly to the TX stream of the socket. */
\r
1707 uxCount = FreeRTOS_min_uint32( uxCount, ( uint32_t )xBufferLength );
\r
1708 if( uxCount > ( size_t ) 0x40000u )
\r
1710 uxCount = ( size_t ) 0x40000u;
\r
1715 /* Use the normal file i/o buffer. */
\r
1716 pcBuffer = pcFILE_BUFFER;
\r
1717 if( uxCount > sizeof( pcFILE_BUFFER ) )
\r
1719 uxCount = sizeof( pcFILE_BUFFER );
\r
1723 if ( pxClient->uxBytesLeft >= 1024u )
\r
1725 uxCount &= ~( ( size_t ) 512u - 1u );
\r
1728 if( uxCount <= 0u )
\r
1730 /* Nothing to send after rounding down to a multiple of a sector size. */
\r
1734 uxItemsRead = ff_fread( pcBuffer, 1, uxCount, pxClient->pxReadHandle );
\r
1736 if( uxCount != uxItemsRead )
\r
1738 FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned )uxCount ) );
\r
1739 xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
\r
1740 pxClient->uxBytesLeft = 0u;
\r
1743 pxClient->uxBytesLeft -= uxCount;
\r
1745 if( pxClient->uxBytesLeft == 0u )
\r
1747 BaseType_t xTrueValue = 1;
\r
1749 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
\r
1751 if( pcBuffer != pcFILE_BUFFER )
\r
1755 xRc = FreeRTOS_send( pxClient->xTransferSocket, pcBuffer, uxCount, 0 );
\r
1757 #endif /* ipconfigFTP_TX_ZERO_COPY */
\r
1764 pxClient->ulRecvBytes += xRc;
\r
1765 if( pxClient->uxBytesLeft == 0u )
\r
1769 } while( uxCount > 0u );
\r
1773 FreeRTOS_printf( ( "prvRetrieveFileWork: already disconnected\n" ) );
\r
1775 else if( pxClient->uxBytesLeft <= 0u )
\r
1779 for( x = 0; x < 5; x++ )
\r
1781 xRc = FreeRTOS_recv( pxClient->xTransferSocket, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), 0 );
\r
1787 // FreeRTOS_printf( ( "prvRetrieveFileWork: %s all sent: xRc %ld\n", pxClient->pcFileName, xRc ) );
\r
1791 FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
1792 xSetEvent = pdTRUE;
\r
1794 if( xSetEvent == pdFALSE )
\r
1796 FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
1800 /*-----------------------------------------------------------*/
\r
1803 ### ##### #### #####
\r
1811 ####### ##### #### ####
\r
1813 /* Prepare sending a directory LIST */
\r
1814 static BaseType_t prvListSendPrep( FTPClient_t *pxClient )
\r
1816 BaseType_t xFindResult;
\r
1819 if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
\r
1821 /* True if PASV is used */
\r
1822 snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
\r
1823 "150 Accepted data connection from %%xip:%%u\r\n" );
\r
1827 BaseType_t xLength;
\r
1829 /* Here the FTP server is supposed to connect() */
\r
1830 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
1831 "150 Opening ASCII mode data connection to for /bin/ls \r\n" );
\r
1833 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
1834 /* Clear the current connection acknowledge message */
\r
1835 pxClient->pcConnectionAck[ 0 ] = '\0';
\r
1836 prvTransferStart( pxClient );
\r
1839 pxClient->xDirCount = 0;
\r
1840 xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pxClient->pcCurrentDir );
\r
1842 xFindResult = ff_findfirst( pcNEW_DIR, &pxClient->xFindData );
\r
1844 pxClient->bits1.bDirHasEntry = ( xFindResult >= 0 );
\r
1846 iErrorNo = stdioGET_ERRNO();
\r
1847 if( ( xFindResult < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE ) )
\r
1849 FreeRTOS_printf( ("prvListSendPrep: Empty directory? (%s)\n", pxClient->pcCurrentDir ) );
\r
1850 prvSendReply( pxClient->xTransferSocket, "total 0\r\n", 0 );
\r
1851 pxClient->xDirCount++;
\r
1853 else if( xFindResult < 0 )
\r
1855 FreeRTOS_printf( ( "prvListSendPrep: rc = %ld iErrorNo = %d\n", xFindResult, iErrorNo ) );
\r
1856 prvSendReply( pxClient->xSocket, REPL_451, 0 );
\r
1858 pxClient->pcClientAck[ 0 ] = '\0';
\r
1860 return pxClient->xDirCount;
\r
1862 /*-----------------------------------------------------------*/
\r
1864 #define MAX_DIR_LIST_ENTRY_SIZE 256
\r
1866 static BaseType_t prvListSendWork( FTPClient_t *pxClient )
\r
1868 BaseType_t xTxSpace;
\r
1870 while( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
\r
1872 char *pcWritePtr = pcCOMMAND_BUFFER;
\r
1873 BaseType_t xWriteLength;
\r
1875 xTxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
\r
1877 if( xTxSpace > ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
1879 xTxSpace = sizeof( pcCOMMAND_BUFFER );
\r
1882 while( ( xTxSpace >= MAX_DIR_LIST_ENTRY_SIZE ) && ( pxClient->bits1.bDirHasEntry != pdFALSE_UNSIGNED ) )
\r
1884 BaseType_t xLength, xEndOfDir;
\r
1888 xLength = prvGetFileInfoStat( &( pxClient->xFindData.xDirectoryEntry ), pcWritePtr, xTxSpace );
\r
1890 pxClient->xDirCount++;
\r
1891 pcWritePtr += xLength;
\r
1892 xTxSpace -= xLength;
\r
1894 iRc = ff_findnext( &pxClient->xFindData );
\r
1895 iErrorNo = stdioGET_ERRNO();
\r
1897 xEndOfDir = ( iRc < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE );
\r
1899 pxClient->bits1.bDirHasEntry = ( xEndOfDir == pdFALSE ) && ( iRc >= 0 );
\r
1901 if( ( iRc < 0 ) && ( xEndOfDir == pdFALSE ) )
\r
1903 FreeRTOS_printf( ("prvListSendWork: %s (rc %08x)\n",
\r
1904 ( const char * ) strerror( iErrorNo ),
\r
1905 ( unsigned )iRc ) );
\r
1908 xWriteLength = ( BaseType_t ) ( pcWritePtr - pcCOMMAND_BUFFER );
\r
1910 if( xWriteLength == 0 )
\r
1915 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
\r
1917 uint32_t ulTotalCount;
\r
1918 uint32_t ulFreeCount;
\r
1919 uint32_t ulPercentage;
\r
1922 ulFreeCount = ff_diskfree( pxClient->pcCurrentDir, &ulTotalCount );
\r
1923 ulPercentage = ( uint32_t ) ( ( 100ULL * ulFreeCount + ulTotalCount / 2 ) / ulTotalCount );
\r
1925 /* Prepare the ACK which will be sent when all data has been sent. */
\r
1926 snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
\r
1927 "226-Options: -l\r\n"
\r
1928 "226-%ld matches total\r\n"
\r
1929 "226 Total %lu KB (%lu %% free)\r\n",
\r
1930 pxClient->xDirCount, ulTotalCount /1024, ulPercentage );
\r
1933 if( xWriteLength )
\r
1935 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
\r
1937 BaseType_t xTrueValue = 1;
\r
1939 FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
\r
1942 prvSendReply( pxClient->xTransferSocket, pcCOMMAND_BUFFER, xWriteLength );
\r
1945 if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
\r
1947 prvSendReply( pxClient->xSocket, pxClient->pcClientAck, 0 );
\r
1951 } /* while( pxClient->bits1.bClientConnected ) */
\r
1955 /*-----------------------------------------------------------*/
\r
1957 static const char *pcMonthAbbrev( BaseType_t xMonth )
\r
1959 static const char pcMonthList[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
\r
1961 if( xMonth < 1 || xMonth > 12 )
\r
1964 return pcMonthList + 3 * ( xMonth - 1 );
\r
1966 /*-----------------------------------------------------------*/
\r
1968 static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength )
\r
1971 char mode[ 11 ] = "----------";
\r
1972 BaseType_t st_nlink = 1;
\r
1973 const char user[ 9 ] = "freertos";
\r
1974 const char group[ 8 ] = "plusfat";
\r
1977 * Creates a unix-style listing, understood by most FTP clients:
\r
1979 * -rw-rw-r-- 1 freertos FreeRTOS+FAT 10564588 Sep 01 00:17 03. Metaharmoniks - Star (Instrumental).mp3
\r
1980 * -rw-rw-r-- 1 freertos FreeRTOS+FAT 19087839 Sep 01 00:17 04. Simon Le Grec - Dimitri (Wherever U Are) (Cosmos Mix).mp3
\r
1981 * -rw-rw-r-- 1 freertos FreeRTOS+FAT 11100621 Sep 01 00:16 05. D-Chill - Mistake (feat. Katy Blue).mp3
\r
1984 #if ( ffconfigTIME_SUPPORT == 1 )
\r
1985 const FF_SystemTime_t *pxCreateTime = &( pxEntry->xCreateTime );
\r
1987 #warning Do not use this.
\r
1988 FF_SystemTime_t xCreateTime;
\r
1989 const FF_SystemTime_t *pxCreateTime = &xCreateTime;
\r
1991 size_t ulSize = ( size_t )pxEntry->ulFileSize;
\r
1992 const char *pcFileName = pxEntry->pcFileName;
\r
1994 mode[ 0 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) != 0 ) ? 'd' : '-';
\r
1995 #if( ffconfigDEV_SUPPORT != 0 )
\r
1997 if( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) == 0 )
\r
1999 switch( pxEntry->ucIsDeviceDir )
\r
2001 case FF_DEV_CHAR_DEV:
\r
2004 case FF_DEV_BLOCK_DEV:
\r
2010 #endif /* ffconfigDEV_SUPPORT != 0 */
\r
2012 mode[ 1 ] = 'r'; /* Owner. */
\r
2013 mode[ 2 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';
\r
2014 mode[ 3 ] = '-'; /* x for executable. */
\r
2016 mode[ 4 ] = 'r'; /* group. */
\r
2017 mode[ 5 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';
\r
2018 mode[ 6 ] = '-'; /* x for executable. */
\r
2020 mode[ 7 ] = 'r'; /* world. */
\r
2022 mode[ 9 ] = '-'; /* x for executable. */
\r
2024 if( pxCreateTime->Month && pxCreateTime->Day )
\r
2026 snprintf( date, sizeof( date ), "%-3.3s %02d %02d:%02d",
\r
2027 pcMonthAbbrev( pxCreateTime->Month ),
\r
2028 pxCreateTime->Day,
\r
2029 pxCreateTime->Hour,
\r
2030 pxCreateTime->Minute );
\r
2034 snprintf (date, sizeof( date ), "Jan 01 1970");
\r
2036 return snprintf( pcLine, xMaxLength, "%s %3ld %-4s %-4s %8d %12s %s\r\n",
\r
2037 mode, st_nlink, user, group, ( int ) ulSize, date, pcFileName );
\r
2039 /*-----------------------------------------------------------*/
\r
2052 static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory )
\r
2054 BaseType_t xResult;
\r
2055 BaseType_t xIsRootDir, xLength, xValid;
\r
2056 BaseType_t xIsDotDir = 0;
\r
2058 if( pcDirectory[ 0 ] == '.' )
\r
2060 if( ( pcDirectory[ 1 ] == '.' ) &&
\r
2061 ( pcDirectory[ 2 ] == '\0' ) )
\r
2065 else if( pcDirectory[ 1 ] == '\0' )
\r
2071 if( xIsDotDir != 0 )
\r
2073 strcpy( pcFILE_BUFFER, pxClient->pcCurrentDir );
\r
2075 if( pcDirectory[ 1 ] == '.' )
\r
2077 char *p = strrchr( pcFILE_BUFFER, '/' );
\r
2080 if( p == pcFILE_BUFFER )
\r
2093 if(pcDirectory[ 0 ] != '/' )
\r
2095 BaseType_t xCurLength;
\r
2097 xCurLength = strlen( pxClient->pcCurrentDir );
\r
2098 snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s%s%s",
\r
2099 pxClient->pcCurrentDir,
\r
2100 pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",
\r
2105 snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s", pcDirectory );
\r
2109 xIsRootDir = ( pcFILE_BUFFER[ 0 ] == '/' ) && ( pcFILE_BUFFER[ 1 ] == '\0' );
\r
2110 xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFILE_BUFFER );
\r
2112 if( ( ( xIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) ) && ( ff_finddir( pcNEW_DIR ) == pdFALSE ) )
\r
2121 if( xValid == pdFALSE )
\r
2123 /* Get the directory cluster, if it exists. */
\r
2124 FreeRTOS_printf( ("FTP: chdir \"%s\": No such dir\n", pcNEW_DIR ) );
\r
2125 //#define REPL_550 "550 Requested action not taken.\r\n"
\r
2126 //550 /home/hein/arch/h8300: No such file or directory
\r
2127 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2128 "550 %s: No such file or directory\r\n",
\r
2130 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
2131 xResult = pdFALSE;
\r
2135 memcpy( pxClient->pcCurrentDir, pcNEW_DIR, sizeof( pxClient->pcCurrentDir ) );
\r
2137 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "250 Changed to %s\r\n", pcNEW_DIR );
\r
2138 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
2144 /*-----------------------------------------------------------*/
\r
2147 ###### ## # ####### ######
\r
2151 ###### # ## # ##### ######
\r
2152 # ## # ## # # # # ##
\r
2155 ### ## # ## #### ### ##
\r
2157 static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName )
\r
2159 const char *myReply;
\r
2162 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
\r
2166 fh = ff_fopen( pxClient->pcFileName, "rb" );
\r
2171 /* REPL_350; "350 Requested file action pending further information." */
\r
2172 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2173 "350 Rename '%s' ...\r\n", pxClient->pcFileName );
\r
2174 myReply = pcCOMMAND_BUFFER;
\r
2175 pxClient->bits.bInRename = pdTRUE_UNSIGNED;
\r
2177 else if( stdioGET_ERRNO() == pdFREERTOS_ERRNO_EISDIR )
\r
2179 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2180 "350 Rename directory '%s' ...\r\n", pxClient->pcFileName );
\r
2181 myReply = pcCOMMAND_BUFFER;
\r
2182 pxClient->bits.bInRename = pdTRUE_UNSIGNED;
\r
2186 FreeRTOS_printf( ("ftp::renameFrom[%s]\n%s\n", pxClient->pcFileName, strerror( stdioGET_ERRNO() ) ) );
\r
2187 myReply = REPL_451; /* "451 Requested action aborted. Local error in processing." */
\r
2191 prvSendReply( pxClient->xSocket, myReply, 0 );
\r
2196 /*-----------------------------------------------------------*/
\r
2199 ###### ## # ##### ###
\r
2200 # # ## # # # # ## ##
\r
2203 ###### # ## # # # #
\r
2207 ### ## # ## #### ###
\r
2209 static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName )
\r
2211 const char *myReply = NULL;
\r
2214 xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFileName );
\r
2216 /* FreeRTOS+FAT rename has an extra parameter: "remove target if already
\r
2218 iResult = ff_rename( pxClient->pcFileName, pcNEW_DIR, pdFALSE );
\r
2222 iResult = stdioGET_ERRNO();
\r
2232 FreeRTOS_printf( ( "ftp::renameTo[%s,%s]: Ok\n", pxClient->pcFileName, pcNEW_DIR ) );
\r
2233 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2234 "250 Rename successful to '%s'\r\n", pcNEW_DIR );
\r
2235 myReply = pcCOMMAND_BUFFER;
\r
2237 case pdFREERTOS_ERRNO_EEXIST:
\r
2238 /* the destination file already exists.
\r
2239 "450 Requested file action not taken.\r\n"*/
\r
2240 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2241 "450 Already exists '%s'\r\n", pcNEW_DIR );
\r
2242 myReply = pcCOMMAND_BUFFER;
\r
2244 case pdFREERTOS_ERRNO_EIO: /* FF_ERR_FILE_COULD_NOT_CREATE_DIRENT */
\r
2245 /* if dirent creation failed (fatal error!).
\r
2246 "553 Requested action not taken.\r\n" */
\r
2247 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: Error creating DirEnt\n",
\r
2248 pxClient->pcFileName, pcNEW_DIR ) );
\r
2249 myReply = REPL_553;
\r
2251 case pdFREERTOS_ERRNO_ENXIO:
\r
2252 case pdFREERTOS_ERRNO_ENOENT:
\r
2253 /* if the source file was not found.
\r
2254 "450 Requested file action not taken.\r\n" */
\r
2255 snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2256 "450 No such file '%s'\r\n", pxClient->pcFileName );
\r
2257 myReply = pcCOMMAND_BUFFER;
\r
2260 FreeRTOS_printf( ("ftp::renameTo[%s,%s]: %s\n", pxClient->pcFileName, pcNEW_DIR,
\r
2261 (const char*)strerror( stdioGET_ERRNO() ) ) );
\r
2262 myReply = REPL_451; /* "451 Requested action aborted. Local error in processing." */
\r
2265 prvSendReply( pxClient->xSocket, myReply, 0 );
\r
2269 /*-----------------------------------------------------------*/
\r
2280 #### ##### ## ####
\r
2282 static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand )
\r
2284 ( void ) pxClient;
\r
2285 ( void ) pcRestCommand;
\r
2289 /*-----------------------------------------------------------*/
\r
2295 # # #### # #### ###### ####
\r
2296 # # # # # # # # # #
\r
2297 # # ###### # ###### # ######
\r
2299 # # # ## # # ## # ## # ##
\r
2300 ##### #### ##### #### ## ####
\r
2302 static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName )
\r
2304 BaseType_t xResult, xLength;
\r
2308 /* DELE: Delete a file. */
\r
2309 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
\r
2311 iRc = ff_remove( pxClient->pcFileName );
\r
2315 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2316 "250 File \"%s\" removed\r\n", pxClient->pcFileName );
\r
2321 const char *errMsg = "other error";
\r
2323 iErrorNo = stdioGET_ERRNO();
\r
2324 switch( iErrorNo )
\r
2325 { /*_RB_ What do these negative numbers relate to? */
\r
2326 case pdFREERTOS_ERRNO_ENOENT: errMsg = "No such file"; break; /* -31 File was not found. */
\r
2327 case pdFREERTOS_ERRNO_EALREADY: errMsg = "File still open"; break; /* -30 File is in use. */
\r
2328 case pdFREERTOS_ERRNO_EISDIR: errMsg = "Is a dir"; break; /* -32 Tried to FF_Open() a Directory. */
\r
2329 case pdFREERTOS_ERRNO_EROFS: errMsg = "Read-only"; break; /* -33 Tried to FF_Open() a file marked read only. */
\r
2330 case pdFREERTOS_ERRNO_ENOTDIR: errMsg = "Invalid path"; break; /* -34 The path of the file was not found. */
\r
2332 FreeRTOS_printf( ( "ftp::delFile: '%s' because %s\n",
\r
2333 pxClient->pcFileName, strerror( iErrorNo ) ) );
\r
2335 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2336 "521-\"%s\" %s;\r\n"
\r
2337 "521 taking no action\r\n",
\r
2338 pxClient->pcFileName, errMsg );
\r
2340 xResult = pdFALSE;
\r
2343 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
2347 /*-----------------------------------------------------------*/
\r
2353 # ### ###### #### # # #### ###### ####
\r
2354 ## # # # # # # # # # # #
\r
2355 ## # # ###### # # ##### # ######
\r
2356 # # # # # # # # # # #
\r
2357 # # # # # ## # # # # # ## # ##
\r
2358 #### ##### ###### #### ##### ### ## ## ####
\r
2360 static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate )
\r
2362 BaseType_t xResult = pdFALSE;
\r
2365 /* SIZE: get the size of a file (xSendDate = 0)
\r
2366 MDTM: get data and time properties (xSendDate = 1) */
\r
2367 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
\r
2369 pcPtr = strrchr( pxClient->pcFileName, '/' );
\r
2371 if( ( pcPtr != NULL ) && ( pcPtr[ 1 ] != '\0' ) )
\r
2373 FF_Stat_t xStatBuf;
\r
2374 int32_t iRc = ff_stat( pxClient->pcFileName, &xStatBuf );
\r
2376 FreeRTOS_printf( ("In %s: %s\n", pxClient->pcFileName,
\r
2377 ( const char* )strerror( stdioGET_ERRNO() ) ) );
\r
2381 BaseType_t xLength;
\r
2382 /* "YYYYMMDDhhmmss" */
\r
2383 if( xSendDate != pdFALSE )
\r
2385 #if( ffconfigTIME_SUPPORT != 0 )
\r
2387 FF_TimeStruct_t tmStruct;
\r
2388 time_t secs = xStatBuf.st_mtime;
\r
2389 FreeRTOS_gmtime_r( &secs, &tmStruct );
\r
2391 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %04u%02u%02u%02u%02u%02u\r\n",
\r
2392 tmStruct.tm_year + 1900,
\r
2393 tmStruct.tm_mon+1,
\r
2397 tmStruct.tm_sec );
\r
2401 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 19700101000000\r\n",
\r
2407 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", xStatBuf.st_size );
\r
2409 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
2414 FreeRTOS_printf( ("ftp::sizeDateFile: No such file %s\n", pxClient->pcFileName ) );
\r
2417 FreeRTOS_printf( ("ftp::sizeDateFile: Invalid file name: %s ?\n", pxClient->pcFileName ) );
\r
2419 if( xResult == pdFALSE )
\r
2421 prvSendReply( pxClient->xSocket, REPL_450, 0 ); /* "Requested file action not taken". */
\r
2426 /*-----------------------------------------------------------*/
\r
2429 ## ## ## ## ##### ###### ## ## #####
\r
2430 ### ### # # # # # # ### ### # #
\r
2431 # ### # # # # # # # # ### # # #
\r
2432 # # # # # # # # # # # # # #
\r
2433 # # # #### # # ###### # # # # #
\r
2434 # # # # # # # ## # # # #
\r
2435 # # # # # # # # # # # #
\r
2436 # # # # # # # # # # # #
\r
2437 # # ### ## ##### ### ## # # #####
\r
2439 static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove )
\r
2441 BaseType_t xResult;
\r
2442 BaseType_t xLength;
\r
2446 /* MKD: Make / create a directory (xDoRemove = 0)
\r
2447 RMD: Remove a directory (xDoRemove = 1) */
\r
2448 xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcDirectory );
\r
2452 iRc = ff_rmdir( pxClient->pcFileName );
\r
2456 #if( ffconfigMKDIR_RECURSIVE != 0 )
\r
2458 iRc = ff_mkdir( pxClient->pcFileName, pdFALSE );
\r
2462 iRc = ff_mkdir( pxClient->pcFileName );
\r
2464 #endif /* ffconfigMKDIR_RECURSIVE */
\r
2470 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "257 \"%s\" directory %s\r\n",
\r
2471 pxClient->pcFileName, xDoRemove ? "removed" : "created" );
\r
2475 const char *errMsg = "other error";
\r
2476 BaseType_t xFTPCode = 521;
\r
2478 xResult = pdFALSE;
\r
2479 iErrorNo = stdioGET_ERRNO();
\r
2480 switch( iErrorNo )
\r
2482 case pdFREERTOS_ERRNO_EEXIST: errMsg = "Directory already exists"; break;
\r
2483 case pdFREERTOS_ERRNO_ENOTDIR: errMsg = "Invalid path"; break; /* -34 The path of the file was not found. *//*_RB_ As before, what do these negative numbers relate to? */
\r
2484 case pdFREERTOS_ERRNO_ENOTEMPTY:errMsg = "Dir not empty"; break;
\r
2485 case pdFREERTOS_ERRNO_EROFS: errMsg = "Read-only"; break; /* -33 Tried to FF_Open() a file marked read only. */
\r
2486 default: errMsg = strerror( iErrorNo ); break;
\r
2488 if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )
\r
2492 xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
\r
2493 "%ld-\"%s\" %s;\r\n"
\r
2494 "%ld taking no action\r\n",
\r
2495 xFTPCode, pxClient->pcFileName, errMsg, xFTPCode );
\r
2496 FreeRTOS_printf( ( "%sdir '%s': %s\n", xDoRemove ? "rm" : "mk", pxClient->pcFileName, errMsg ) );
\r
2498 prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
\r
2502 /*-----------------------------------------------------------*/
\r
2504 static portINLINE BaseType_t IsDigit( char cChar )
\r
2506 BaseType_t xResult;
\r
2508 if( cChar >= '0' && cChar <= '9' )
\r
2514 xResult = pdFALSE;
\r
2519 static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength )
\r
2521 BaseType_t xResult;
\r
2523 if( xLength == 0 )
\r
2525 xLength = strlen( pcBuffer );
\r
2527 xResult = FreeRTOS_send( xSocket, ( const void * )pcBuffer, ( size_t ) xLength, 0 );
\r
2528 if( IsDigit( ( int ) pcBuffer[ 0 ] ) &&
\r
2529 IsDigit( ( int ) pcBuffer[ 1 ] ) &&
\r
2530 IsDigit( ( int ) pcBuffer[ 2 ] ) &&
\r
2531 IsDigit( ( int ) pcBuffer[ 3 ] ) )
\r
2533 const char *last = pcBuffer + strlen( pcBuffer );
\r
2535 while( ( last > pcBuffer ) && ( ( last[ -1 ] == ftpASCII_CR ) || ( last[ -1 ] == ftpASCII_LF ) ) )
\r
2539 iLength = ( int )( last - pcBuffer );
\r
2540 FF_PRINTF( " %-*.*s", iLength, iLength, pcBuffer );
\r
2544 /*-----------------------------------------------------------*/
\r
2546 #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )
\r
2549 * The following function is called for every file received:
\r
2550 * void vApplicationFTPReceivedHook( pcFileName, ulSize, pxFTPClient );
\r
2551 * This callback function may do a callback to vFTPReplyMessage() to send messages
\r
2552 * to the FTP client like:
\r
2553 * 200-Please wait: Received new firmware
\r
2554 * 200-Please wait: Please wait a few seconds for reboot
\r
2556 void vFTPReplyMessage( struct xFTP_CLIENT *pxFTPClient, const char *pcMessage )
\r
2558 if( ( pxFTPClient != NULL ) && ( pxFTPClient->xSocket != NULL ) )
\r
2560 prvSendReply( pxFTPClient->xSocket, pcMessage, 0 );
\r
2563 /*-----------------------------------------------------------*/
\r
2565 #endif /* ipconfigFTP_HAS_RECEIVED_HOOK != 0 */
\r
2568 * Some explanation:
\r
2569 * The FTP client may send: "DELE readme.txt"
\r
2570 * Here the complete path is constructed consisting of 3 parts:
\r
2572 * pxClient->pcRootDir + pxClient->pcCurrentDir + pcFileName
\r
2574 * 'pcCurrentDir' will not be applied for an absolute path like in "DELE /.htaccess"
\r
2576 BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )
\r
2578 BaseType_t xLength = strlen( pxClient->pcRootDir );
\r
2580 if( pcFileName[ 0 ] != '/' )
\r
2582 char *pcNewDirBuffer = pcNEW_DIR;
\r
2583 BaseType_t xCurLength;
\r
2585 xCurLength = strlen( pxClient->pcCurrentDir );
\r
2586 if( pcBuffer == pcNEW_DIR )
\r
2588 /* In one call, the result already goes into pcNEW_DIR.
\r
2589 Use pcFILE_BUFFER in that case */
\r
2590 pcNewDirBuffer = pcFILE_BUFFER;
\r
2592 snprintf( pcNewDirBuffer, sizeof( pcNEW_DIR ), "%s%s%s",
\r
2593 pxClient->pcCurrentDir,
\r
2594 pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",
\r
2596 pcFileName = pcNewDirBuffer;
\r
2598 if( strncasecmp( pxClient->pcRootDir, pcFileName, xLength ) == 0 )
\r
2600 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );
\r
2604 xLength = snprintf( pcBuffer, xBufferLength, "%s/%s",
\r
2605 pxClient->pcRootDir,
\r
2606 pcFileName[ 0 ] == '/' ? ( pcFileName + 1 ) : pcFileName );
\r
2609 #if( ipconfigFTP_FS_USES_BACKSLAH == 1 )
\r
2610 for( pcPtr = pcBuffer; *pcPtr; pcPtr++ )
\r
2612 if( pcPtr[ 0 ] == '/' )
\r
2614 pcPtr[ 0 ] = '\\';
\r
2621 /*-----------------------------------------------------------*/
\r
2623 BaseType_t xMakeRelative( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )
\r
2625 BaseType_t xLength = strlen( pxClient->pcRootDir );
\r
2627 if( strncasecmp ( pxClient->pcRootDir, pcFileName, xLength ) == 0 )
\r
2629 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName + xLength );
\r
2633 xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );
\r
2638 /*-----------------------------------------------------------*/
\r
2640 #endif /* ipconfigUSE_FTP */
\r