summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Demo
diff options
context:
space:
mode:
authorrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>2018-08-22 21:29:21 +0000
committerrtel <rtel@1d2547de-c912-0410-9cb9-b8ca96c0e9e2>2018-08-22 21:29:21 +0000
commita5ae552906d6fd94fe1ce74f09ef1f9511df4357 (patch)
treea1e51ea5abbbc0e9c83e3cf654c9d2c944f09de4 /FreeRTOS-Plus/Demo
parentb4a4805335c2ee57e4b138639925030e5e6bfa1a (diff)
downloadfreertos-a5ae552906d6fd94fe1ce74f09ef1f9511df4357.tar.gz
Fix some build issues in older kernel demo projects.
Update to V2.0.7 of the TCP/IP stack: + Multiple security improvements and fixes in packet parsing routines, DNS caching, and TCP sequence number and ID generation. + Disable NBNS and LLMNR by default. + Add TCP hang protection by default. We thank Ori Karliner of Zimperium zLabs Team for reporting these issues. git-svn-id: http://svn.code.sf.net/p/freertos/code/trunk@2563 1d2547de-c912-0410-9cb9-b8ca96c0e9e2
Diffstat (limited to 'FreeRTOS-Plus/Demo')
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/Common/FreeRTOS_TCP_server.c353
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_commands.c74
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c2637
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_commands.c71
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_server.c428
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/NTP/NTPDemo.c440
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_FTP_commands.h133
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_HTTP_commands.h67
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_TCP_server.h125
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_server_private.h185
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPClient.h71
-rw-r--r--FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPDemo.h11
12 files changed, 4595 insertions, 0 deletions
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/Common/FreeRTOS_TCP_server.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/Common/FreeRTOS_TCP_server.c
new file mode 100644
index 000000000..3b9f8fb2f
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/Common/FreeRTOS_TCP_server.c
@@ -0,0 +1,353 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+
+/* Standard includes. */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+#include "task.h"
+
+/* FreeRTOS+TCP includes. */
+#include "FreeRTOS_IP.h"
+#include "FreeRTOS_Sockets.h"
+#include "FreeRTOS_TCP_server.h"
+#include "FreeRTOS_server_private.h"
+
+/* Remove the entire file if TCP is not being used. */
+#if( ipconfigUSE_TCP == 1 ) && ( ( ipconfigUSE_HTTP == 1 ) || ( ipconfigUSE_FTP == 1 ) )
+
+#if !defined( ARRAY_SIZE )
+ #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
+#endif
+
+
+static void prvReceiveNewClient( TCPServer_t *pxServer, BaseType_t xIndex, Socket_t xNexSocket );
+static char *strnew( const char *pcString );
+/* Remove slashes at the end of a path. */
+static void prvRemoveSlash( char *pcDir );
+
+TCPServer_t *FreeRTOS_CreateTCPServer( const struct xSERVER_CONFIG *pxConfigs, BaseType_t xCount )
+{
+TCPServer_t *pxServer;
+SocketSet_t xSocketSet;
+
+ /* Create a new server.
+ xPort / xPortAlt : Make the service available on 1 or 2 public port numbers. */
+ xSocketSet = FreeRTOS_CreateSocketSet();
+
+ if( xSocketSet != NULL )
+ {
+ BaseType_t xSize;
+
+ xSize = sizeof( *pxServer ) - sizeof( pxServer->xServers ) + xCount * sizeof( pxServer->xServers[ 0 ] );
+
+ pxServer = ( TCPServer_t * ) pvPortMallocLarge( xSize );
+ if( pxServer != NULL )
+ {
+ struct freertos_sockaddr xAddress;
+ BaseType_t xNoTimeout = 0;
+ BaseType_t xIndex;
+
+ memset( pxServer, '\0', xSize );
+ pxServer->xServerCount = xCount;
+ pxServer->xSocketSet = xSocketSet;
+
+ for( xIndex = 0; xIndex < xCount; xIndex++ )
+ {
+ BaseType_t xPortNumber = pxConfigs[ xIndex ].xPortNumber;
+
+ if( xPortNumber > 0 )
+ {
+ Socket_t xSocket;
+
+ xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
+ FreeRTOS_printf( ( "TCP socket on port %d\n", ( int )xPortNumber ) );
+
+ if( xSocket != FREERTOS_NO_SOCKET )
+ {
+ xAddress.sin_addr = FreeRTOS_GetIPAddress(); // Single NIC, currently not used
+ xAddress.sin_port = FreeRTOS_htons( xPortNumber );
+
+ FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
+ FreeRTOS_listen( xSocket, pxConfigs[ xIndex ].xBackLog );
+
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xNoTimeout, sizeof( BaseType_t ) );
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xNoTimeout, sizeof( BaseType_t ) );
+
+ #if( ipconfigHTTP_RX_BUFSIZE > 0 )
+ {
+ if( pxConfigs[ xIndex ].eType == eSERVER_HTTP )
+ {
+ WinProperties_t xWinProps;
+
+ memset( &xWinProps, '\0', sizeof( xWinProps ) );
+ /* The parent socket itself won't get connected. The properties below
+ will be inherited by each new child socket. */
+ xWinProps.lTxBufSize = ipconfigHTTP_TX_BUFSIZE;
+ xWinProps.lTxWinSize = ipconfigHTTP_TX_WINSIZE;
+ xWinProps.lRxBufSize = ipconfigHTTP_RX_BUFSIZE;
+ xWinProps.lRxWinSize = ipconfigHTTP_RX_WINSIZE;
+
+ /* Set the window and buffer sizes. */
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) );
+ }
+ }
+ #endif
+
+ FreeRTOS_FD_SET( xSocket, xSocketSet, eSELECT_READ|eSELECT_EXCEPT );
+ pxServer->xServers[ xIndex ].xSocket = xSocket;
+ pxServer->xServers[ xIndex ].eType = pxConfigs[ xIndex ].eType;
+ pxServer->xServers[ xIndex ].pcRootDir = strnew( pxConfigs[ xIndex ].pcRootDir );
+ prvRemoveSlash( ( char * ) pxServer->xServers[ xIndex ].pcRootDir );
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Could not allocate the server, delete the socket set */
+ FreeRTOS_DeleteSocketSet( xSocketSet );
+ }
+ }
+ else
+ {
+ /* Could not create a socket set, return NULL */
+ pxServer = NULL;
+ }
+
+ return pxServer;
+}
+/*-----------------------------------------------------------*/
+
+static void prvReceiveNewClient( TCPServer_t *pxServer, BaseType_t xIndex, Socket_t xNexSocket )
+{
+TCPClient_t *pxClient = NULL;
+BaseType_t xSize = 0;
+FTCPWorkFunction fWorkFunc = NULL;
+FTCPDeleteFunction fDeleteFunc = NULL;
+const char *pcType = "Unknown";
+
+ /*_RB_ Can the work and delete functions be part of the xSERVER_CONFIG structure
+ becomes generic, with no pre-processing required? */
+ #if( ipconfigUSE_HTTP != 0 )
+ {
+ if( pxServer->xServers[ xIndex ].eType == eSERVER_HTTP )
+ {
+ xSize = sizeof( HTTPClient_t );
+ fWorkFunc = xHTTPClientWork;
+ fDeleteFunc = vHTTPClientDelete;
+ pcType = "HTTP";
+ }
+ }
+ #endif /* ipconfigUSE_HTTP != 0 */
+
+ #if( ipconfigUSE_FTP != 0 )
+ {
+ if( pxServer->xServers[ xIndex ].eType == eSERVER_FTP )
+ {
+ xSize = sizeof( FTPClient_t );
+ fWorkFunc = xFTPClientWork;
+ fDeleteFunc = vFTPClientDelete;
+ pcType = "FTP";
+ }
+ }
+ #endif /* ipconfigUSE_FTP != 0 */
+
+ /* Malloc enough space for a new HTTP-client */
+ if( xSize )
+ {
+ pxClient = ( TCPClient_t* ) pvPortMallocLarge( xSize );
+ }
+
+ if( pxClient != NULL )
+ {
+ memset( pxClient, '\0', xSize );
+
+ /* Put the new client in front of the list. */
+ pxClient->eType = pxServer->xServers[ xIndex ].eType;
+ pxClient->pcRootDir = pxServer->xServers[ xIndex ].pcRootDir;
+ pxClient->pxParent = pxServer;
+ pxClient->xSocket = xNexSocket;
+ pxClient->pxNextClient = pxServer->pxClients;
+ pxClient->fWorkFunction = fWorkFunc;
+ pxClient->fDeleteFunction = fDeleteFunc;
+ pxServer->pxClients = pxClient;
+
+ FreeRTOS_FD_SET( xNexSocket, pxServer->xSocketSet, eSELECT_READ|eSELECT_EXCEPT );
+ }
+ else
+ {
+ pcType = "closed";
+ FreeRTOS_closesocket( xNexSocket );
+ }
+ {
+ struct freertos_sockaddr xRemoteAddress;
+ FreeRTOS_GetRemoteAddress( pxClient->xSocket, &xRemoteAddress );
+ FreeRTOS_printf( ( "TPC-server: new %s client %xip\n", pcType, (unsigned)FreeRTOS_ntohl( xRemoteAddress.sin_addr ) ) );
+ }
+
+ /* Remove compiler warnings in case FreeRTOS_printf() is not used. */
+ ( void ) pcType;
+}
+/*-----------------------------------------------------------*/
+
+void FreeRTOS_TCPServerWork( TCPServer_t *pxServer, TickType_t xBlockingTime )
+{
+TCPClient_t **ppxClient;
+BaseType_t xIndex;
+BaseType_t xRc;
+
+ /* Let the server do one working cycle */
+ xRc = FreeRTOS_select( pxServer->xSocketSet, xBlockingTime );
+
+ if( xRc != 0 )
+ {
+ for( xIndex = 0; xIndex < pxServer->xServerCount; xIndex++ )
+ {
+ struct freertos_sockaddr xAddress;
+ Socket_t xNexSocket;
+ socklen_t xSocketLength;
+
+ if( pxServer->xServers[ xIndex ].xSocket == FREERTOS_NO_SOCKET )
+ {
+ continue;
+ }
+
+ xSocketLength = sizeof( xAddress );
+ xNexSocket = FreeRTOS_accept( pxServer->xServers[ xIndex ].xSocket, &xAddress, &xSocketLength);
+
+ if( ( xNexSocket != FREERTOS_NO_SOCKET ) && ( xNexSocket != FREERTOS_INVALID_SOCKET ) )
+ {
+ prvReceiveNewClient( pxServer, xIndex, xNexSocket );
+ }
+ }
+ }
+
+ ppxClient = &pxServer->pxClients;
+
+ while( ( * ppxClient ) != NULL )
+ {
+ TCPClient_t *pxThis = *ppxClient;
+
+ /* Almost C++ */
+ xRc = pxThis->fWorkFunction( pxThis );
+
+ if (xRc < 0 )
+ {
+ *ppxClient = pxThis->pxNextClient;
+ /* Close handles, resources */
+ pxThis->fDeleteFunction( pxThis );
+ /* Free the space */
+ vPortFreeLarge( pxThis );
+ }
+ else
+ {
+ ppxClient = &( pxThis->pxNextClient );
+ }
+ }
+}
+/*-----------------------------------------------------------*/
+
+static char *strnew( const char *pcString )
+{
+BaseType_t xLength;
+char *pxBuffer;
+
+ xLength = strlen( pcString ) + 1;
+ pxBuffer = ( char * ) pvPortMalloc( xLength );
+ if( pxBuffer != NULL )
+ {
+ memcpy( pxBuffer, pcString, xLength );
+ }
+
+ return pxBuffer;
+}
+/*-----------------------------------------------------------*/
+
+static void prvRemoveSlash( char *pcDir )
+{
+BaseType_t xLength = strlen( pcDir );
+
+ while( ( xLength > 0 ) && ( pcDir[ xLength - 1 ] == '/' ) )
+ {
+ pcDir[ --xLength ] = '\0';
+ }
+}
+/*-----------------------------------------------------------*/
+
+#if( ipconfigSUPPORT_SIGNALS != 0 )
+
+ /* FreeRTOS_TCPServerWork() calls select().
+ The two functions below provide a possibility to interrupt
+ the call to select(). After the interruption, resume
+ by calling FreeRTOS_TCPServerWork() again. */
+ BaseType_t FreeRTOS_TCPServerSignal( TCPServer_t *pxServer )
+ {
+ BaseType_t xIndex;
+ BaseType_t xResult = pdFALSE;
+ for( xIndex = 0; xIndex < pxServer->xServerCount; xIndex++ )
+ {
+ if( pxServer->xServers[ xIndex ].xSocket != FREERTOS_NO_SOCKET )
+ {
+ FreeRTOS_SignalSocket( pxServer->xServers[ xIndex ].xSocket );
+ xResult = pdTRUE;
+ break;
+ }
+ }
+
+ return xResult;
+ }
+
+#endif /* ipconfigSUPPORT_SIGNALS */
+/*-----------------------------------------------------------*/
+
+#if( ipconfigSUPPORT_SIGNALS != 0 )
+
+ /* Same as above: this function may be called from an ISR,
+ for instance a GPIO interrupt. */
+ BaseType_t FreeRTOS_TCPServerSignalFromISR( TCPServer_t *pxServer, BaseType_t *pxHigherPriorityTaskWoken )
+ {
+ BaseType_t xIndex;
+ BaseType_t xResult = pdFALSE;
+ for( xIndex = 0; xIndex < pxServer->xServerCount; xIndex++ )
+ {
+ if( pxServer->xServers[ xIndex ].xSocket != FREERTOS_NO_SOCKET )
+ {
+ FreeRTOS_SignalSocketFromISR( pxServer->xServers[ xIndex ].xSocket, pxHigherPriorityTaskWoken );
+ xResult = pdTRUE;
+ break;
+ }
+ }
+
+ return xResult;
+ }
+#endif /* ipconfigSUPPORT_SIGNALS */
+/*-----------------------------------------------------------*/
+
+#endif /* ( ipconfigUSE_TCP == 1 ) && ( ( ipconfigUSE_HTTP == 1 ) || ( ipconfigUSE_FTP == 1 ) ) */
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_commands.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_commands.c
new file mode 100644
index 000000000..b399f3648
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_commands.c
@@ -0,0 +1,74 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+
+/* FreeRTOS+TCP includes. */
+#include "FreeRTOS_FTP_commands.h"
+
+const FTPCommand_t xFTPCommands[ FTP_CMD_COUNT ] =
+{
+/* cmdLen cmdName[7] cmdType checkLogin checkNullArg */
+ { 4, "USER", ECMD_USER, pdFALSE, pdFALSE },
+ { 4, "PASS", ECMD_PASS, pdFALSE, pdFALSE },
+ { 4, "ACCT", ECMD_ACCT, pdTRUE, pdFALSE },
+ { 3, "CWD", ECMD_CWD, pdTRUE, pdTRUE },
+ { 4, "CDUP", ECMD_CDUP, pdTRUE, pdFALSE },
+ { 4, "SMNT", ECMD_SMNT, pdTRUE, pdFALSE },
+ { 4, "QUIT", ECMD_QUIT, pdTRUE, pdFALSE },
+ { 4, "REIN", ECMD_REIN, pdTRUE, pdFALSE },
+ { 4, "PORT", ECMD_PORT, pdTRUE, pdFALSE },
+ { 4, "PASV", ECMD_PASV, pdTRUE, pdFALSE },
+ { 4, "TYPE", ECMD_TYPE, pdTRUE, pdFALSE },
+ { 4, "STRU", ECMD_STRU, pdTRUE, pdFALSE },
+ { 4, "MODE", ECMD_MODE, pdTRUE, pdFALSE },
+ { 4, "RETR", ECMD_RETR, pdTRUE, pdTRUE },
+ { 4, "STOR", ECMD_STOR, pdTRUE, pdTRUE },
+ { 4, "STOU", ECMD_STOU, pdTRUE, pdFALSE },
+ { 4, "APPE", ECMD_APPE, pdTRUE, pdFALSE },
+ { 4, "ALLO", ECMD_ALLO, pdTRUE, pdFALSE },
+ { 4, "REST", ECMD_REST, pdTRUE, pdFALSE },
+ { 4, "RNFR", ECMD_RNFR, pdTRUE, pdTRUE },
+ { 4, "RNTO", ECMD_RNTO, pdTRUE, pdTRUE },
+ { 4, "ABOR", ECMD_ABOR, pdTRUE, pdFALSE },
+ { 4, "SIZE", ECMD_SIZE, pdTRUE, pdTRUE },
+ { 4, "MDTM", ECMD_MDTM, pdTRUE, pdTRUE },
+ { 4, "DELE", ECMD_DELE, pdTRUE, pdTRUE },
+ { 3, "RMD", ECMD_RMD, pdTRUE, pdTRUE },
+ { 3, "MKD", ECMD_MKD, pdTRUE, pdTRUE },
+ { 3, "PWD", ECMD_PWD, pdTRUE, pdFALSE },
+ { 4, "LIST", ECMD_LIST, pdTRUE, pdFALSE },
+ { 4, "NLST", ECMD_NLST, pdTRUE, pdFALSE },
+ { 4, "SITE", ECMD_SITE, pdTRUE, pdFALSE },
+ { 4, "SYST", ECMD_SYST, pdFALSE, pdFALSE },
+ { 4, "FEAT", ECMD_FEAT, pdFALSE, pdFALSE },
+ { 4, "STAT", ECMD_STAT, pdTRUE, pdFALSE },
+ { 4, "HELP", ECMD_HELP, pdFALSE, pdFALSE },
+ { 4, "NOOP", ECMD_NOOP, pdFALSE, pdFALSE },
+ { 4, "EMPT", ECMD_EMPTY, pdFALSE, pdFALSE },
+ { 4, "CLOS", ECMD_CLOSE, pdTRUE, pdFALSE },
+ { 4, "UNKN", ECMD_UNKNOWN, pdFALSE, pdFALSE },
+};
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c
new file mode 100644
index 000000000..a2a6ba155
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/FTP/FreeRTOS_FTP_server.c
@@ -0,0 +1,2637 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+/* Standard includes. */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+#include "task.h"
+#include "portmacro.h"
+
+/* FreeRTOS+TCP includes. */
+#include "FreeRTOS_IP.h"
+#include "FreeRTOS_TCP_IP.h"
+#include "FreeRTOS_Sockets.h"
+#include "FreeRTOS_Stream_Buffer.h"
+
+/* FreeRTOS Protocol includes. */
+#include "FreeRTOS_FTP_commands.h"
+#include "FreeRTOS_TCP_server.h"
+#include "FreeRTOS_server_private.h"
+
+/* Remove the whole file if FTP is not supported. */
+#if( ipconfigUSE_FTP == 1 )
+
+#ifndef HTTP_SERVER_BACKLOG
+ #define HTTP_SERVER_BACKLOG ( 12 )
+#endif
+
+#if !defined( ARRAY_SIZE )
+ #define ARRAY_SIZE( x ) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
+#endif
+
+#if defined(__WIN32__) && !defined(ipconfigFTP_FS_USES_BACKSLAH)
+ #define ipconfigFTP_FS_USES_BACKSLAH 1
+#endif
+
+/* Some defines to make the code more readbale */
+#define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
+#define pcNEW_DIR pxClient->pxParent->pcNewDir
+#define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
+
+/* This FTP server will only do binary transfers */
+#define TMODE_BINARY 1
+#define TMODE_ASCII 2
+#define TMODE_7BITS 3
+#define TMODE_8BITS 4
+
+/* Ascii character definitions. */
+#define ftpASCII_CR 13
+#define ftpASCII_LF 10
+
+#if defined( FTP_WRITES_ALIGNED ) || defined( ipconfigFTP_WRITES_ALIGNED )
+ #error Name change : please rename the define to the new name 'ipconfigFTP_ZERO_COPY_ALIGNED_WRITES'
+#endif
+
+/*
+ * ipconfigFTP_ZERO_COPY_ALIGNED_WRITES : experimental optimisation option.
+ * If non-zero, receiving data will be done with the zero-copy method and also
+ * writes to disk will be done with sector-alignment as much as possible.
+ */
+#ifndef ipconfigFTP_ZERO_COPY_ALIGNED_WRITES
+ #define ipconfigFTP_ZERO_COPY_ALIGNED_WRITES 0
+#endif
+
+/*
+ * This module only has 2 public functions:
+ */
+BaseType_t xFTPClientWork( TCPClient_t *pxClient );
+void vFTPClientDelete( TCPClient_t *pxClient );
+
+/*
+ * Process a single command.
+ */
+static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand );
+
+/*
+ * Create a socket for a data connection to the FTP client.
+ */
+static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen );
+
+/*
+ * Either call listen() or connect() to start the transfer connection.
+ */
+static BaseType_t prvTransferStart( FTPClient_t *pxClient );
+
+/*
+ * See if the socket has got connected or disconnected. Close the socket if
+ * necessary.
+ */
+static void prvTransferCheck( FTPClient_t *pxClient );
+
+/*
+ * Close the data socket and issue some informative logging.
+ */
+static void prvTransferCloseSocket( FTPClient_t *pxClient );
+
+/*
+ * Close the file handle (pxReadHandle or pxWriteHandle).
+ */
+static void prvTransferCloseFile( FTPClient_t *pxClient );
+
+/*
+ * Close a directory (-handle).
+ */
+static void prvTransferCloseDir( FTPClient_t *pxClient );
+
+/*
+ * Translate a string (indicating a transfer type) to a number.
+ */
+static BaseType_t prvGetTransferType( const char *pcType );
+
+#if( ipconfigHAS_PRINTF != 0 )
+ /*
+ * For nice logging: write an amount (number of bytes), e.g. 3512200 as
+ * "3.45 MB"
+ */
+ static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize );
+#endif
+
+#if( ipconfigHAS_PRINTF != 0 )
+ /*
+ * Calculate the average as bytes-per-second, when amount and milliseconds
+ * are known.
+ */
+ static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs );
+#endif
+
+/*
+ * A port command looks like: PORT h1,h2,h3,h4,p1,p2. Parse it and translate it
+ * to an IP-address and a port number.
+ */
+static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress );
+
+/*
+ * CWD: Change current working directory.
+ */
+
+static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory );
+
+/*
+ * RNFR: Rename from ...
+ */
+static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName );
+
+/*
+ * RNTO: Rename to ...
+ */
+static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName );
+
+/*
+ * SITE: Change file permissions.
+ */
+static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand );
+
+/*
+ * DELE: Delete a file.
+ */
+static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName );
+
+/*
+ * SIZE: get the size of a file (xSendDate = 0).
+ * MDTM: get data and time properties (xSendDate = 1).
+ */
+static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate );
+
+/*
+ * MKD: Make / create a directory (xDoRemove = 0).
+ * RMD: Remove a directory (xDoRemove = 1).
+ */
+static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove );
+
+/*
+ * The next three commands: LIST, RETR and STOR all require a data socket.
+ * The data connection is either started with a 'PORT' or a 'PASV' command.
+ * Each of the commands has a prepare- (Prep) and a working- (Work) function.
+ * The Work function should be called as long as the data socket is open, and
+ * there is data to be transmitted.
+ */
+
+/*
+ * LIST: Send a directory listing in Unix style.
+ */
+static BaseType_t prvListSendPrep( FTPClient_t *pxClient );
+static BaseType_t prvListSendWork( FTPClient_t *pxClient );
+
+/*
+ * RETR: Send a file to the FTP client.
+ */
+static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName );
+static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient );
+
+/*
+ * STOR: Receive a file from the FTP client and store it.
+ */
+static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName );
+static BaseType_t prvStoreFileWork( FTPClient_t *pxClient );
+
+/*
+ * Print/format a single directory entry in Unix style.
+ */
+static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength );
+
+/*
+ * Send a reply to a socket, either the command- or the data-socket.
+ */
+static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength );
+
+/*
+ * Prepend the root directory (if any), plus the current working directory
+ * (always), to get an absolute path.
+ */
+BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcPath );
+
+/*
+
+####### ##### ###### # # ##
+ # ## # # # # # # # #
+ # # # # # # #
+ # # # # # # # #### ### ## # #
+ ##### # ##### # # # # # # # # # #
+ # # # # # # # # # ## # ####
+ # # # ## ## # # # # #
+ # # # ## ## # # # # #
+#### #### #### ## ## #### #### ## ##
+
+ * xFTPClientWork()
+ * will be called by FreeRTOS_TCPServerWork(), after select has expired().
+ * FD_ISSET will not be used. This work function will always be called at
+ * regular intervals, and also after a select() event has occurred.
+ */
+BaseType_t xFTPClientWork( TCPClient_t *pxTCPClient )
+{
+FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;
+BaseType_t xRc;
+
+ if( pxClient->bits.bHelloSent == pdFALSE_UNSIGNED )
+ {
+ BaseType_t xLength;
+
+ pxClient->bits.bHelloSent = pdTRUE_UNSIGNED;
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "220 Welcome to the FreeRTOS+TCP FTP server\r\n" );
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ }
+
+ /* Call recv() in a non-blocking way, to see if there is an FTP command
+ sent to this server. */
+ xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
+
+ if( xRc > 0 )
+ {
+ BaseType_t xIndex;
+ const FTPCommand_t *pxCommand;
+ char *pcRestCommand;
+
+ if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
+ {
+ pcCOMMAND_BUFFER[ xRc ] = '\0';
+ }
+
+ while( xRc && ( ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_CR ) || ( pcCOMMAND_BUFFER[ xRc - 1 ] == ftpASCII_LF ) ) )
+ {
+ pcCOMMAND_BUFFER[ --xRc ] = '\0';
+ }
+
+ /* Now iterate through a list of FTP commands, and look for a match. */
+ pxCommand = xFTPCommands;
+ pcRestCommand = pcCOMMAND_BUFFER;
+ for( xIndex = 0; xIndex < FTP_CMD_COUNT - 1; xIndex++, pxCommand++ )
+ {
+ BaseType_t xLength;
+
+ /* The length of each command is stored as well, just to be a bit
+ quicker here. */
+ xLength = pxCommand->xCommandLength;
+
+ if( ( xRc >= xLength ) && ( memcmp( ( const void * ) pxCommand->pcCommandName, ( const void * ) pcCOMMAND_BUFFER, xLength ) == 0 ) )
+ {
+ /* A match with an existing command is found. Skip any
+ whitespace to get the first parameter. */
+ pcRestCommand += xLength;
+ while( ( *pcRestCommand == ' ' ) || ( *pcRestCommand == '\t' ) )
+ {
+ pcRestCommand++;
+ }
+ break;
+ }
+ }
+
+ /* If the command received was not recognised, xIndex will point to a
+ fake entry called 'ECMD_UNKNOWN'. */
+ prvProcessCommand( pxClient, xIndex, pcRestCommand );
+ }
+ else if( xRc < 0 )
+ {
+ /* The connection will be closed and the client will be deleted. */
+ FreeRTOS_printf( ( "xFTPClientWork: xRc = %ld\n", xRc ) );
+ }
+
+ /* Does it have an open data connection? */
+ if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
+ {
+ /* See if the connection has changed. */
+ prvTransferCheck( pxClient );
+
+ /* "pcConnectionAck" contains a string like:
+ "Response: 150 Accepted data connection from 192.168.2.3:6789"
+ The socket can only be used once this acknowledgement has been sent. */
+ if( ( pxClient->xTransferSocket != FREERTOS_NO_SOCKET ) && ( pxClient->pcConnectionAck[ 0 ] == '\0' ) )
+ {
+ BaseType_t xClientRc = 0;
+
+ if( pxClient->bits1.bDirHasEntry )
+ {
+ /* Still listing a directory. */
+ xClientRc = prvListSendWork( pxClient );
+ }
+ else if( pxClient->pxReadHandle != NULL )
+ {
+ /* Sending a file. */
+ xClientRc = prvRetrieveFileWork( pxClient );
+ }
+ else if( pxClient->pxWriteHandle != NULL )
+ {
+ /* Receiving a file. */
+ xClientRc = prvStoreFileWork( pxClient );
+ }
+
+ if( xClientRc < 0 )
+ {
+ prvTransferCloseSocket( pxClient );
+ prvTransferCloseFile( pxClient );
+ }
+ }
+ }
+
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+static void prvTransferCloseDir( FTPClient_t *pxClient )
+{
+ /* Nothing to close for +FAT. */
+ ( void ) pxClient;
+}
+/*-----------------------------------------------------------*/
+
+void vFTPClientDelete( TCPClient_t *pxTCPClient )
+{
+FTPClient_t *pxClient = ( FTPClient_t * ) pxTCPClient;
+
+ /* Close any directory-listing-handles (not used by +FAT ). */
+ prvTransferCloseDir( pxClient );
+ /* Close the data-socket. */
+ prvTransferCloseSocket( pxClient );
+ /* Close any open file handle. */
+ prvTransferCloseFile( pxClient );
+
+ /* Close the FTP command socket */
+ if( pxClient->xSocket != FREERTOS_NO_SOCKET )
+ {
+ FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
+ FreeRTOS_closesocket( pxClient->xSocket );
+ pxClient->xSocket = FREERTOS_NO_SOCKET;
+ }
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvProcessCommand( FTPClient_t *pxClient, BaseType_t xIndex, char *pcRestCommand )
+{
+const FTPCommand_t *pxFTPCommand = &( xFTPCommands[ xIndex ] );
+const char *pcMyReply = NULL;
+BaseType_t xResult = 0;
+
+ if( ( pxFTPCommand->ucCommandType != ECMD_PASS ) && ( pxFTPCommand->ucCommandType != ECMD_PORT ) )
+ {
+ FreeRTOS_printf( ( " %s %s\n", pxFTPCommand->pcCommandName, pcRestCommand ) );
+ }
+
+ if( ( pxFTPCommand->checkLogin != pdFALSE ) && ( pxClient->bits.bLoggedIn == pdFALSE_UNSIGNED ) )
+ {
+ pcMyReply = REPL_530; /* Please first log in. */
+ }
+ else if( ( pxFTPCommand->checkNullArg != pdFALSE ) && ( ( pcRestCommand == NULL ) || ( pcRestCommand[ 0 ] == '\0' ) ) )
+ {
+ pcMyReply = REPL_501; /* Command needs a parameter. */
+ }
+
+ if( pcMyReply == NULL )
+ {
+ switch( pxFTPCommand->ucCommandType )
+ {
+ case ECMD_USER: /* User. */
+ /* User name has been entered, expect password. */
+ pxClient->bits.bStatusUser = pdTRUE_UNSIGNED;
+
+ #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */
+ {
+ /* Save the user name in 'pcFileName'. */
+ snprintf( pxClient->pcFileName, sizeof( pxClient->pcFileName ), "%s", pcRestCommand );
+
+ /* The USER name is presented to the application. The function
+ may return a const string like "331 Please enter your
+ password\r\n". */
+ pcMyReply = pcApplicationFTPUserHook( pxClient->pcFileName );
+ if( pcMyReply == NULL )
+ {
+ pcMyReply = REPL_331_ANON;
+ }
+ }
+ #else
+ {
+ /* No password checks, any password will be accepted. */
+ pcMyReply = REPL_331_ANON;
+ }
+ #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 */
+
+ #if( ipconfigFTP_HAS_USER_PROPERTIES_HOOK != 0 )/*_RB_ Needs defaulting and adding to the web documentation. */
+ {
+ FTPUserProperties_t xProperties;
+
+ xProperties.pcRootDir = pxClient->pcRootDir;
+ xProperties.xReadOnly = pdFALSE;
+ xProperties.usPortNumber = pxClient->usClientPort;
+ vApplicationFTPUserPropertiesHook( pxClient->pcFileName, &( xProperties ) );
+
+ if( xProperties.pcRootDir != NULL )
+ {
+ pxClient->pcRootDir = xProperties.pcRootDir;
+ }
+ pxClient->bits.bReadOnly = ( xProperties.xReadOnly != pdFALSE_UNSIGNED );
+ }
+ #endif /* ipconfigFTP_HAS_USER_PROPERTIES_HOOK */
+ break;
+
+ case ECMD_PASS: /* Password. */
+ pxClient->ulRestartOffset = 0;
+ if( pxClient->bits.bStatusUser == pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_503; /* "503 Bad sequence of commands.\r\n". */
+ }
+ else
+ {
+ BaseType_t xAllow;
+
+ pxClient->bits.bStatusUser = pdFALSE_UNSIGNED;
+ #if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )
+ {
+ xAllow = xApplicationFTPPasswordHook( pxClient->pcFileName, pcRestCommand );
+ }
+ #else
+ {
+ xAllow = 1;
+ }
+ #endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */
+
+ if( xAllow > 0 )
+ {
+ pxClient->bits.bLoggedIn = pdTRUE_UNSIGNED; /* Client has now logged in. */
+ pcMyReply = "230 OK. Current directory is /\r\n";
+ }
+ else
+ {
+ pcMyReply = "530 Login incorrect\r\n"; /* 530 Login incorrect. */
+ }
+
+ strcpy( pxClient->pcCurrentDir, ( const char * ) "/" );
+ }
+ break;
+
+ case ECMD_SYST: /* System. */
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "215 UNIX Type: L8\r\n" );
+ pcMyReply = pcCOMMAND_BUFFER;
+ break;
+
+ case ECMD_PWD: /* Get working directory. */
+ xMakeRelative( pxClient, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), pxClient->pcCurrentDir );
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_257_PWD, pcFILE_BUFFER );
+ pcMyReply = pcCOMMAND_BUFFER;
+ break;
+
+ case ECMD_REST:
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else
+ {
+ const char *pcPtr = pcRestCommand;
+
+ while( *pcPtr == ' ' )
+ {
+ pcPtr++;
+ }
+
+ if( ( *pcPtr >= '0' ) && ( *pcPtr <= '9' ) )
+ {
+ sscanf( pcPtr, "%lu", &pxClient->ulRestartOffset );
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "350 Restarting at %lu. Send STORE or RETRIEVE\r\n", pxClient->ulRestartOffset );
+ pcMyReply = pcCOMMAND_BUFFER;
+ }
+ else
+ {
+ pcMyReply = REPL_500; /* 500 Syntax error, command unrecognised. */
+ }
+ }
+ break;
+
+ case ECMD_NOOP: /* NOP operation */
+ if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
+ {
+ pcMyReply = REPL_200_PROGRESS;
+ }
+ else
+ {
+ pcMyReply = REPL_200;
+ }
+ break;
+
+ case ECMD_TYPE: /* Ask or set transfer type. */
+ {
+ /* e.g. "TYPE I" for Images (binary). */
+ BaseType_t xType = prvGetTransferType( pcRestCommand );
+
+ if( xType < 0 )
+ {
+ /* TYPE not recognised. */
+ pcMyReply = REPL_500;
+ }
+ else
+ {
+ pxClient->xTransType = xType;
+ pcMyReply = REPL_200;
+ }
+ }
+ break;
+
+ case ECMD_PASV: /* Enter passive mode. */
+ /* Connect passive: Server will listen() and wait for a connection.
+ Start up a new data connection with 'xDoListen' set to true. */
+ if( prvTransferConnect( pxClient, pdTRUE ) == pdFALSE )
+ {
+ pcMyReply = REPL_502;
+ }
+ else
+ {
+ uint32_t ulIP;
+ uint16_t ulPort;
+ struct freertos_sockaddr xLocalAddress;
+ struct freertos_sockaddr xRemoteAddress;
+
+ FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );
+ FreeRTOS_GetRemoteAddress( pxClient->xSocket, &xRemoteAddress );
+
+ ulIP = FreeRTOS_ntohl( xLocalAddress.sin_addr );
+ pxClient->ulClientIP = FreeRTOS_ntohl( xRemoteAddress.sin_addr );
+ ulPort = FreeRTOS_ntohs( xLocalAddress.sin_port );
+
+ pxClient->usClientPort = FreeRTOS_ntohs( xRemoteAddress.sin_port );
+
+ /* REPL_227_D "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d). */
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), REPL_227_D,
+ ( unsigned )ulIP >> 24,
+ ( unsigned )( ulIP >> 16 ) & 0xFF,
+ ( unsigned )( ulIP >> 8 ) & 0xFF,
+ ( unsigned )ulIP & 0xFF,
+ ( unsigned )ulPort >> 8,
+ ( unsigned )ulPort & 0xFF );
+
+ pcMyReply = pcCOMMAND_BUFFER;
+ }
+ break;
+
+ case ECMD_PORT: /* Active connection to the client. */
+ /* The client uses this command to tell the server to what
+ client-side port the server should contact; use of this command
+ indicates an active data transfer. e.g. PORT 192,168,1,2,4,19. */
+ {
+ uint32_t ulIPAddress = 0;
+ UBaseType_t uxPort;
+
+ uxPort = prvParsePortData( pcRestCommand, &ulIPAddress );
+ FreeRTOS_printf( (" PORT %lxip:%ld\n", ulIPAddress, uxPort ) );
+
+ if( uxPort == 0u )
+ {
+ pcMyReply = REPL_501;
+ }
+ else if( prvTransferConnect( pxClient, pdFALSE ) == pdFALSE )
+ {
+ /* Call prvTransferConnect() with 'xDoListen' = false for an
+ active connect(). */
+ pcMyReply = REPL_501;
+ }
+ else
+ {
+ pxClient->usClientPort = ( uint16_t ) uxPort;
+ pxClient->ulClientIP = ulIPAddress;
+ FreeRTOS_printf( ("Client address %lxip:%lu\n", ulIPAddress, uxPort ) );
+ pcMyReply = REPL_200;
+ }
+ }
+ break;
+
+ case ECMD_CWD: /* Change current working directory. */
+ prvChangeDir( pxClient, pcRestCommand );
+ break;
+
+ case ECMD_RNFR:
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else
+ {
+ prvRenameFrom( pxClient, pcRestCommand );
+ }
+ break;
+
+ case ECMD_RNTO:
+ if( pxClient->bits.bInRename == pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_503; /* "503 Bad sequence of commands. */
+ }
+ else
+ {
+ prvRenameTo( pxClient, pcRestCommand );
+ }
+ break;
+
+ case ECMD_SITE: /* Set file permissions */
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else if( prvSiteCmd( pxClient, pcRestCommand ) == pdFALSE )
+ {
+ pcMyReply = REPL_202;
+ }
+ break;
+
+ case ECMD_DELE:
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else
+ {
+ prvDeleteFile( pxClient, pcRestCommand );
+ }
+ break;
+
+ case ECMD_MDTM:
+ prvSizeDateFile( pxClient, pcRestCommand, pdTRUE );
+ break;
+
+ case ECMD_SIZE:
+ if( pxClient->pxWriteHandle != NULL )
+ {
+ /* This SIZE query is probably about a file which is now being
+ received. If so, return the value of pxClient->ulRecvBytes,
+ pcRestCommand points to 'pcCommandBuffer', make it free by
+ copying it to pcNewDir. */
+
+ xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcRestCommand );
+
+ if( strcmp( pcNEW_DIR, pcRestCommand ) == 0 )
+ {
+ BaseType_t xCount;
+ for( xCount = 0; xCount < 3 && pxClient->pxWriteHandle; xCount++ )
+ {
+ prvStoreFileWork( pxClient );
+ }
+ if( pxClient->pxWriteHandle != NULL )
+ {
+ /* File being queried is still open, return number of
+ bytes received until now. */
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", pxClient->ulRecvBytes );
+ pcMyReply = pcCOMMAND_BUFFER;
+ } /* otherwise, do a normal stat(). */
+ }
+ strcpy( pcRestCommand, pcNEW_DIR );
+ }
+ if( pcMyReply == NULL )
+ {
+ prvSizeDateFile( pxClient, pcRestCommand, pdFALSE );
+ }
+ break;
+ case ECMD_MKD:
+ case ECMD_RMD:
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else
+ {
+ prvMakeRemoveDir( pxClient, pcRestCommand, pxFTPCommand->ucCommandType == ECMD_RMD );
+ }
+ break;
+ case ECMD_CDUP:
+ prvChangeDir( pxClient, ".." );
+ break;
+
+ case ECMD_QUIT:
+ prvSendReply( pxClient->xSocket, REPL_221, 0 );
+ pxClient->bits.bLoggedIn = pdFALSE_UNSIGNED;
+ break;
+ case ECMD_LIST:
+ case ECMD_RETR:
+ case ECMD_STOR:
+ if( ( pxClient->xTransferSocket == FREERTOS_NO_SOCKET ) &&
+ ( ( pxFTPCommand->ucCommandType != ECMD_STOR ) ||
+ ( pxClient->bits1.bEmptyFile == pdFALSE_UNSIGNED ) ) )
+ {
+ /* Sending "425 Can't open data connection." :
+ Before receiving any of these commands, there must have been a
+ PORT or PASV command, which causes the creation of a data socket. */
+ /* There is one exception: a STOR command is received while the
+ data connection has already been closed. This is tested with the
+ 'bEmptyFile' flag. */
+ pcMyReply = REPL_425;
+ }
+ else
+ {
+ /* In case an empty file was received ( bits1.bEmptyFile ), the
+ transfer socket never delivered any data. Check if the transfer
+ socket is still open: */
+ if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
+ {
+ prvTransferCheck( pxClient );
+ }
+ switch( pxFTPCommand->ucCommandType )
+ {
+ case ECMD_LIST:
+ prvListSendPrep( pxClient );
+ break;
+ case ECMD_RETR:
+ prvRetrieveFilePrep( pxClient, pcRestCommand );
+ break;
+ case ECMD_STOR:
+ if( pxClient->bits.bReadOnly != pdFALSE_UNSIGNED )
+ {
+ pcMyReply = REPL_553_READ_ONLY;
+ }
+ else
+ {
+ prvStoreFilePrep( pxClient, pcRestCommand );
+ if( pxClient->bits1.bEmptyFile != pdFALSE_UNSIGNED )
+ {
+ /* Although the 'xTransferSocket' is closed already,
+ call this function just for the logging. */
+ prvTransferCloseSocket( pxClient );
+
+ /* Close an empty file. */
+ prvTransferCloseFile( pxClient );
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case ECMD_FEAT:
+ {
+ static const char pcFeatAnswer[] =
+ "211-Features:\x0a"
+ /* The MDTM command is only allowed when
+ there is support for date&time. */
+ #if( ffconfigTIME_SUPPORT != 0 )
+ " MDTM\x0a"
+ #endif
+ " REST STREAM\x0a"
+ " SIZE\x0d\x0a"
+ "211 End\x0d\x0a";
+ pcMyReply = pcFeatAnswer;
+ }
+ break;
+
+ case ECMD_UNKNOWN:
+ FreeRTOS_printf( ("ftp::processCmd: Cmd %s unknown\n", pcRestCommand ) );
+ pcMyReply = REPL_500;
+ break;
+ }
+ }
+ if( pxFTPCommand->ucCommandType != ECMD_RNFR )
+ {
+ pxClient->bits.bInRename = pdFALSE_UNSIGNED;
+ }
+
+ if( pcMyReply != NULL )
+ {
+ xResult = prvSendReply( pxClient->xSocket, pcMyReply, strlen( pcMyReply ) );
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvTransferConnect( FTPClient_t *pxClient, BaseType_t xDoListen )
+{
+Socket_t xSocket;
+BaseType_t xResult;
+
+ /* Open a socket for a data connection with the FTP client.
+ Happens after a PORT or a PASV command. */
+
+ /* Make sure the previous socket is deleted and flags reset */
+ prvTransferCloseSocket( pxClient );
+
+ pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
+
+ xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
+
+ if( ( xSocket != FREERTOS_NO_SOCKET ) && ( xSocket != FREERTOS_INVALID_SOCKET ) )
+ {
+ BaseType_t xSmallTimeout = pdMS_TO_TICKS( 100 );
+ struct freertos_sockaddr xAddress;
+
+ #if( ipconfigFTP_TX_BUFSIZE > 0 )
+ WinProperties_t xWinProps;
+ #endif
+ xAddress.sin_addr = FreeRTOS_GetIPAddress( ); /* Single NIC, currently not used */
+ xAddress.sin_port = FreeRTOS_htons( 0 ); /* Bind to any available port number */
+
+ FreeRTOS_bind( xSocket, &xAddress, sizeof( xAddress ) );
+
+ #if( ipconfigFTP_TX_BUFSIZE > 0 )
+ {
+ /* Fill in the buffer and window sizes that will be used by the
+ socket. */
+ xWinProps.lTxBufSize = ipconfigFTP_TX_BUFSIZE;
+ xWinProps.lTxWinSize = ipconfigFTP_TX_WINSIZE;
+ xWinProps.lRxBufSize = ipconfigFTP_RX_BUFSIZE;
+ xWinProps.lRxWinSize = ipconfigFTP_RX_WINSIZE;
+
+ /* Set the window and buffer sizes. */
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) );
+ }
+ #endif
+
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xSmallTimeout, sizeof( BaseType_t ) );
+
+ /* The same instance of the socket will be used for the connection and
+ data transport. */
+ if( xDoListen != pdFALSE )
+ {
+ BaseType_t xTrueValue = pdTRUE;
+ FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_REUSE_LISTEN_SOCKET, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
+ }
+ pxClient->bits1.bIsListen = xDoListen;
+ pxClient->xTransferSocket = xSocket;
+
+ if( xDoListen != pdFALSE )
+ {
+ FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ );
+ /* Calling FreeRTOS_listen( ) */
+ xResult = prvTransferStart( pxClient );
+ if( xResult >= 0 )
+ {
+ xResult = pdTRUE;
+ }
+ }
+ else
+ {
+ FreeRTOS_FD_SET( xSocket, pxClient->pxParent->xSocketSet, eSELECT_EXCEPT | eSELECT_READ | eSELECT_WRITE );
+ xResult = pdTRUE;
+ }
+ }
+ else
+ {
+ FreeRTOS_printf( ( "FreeRTOS_socket() failed\n" ) );
+ xResult = -pdFREERTOS_ERRNO_ENOMEM;
+ }
+
+ /* An active socket (PORT) should connect() later. */
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvTransferStart( FTPClient_t *pxClient )
+{
+BaseType_t xResult;
+
+ /* A transfer socket has been opened, now either call listen() for 'PASV'
+ or connect() for the 'PORT' command. */
+ if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
+ {
+ xResult = FreeRTOS_listen( pxClient->xTransferSocket, 1 );
+ }
+ else
+ {
+ struct freertos_sockaddr xAddress;
+
+ xAddress.sin_addr = FreeRTOS_htonl( pxClient->ulClientIP );
+ xAddress.sin_port = FreeRTOS_htons( pxClient->usClientPort );
+ /* Start an active connection for this data socket */
+ xResult = FreeRTOS_connect( pxClient->xTransferSocket, &xAddress, sizeof( xAddress ) );
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+static void prvTransferCheck( FTPClient_t *pxClient )
+{
+BaseType_t xRxSize;
+
+ /* A data transfer is busy. Check if there are changes in connectedness. */
+ xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );
+
+ if( pxClient->bits1.bClientConnected == pdFALSE_UNSIGNED )
+ {
+ /* The time to receive a small file can be so short, that we don't even
+ see that the socket gets connected and disconnected. Therefore, check
+ the sizeof of the RX buffer. */
+ {
+ struct freertos_sockaddr xAddress;
+ Socket_t xNexSocket;
+ socklen_t xSocketLength = sizeof( xAddress );
+
+ if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
+ {
+ xNexSocket = FreeRTOS_accept( pxClient->xTransferSocket, &xAddress, &xSocketLength);
+ if( ( ( xNexSocket != FREERTOS_NO_SOCKET ) && ( xNexSocket != FREERTOS_INVALID_SOCKET ) ) ||
+ xRxSize > 0 )
+ {
+ pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;
+ }
+ }
+ else
+ {
+ if( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) > 0 ||
+ xRxSize > 0 )
+ {
+ pxClient->bits1.bClientConnected = pdTRUE_UNSIGNED;
+ }
+ }
+ if( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
+ {
+ pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
+ #if( ipconfigHAS_PRINTF != 0 )
+ {
+ struct freertos_sockaddr xRemoteAddress, xLocalAddress;
+ FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );
+ FreeRTOS_GetLocalAddress( pxClient->xTransferSocket, &xLocalAddress );
+ FreeRTOS_printf( ( "%s Connected from %u to %u\n",
+ pxClient->bits1.bIsListen != pdFALSE_UNSIGNED ? "PASV" : "PORT",
+ ( unsigned ) FreeRTOS_ntohs( xLocalAddress.sin_port ),
+ ( unsigned ) FreeRTOS_ntohs( xRemoteAddress.sin_port ) ) );
+ }
+ #endif /* ipconfigHAS_PRINTF */
+ FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
+ FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_READ|eSELECT_EXCEPT );
+ }
+ }
+ }
+
+ if ( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
+ {
+ if( pxClient->pcConnectionAck[ 0 ] != '\0' )
+ {
+ BaseType_t xLength;
+ BaseType_t xRemotePort;
+ struct freertos_sockaddr xRemoteAddress;
+
+ FreeRTOS_GetRemoteAddress( pxClient->xTransferSocket, &xRemoteAddress );
+ xRemotePort = FreeRTOS_ntohs( xRemoteAddress.sin_port );
+
+ /* Tell on the command port 21 we have a data connection */
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ pxClient->pcConnectionAck, pxClient->ulClientIP, xRemotePort );
+
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ pxClient->pcConnectionAck[ 0 ] = '\0';
+ }
+
+ if( ( FreeRTOS_issocketconnected( pxClient->xTransferSocket ) == pdFALSE ) && FreeRTOS_rx_size( pxClient->xTransferSocket ) == 0 )
+ {
+ prvTransferCloseSocket( pxClient );
+ prvTransferCloseFile( pxClient );
+ }
+ }
+}
+/*-----------------------------------------------------------*/
+
+static void prvTransferCloseSocket( FTPClient_t *pxClient )
+{
+ if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
+ {
+ /* DEBUGGING ONLY */
+ BaseType_t xRxSize = FreeRTOS_rx_size( pxClient->xTransferSocket );
+ if( xRxSize > 0 )
+ {
+ BaseType_t xRxSize2;
+ BaseType_t xStatus;
+ prvStoreFileWork( pxClient );
+ xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );
+ xRxSize2 = FreeRTOS_rx_size( pxClient->xTransferSocket );
+ FreeRTOS_printf( ( "FTP: WARNING: %s: RX size = %ld -> %ld (%s)\n",
+ FreeRTOS_GetTCPStateName( xStatus ),
+ xRxSize, xRxSize2, pxClient->pcFileName ) );
+ if( xRxSize2 > 1 )
+ {
+ return;
+ }
+
+ /* Remove compiler warnings in case FreeRTOS_printf() is not
+ defined. */
+ ( void ) xStatus;
+ }
+ }
+
+ if( ( pxClient->pxWriteHandle != NULL ) || ( pxClient->pxReadHandle != NULL ) )
+ {
+ BaseType_t xLength;
+ char pcStrBuf[ 32 ];
+
+ if( pxClient->bits1.bHadError == pdFALSE_UNSIGNED )
+ {
+ xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
+ "226 Closing connection %d bytes transmitted\r\n", ( int ) pxClient->ulRecvBytes );
+ }
+ else
+ {
+ xLength = snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
+ "451 Requested action aborted after %d bytes\r\n", ( int ) pxClient->ulRecvBytes );
+ }
+
+ /* Tell on the command socket the data connection is now closed. */
+ prvSendReply( pxClient->xSocket, pxClient->pcClientAck, xLength );
+
+ #if( ipconfigHAS_PRINTF != 0 )
+ {
+ TickType_t xDelta;
+ uint32_t ulAverage;
+ xDelta = xTaskGetTickCount( ) - pxClient->xStartTime;
+ ulAverage = ulGetAverage( pxClient->ulRecvBytes, xDelta );
+
+ FreeRTOS_printf( ("FTP: %s: '%s' %lu Bytes (%s/sec)\n",
+ pxClient->pxReadHandle ? "sent" : "recv",
+ pxClient->pcFileName,
+ pxClient->ulRecvBytes,
+ pcMkSize( ulAverage, pcStrBuf, sizeof( pcStrBuf ) ) ) );
+ }
+ #endif
+ }
+
+ if( pxClient->xTransferSocket != FREERTOS_NO_SOCKET )
+ {
+ FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
+ FreeRTOS_closesocket( pxClient->xTransferSocket );
+ pxClient->xTransferSocket = FREERTOS_NO_SOCKET;
+ if( pxClient->ulRecvBytes == 0ul )
+ {
+ /* Received zero bytes: an empty file */
+ pxClient->bits1.bEmptyFile = pdTRUE_UNSIGNED;
+ }
+ else
+ {
+ pxClient->bits1.bEmptyFile = pdFALSE_UNSIGNED;
+ }
+ }
+ pxClient->bits1.bIsListen = pdFALSE_UNSIGNED;
+ pxClient->bits1.bDirHasEntry = pdFALSE_UNSIGNED;
+ pxClient->bits1.bClientConnected = pdFALSE_UNSIGNED;
+ pxClient->bits1.bHadError = pdFALSE_UNSIGNED;
+}
+/*-----------------------------------------------------------*/
+
+static void prvTransferCloseFile( FTPClient_t *pxClient )
+{
+ if( pxClient->pxWriteHandle != NULL )
+ {
+ ff_fclose( pxClient->pxWriteHandle );
+ pxClient->pxWriteHandle = NULL;
+ #if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )
+ {
+ vApplicationFTPReceivedHook( pxClient->pcFileName, pxClient->ulRecvBytes, pxClient );
+ }
+ #endif
+
+ }
+ if( pxClient->pxReadHandle != NULL )
+ {
+ ff_fclose( pxClient->pxReadHandle );
+ pxClient->pxReadHandle = NULL;
+ }
+ /* These two field are only used for logging / file-statistics */
+ pxClient->ulRecvBytes = 0ul;
+ pxClient->xStartTime = 0ul;
+}
+/*-----------------------------------------------------------*/
+
+/**
+ * Guess the transfer type, given the client requested type.
+ * Actually in unix there is no difference between binary and
+ * ascii mode when we work with file descriptors.
+ * If #type is not recognized as a valid client request, -1 is returned.
+ */
+static BaseType_t prvGetTransferType( const char *pcType )
+{
+BaseType_t xResult = -1;
+
+ if( pcType != NULL )
+ {
+ BaseType_t xLength = strlen( pcType );
+ if( xLength == 0 )
+ {
+ return -1;
+ }
+ switch( pcType[ 0 ] ) {
+ case 'I':
+ xResult = TMODE_BINARY;
+ break;
+ case 'A':
+ xResult = TMODE_ASCII;
+ break;
+ case 'L':
+ if( xLength >= 3 )
+ {
+ if( pcType[ 2 ] == '7' )
+ {
+ xResult = TMODE_7BITS;
+ }
+ else if( pcType[ 2 ] == '8' )
+ {
+ xResult = TMODE_7BITS;
+ }
+ }
+ break;
+ }
+ }
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+#if( ipconfigHAS_PRINTF != 0 )
+ #define SIZE_1_GB ( 1024ul * 1024ul * 1024ul )
+ #define SIZE_1_MB ( 1024ul * 1024ul )
+ #define SIZE_1_KB ( 1024ul )
+
+ static const char *pcMkSize( uint32_t ulAmount, char *pcBuffer, BaseType_t xBufferSize )
+ {
+ uint32_t ulGB, ulMB, ulKB, ulByte;
+
+ ulGB = ( ulAmount / SIZE_1_GB );
+ ulAmount -= ( ulGB * SIZE_1_GB );
+ ulMB = ( ulAmount / SIZE_1_MB );
+ ulAmount -= ( ulMB * SIZE_1_MB );
+ ulKB = ( ulAmount / SIZE_1_KB );
+ ulAmount -= ( ulKB * SIZE_1_KB );
+ ulByte = ( ulAmount );
+
+ if (ulGB != 0ul )
+ {
+ snprintf( pcBuffer, xBufferSize, "%lu.%02lu GB", ulGB, (100 * ulMB) / SIZE_1_KB );
+ }
+ else if( ulMB != 0ul )
+ {
+ snprintf( pcBuffer, xBufferSize, "%lu.%02lu MB", ulMB, (100 * ulKB) / SIZE_1_KB );
+ }
+ else if( ulKB != 0ul )
+ {
+ snprintf(pcBuffer, xBufferSize, "%lu.%02lu KB", ulKB, (100 * ulByte) / SIZE_1_KB );
+ }
+ else
+ {
+ snprintf( pcBuffer, xBufferSize, "%lu bytes", ulByte );
+ }
+
+ return pcBuffer;
+ }
+ /*-----------------------------------------------------------*/
+#endif /* ipconfigHAS_PRINTF != 0 */
+
+#if( ipconfigHAS_PRINTF != 0 )
+ static uint32_t ulGetAverage( uint32_t ulAmount, TickType_t xDeltaMs )
+ {
+ uint32_t ulAverage;
+
+ /* Get the average amount of bytes per seconds. Ideally this is
+ calculated by Multiplying with 1000 and dividing by milliseconds:
+ ulAverage = ( 1000ul * ulAmount ) / xDeltaMs;
+ Now get a maximum precision, while avoiding an arithmetic overflow:
+ */
+ if( xDeltaMs == 0ul )
+ {
+ /* Time is zero, there is no average */
+ ulAverage = 0ul;
+ }
+ else if( ulAmount >= ( ~0ul / 10ul ) )
+ {
+ /* More than 409 MB has been transferred, do not multiply. */
+ ulAverage = ( ulAmount / ( xDeltaMs / 1000ul ) );
+ }
+ else if( ulAmount >= ( ~0ul / 100ul ) )
+ {
+ /* Between 409 and 41 MB has been transferred, can multiply by 10. */
+ ulAverage = ( ( ulAmount * 10ul ) / ( xDeltaMs / 100ul ) );
+ }
+ else if( ulAmount >= ( ~0ul / 1000ul ) )
+ {
+ /* Between 4.1 MB and 41 has been transferred, can multiply by 100. */
+ ulAverage = ( ( ulAmount * 100ul ) / ( xDeltaMs / 10ul ) );
+ }
+ else
+ {
+ /* Less than 4.1 MB: can multiply by 1000. */
+ ulAverage = ( ( ulAmount * 1000ul ) / xDeltaMs );
+ }
+
+ return ulAverage;
+ }
+ /*-----------------------------------------------------------*/
+#endif /* ipconfigHAS_PRINTF != 0 */
+
+static UBaseType_t prvParsePortData( const char *pcCommand, uint32_t *pulIPAddress )
+{
+/*_HT_ Using 'unsigned' here because when sscanf() sees '%u', it expects a pointer to 'unsigned'.
+Not sure about the sscanf() format for UBaseType_t ? */
+unsigned h1, h2, h3, h4, p1, p2;
+char sep;
+UBaseType_t uxResult;
+
+ /* Expect PORT h1,h2,h3,h4,p1,p2 */
+ 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)
+ {
+ uxResult= 0u;
+ }
+ else
+ {
+ /* Put in network byte order. */
+ *pulIPAddress =
+ ( ( uint32_t ) h1 << 24 ) |
+ ( ( uint32_t ) h2 << 16 ) |
+ ( ( uint32_t ) h3 << 8 ) |
+ ( ( uint32_t ) h4 );
+ uxResult = ( p1 << 8 ) | p2;
+ }
+ return uxResult;
+}
+/*-----------------------------------------------------------*/
+
+/*
+
+ #### ####### # ###
+# # # # ## # #
+# # # # # #
+# ###### #### ### ## #### # # ### # ####
+ ## # # # # # # # # ##### # # # #
+ ## # # # ## # ###### # # # # ######
+# # # # # # # # # # #
+# # # ## # # # # ## # # # # ##
+ #### ## #### #### #### #### ##### ##### ####
+
+*/
+
+static BaseType_t prvStoreFilePrep( FTPClient_t *pxClient, char *pcFileName )
+{
+BaseType_t xResult;
+FF_FILE *pxNewHandle;
+size_t uxFileSize = 0ul;
+int iErrorNo;
+
+ /* Close previous handle (if any) and reset file transfer parameters. */
+ prvTransferCloseFile( pxClient );
+
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
+
+ pxNewHandle = NULL;
+
+ if( pxClient->ulRestartOffset != 0 )
+ {
+ size_t uxOffset = pxClient->ulRestartOffset;
+ int32_t lRc;
+
+ pxClient->ulRestartOffset = 0ul; /* Only use 1 time. */
+ pxNewHandle = ff_fopen( pxClient->pcFileName, "ab" );
+
+ if( pxNewHandle != NULL )
+ {
+ uxFileSize = pxNewHandle->ulFileSize;
+
+ if( uxOffset <= uxFileSize )
+ {
+ lRc = ff_fseek( pxNewHandle, uxOffset, FF_SEEK_SET );
+ }
+ else
+ {
+ /* Won't even try to seek after EOF */
+ lRc = -pdFREERTOS_ERRNO_EINVAL;
+ }
+ if( lRc != 0 )
+ {
+ BaseType_t xLength;
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "450 Seek invalid %u length %u\r\n",
+ ( unsigned ) uxOffset, ( unsigned ) uxFileSize );
+
+ /* "Requested file action not taken". */
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+
+ FreeRTOS_printf( ( "ftp::storeFile: create %s: Seek %u length %u\n",
+ pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );
+
+ ff_fclose( pxNewHandle );
+ pxNewHandle = NULL;
+ }
+ }
+ }
+ else
+ {
+ pxNewHandle = ff_fopen( pxClient->pcFileName, "wb" );
+ }
+
+ if( pxNewHandle == NULL )
+ {
+ iErrorNo = stdioGET_ERRNO();
+ if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )
+ {
+ prvSendReply( pxClient->xSocket, REPL_552, 0 );
+ }
+ else
+ {
+ /* "Requested file action not taken". */
+ prvSendReply( pxClient->xSocket, REPL_450, 0 );
+ }
+ FreeRTOS_printf( ( "ftp::storeFile: create %s: %s (errno %d)\n",
+ pxClient->pcFileName,
+ ( const char* ) strerror( iErrorNo ), iErrorNo ) );
+
+ xResult = pdFALSE;
+ }
+ else
+ {
+ if( pxClient->bits1.bIsListen )
+ {
+ /* True if PASV is used. */
+ snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
+ "150 Accepted data connection from %%xip:%%u\r\n" );
+ prvTransferCheck( pxClient );
+ }
+ else
+ {
+ BaseType_t xLength;
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150 Opening BIN connection to store file\r\n" );
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ pxClient->pcConnectionAck[ 0 ] = '\0';
+ prvTransferStart( pxClient ); /* Now active connect. */
+ }
+
+ pxClient->pxWriteHandle = pxNewHandle;
+
+ /* To get some statistics about the performance. */
+ pxClient->xStartTime = xTaskGetTickCount( );
+
+ xResult = pdTRUE;
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+#if( ipconfigFTP_ZERO_COPY_ALIGNED_WRITES == 0 )
+
+ static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
+ {
+ BaseType_t xRc, xWritten;
+
+ /* Read from the data socket until all has been read or until a negative value
+ is returned. */
+ for( ; ; )
+ {
+ char *pcBuffer;
+
+ /* The "zero-copy" method: */
+ xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
+ 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
+ if( xRc <= 0 )
+ {
+ break;
+ }
+ pxClient->ulRecvBytes += xRc;
+ xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );
+ FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );
+ if( xWritten != xRc )
+ {
+ xRc = -1;
+ /* bHadError: a transfer got aborted because of an error. */
+ pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
+ break;
+ }
+ }
+ return xRc;
+ }
+
+#else /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES != 0 */
+
+ #if !defined( ipconfigFTP_PREFERRED_WRITE_SIZE )
+ /* If you store data on flash, it may be profitable to give 'ipconfigFTP_PREFERRED_WRITE_SIZE'
+ the same size as the size of the flash' erase blocks, e.g. 4KB */
+ #define ipconfigFTP_PREFERRED_WRITE_SIZE 512ul
+ #endif
+
+ static BaseType_t prvStoreFileWork( FTPClient_t *pxClient )
+ {
+ BaseType_t xRc, xWritten;
+
+ /* Read from the data socket until all has been read or until a negative
+ value is returned. */
+ for( ; ; )
+ {
+ char *pcBuffer;
+ UBaseType_t xStatus;
+
+ /* The "zero-copy" method: */
+ xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) &pcBuffer,
+ 0x20000u, FREERTOS_ZERO_COPY | FREERTOS_MSG_DONTWAIT );
+
+ if( xRc <= 0 )
+ {
+ /* There are no data or the connection is closed. */
+ break;
+ }
+ xStatus = FreeRTOS_connstatus( pxClient->xTransferSocket );
+ if( xStatus != eESTABLISHED )
+ {
+ /* The connection is not established (any more), therefore
+ accept any amount of bytes, probably the last few bytes. */
+ }
+ else
+ {
+ if( xRc >= ipconfigFTP_PREFERRED_WRITE_SIZE )
+ {
+ /* More than a sector to write, round down to a multiple of
+ PREFERRED_WRITE_SIZE bytes. */
+ xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;
+ }
+ else
+ {
+ const StreamBuffer_t *pxBuffer = FreeRTOS_get_rx_buf( pxClient->xTransferSocket );
+ size_t uxSpace = pxBuffer->LENGTH - pxBuffer->uxTail;
+
+ if( uxSpace >= ipconfigFTP_PREFERRED_WRITE_SIZE )
+ {
+ /* At this moment there are les than PREFERRED_WRITE_SIZE bytes in the RX
+ buffer, but there is space for more. Just return and
+ wait for more. */
+ xRc = 0;
+ }
+ else
+ {
+ /* Now reading beyond the end of the circular buffer,
+ use a normal read. */
+ pcBuffer = pcFILE_BUFFER;
+ xRc = FreeRTOS_recvcount( pxClient->xTransferSocket );
+ xRc = ( xRc / ipconfigFTP_PREFERRED_WRITE_SIZE ) * ipconfigFTP_PREFERRED_WRITE_SIZE;
+ if( xRc > 0 )
+ {
+ xRc = FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) pcBuffer,
+ sizeof( pcFILE_BUFFER ), FREERTOS_MSG_DONTWAIT );
+ }
+ }
+ }
+ }
+ if( xRc == 0 )
+ {
+ break;
+ }
+ pxClient->ulRecvBytes += xRc;
+
+ xWritten = ff_fwrite( pcBuffer, 1, xRc, pxClient->pxWriteHandle );
+ if( pcBuffer != pcFILE_BUFFER )
+ {
+ FreeRTOS_recv( pxClient->xTransferSocket, ( void * ) NULL, xRc, 0 );
+ }
+ if( xWritten != xRc )
+ {
+ xRc = -1;
+ /* bHadError: a transfer got aborted because of an error. */
+ pxClient->bits1.bHadError = pdTRUE_UNSIGNED;
+ break;
+ }
+ }
+ return xRc;
+ }
+
+#endif /* ipconfigFTP_ZERO_COPY_ALIGNED_WRITES */
+/*-----------------------------------------------------------*/
+
+/*
+###### # ####### # ###
+ # # # # # ## # #
+ # # # # # #
+ # # #### ###### ### ## ### #### # # #### # # ### # ####
+ ###### # # # # # # # # # # # # # ##### # # # #
+ # ## ###### # ## # # ###### # # ###### # # # # ######
+ # # # # # # # # # # # # # #
+ # # # ## # ## # # # ## # # # ## # # # # ##
+### ## #### ## #### ##### #### ## #### #### ##### ##### ####
+*/
+static BaseType_t prvRetrieveFilePrep( FTPClient_t *pxClient, char *pcFileName )
+{
+BaseType_t xResult = pdTRUE;
+size_t uxFileSize;
+
+ /* Close previous handle (if any) and reset file transfer parameters */
+ prvTransferCloseFile( pxClient );
+
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
+
+ pxClient->pxReadHandle = ff_fopen( pxClient->pcFileName, "rb" );
+ if( pxClient->pxReadHandle == NULL )
+ {
+ int iErrno = stdioGET_ERRNO();
+ /* "Requested file action not taken". */
+ prvSendReply( pxClient->xSocket, REPL_450, 0 );
+ FreeRTOS_printf( ("prvRetrieveFilePrep: open '%s': errno %d: %s\n",
+ pxClient->pcFileName, iErrno, ( const char * ) strerror( iErrno ) ) );
+ uxFileSize = 0ul;
+ xResult = pdFALSE;
+ }
+ else
+ {
+ uxFileSize = pxClient->pxReadHandle->ulFileSize;
+ pxClient->uxBytesLeft = uxFileSize;
+ if( pxClient->ulRestartOffset != 0ul )
+ {
+ size_t uxOffset = pxClient->ulRestartOffset;
+ int32_t iRc;
+
+ /* Only use 1 time. */
+ pxClient->ulRestartOffset = 0;
+
+ if( uxOffset < uxFileSize )
+ {
+ iRc = ff_fseek( pxClient->pxReadHandle, uxOffset, FF_SEEK_SET );
+ }
+ else
+ {
+ iRc = -pdFREERTOS_ERRNO_EINVAL;
+ }
+ if( iRc != 0 )
+ {
+ BaseType_t xLength;
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "450 Seek invalid %u length %u\r\n", ( unsigned ) uxOffset, ( unsigned ) uxFileSize );
+
+ /* "Requested file action not taken". */
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+
+ FreeRTOS_printf( ( "prvRetrieveFilePrep: create %s: Seek %u length %u\n",
+ pxClient->pcFileName, ( unsigned ) uxOffset, ( unsigned ) uxFileSize ) );
+
+ ff_fclose( pxClient->pxReadHandle );
+ pxClient->pxReadHandle = NULL;
+ xResult = pdFALSE;
+ }
+ else
+ {
+ pxClient->uxBytesLeft = uxFileSize - pxClient->ulRestartOffset;
+ }
+ }
+ }
+ if( xResult != pdFALSE )
+ {
+ if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
+ {
+ /* True if PASV is used. */
+ snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
+ "150%cAccepted data connection from %%xip:%%u\r\n%s",
+ pxClient->xTransType == TMODE_ASCII ? '-' : ' ',
+ pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );
+ } else {
+ BaseType_t xLength;
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "150%cOpening data connection to %lxip:%u\r\n%s",
+ pxClient->xTransType == TMODE_ASCII ? '-' : ' ',
+ pxClient->ulClientIP,
+ pxClient->usClientPort,
+ pxClient->xTransType == TMODE_ASCII ? "150 NOTE: ASCII mode requested, but binary mode used\r\n" : "" );
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ pxClient->pcConnectionAck[ 0 ] = '\0';
+ prvTransferStart( pxClient );
+ }
+
+ /* Prepare the ACK which will be sent when all data has been sent. */
+ snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ), "%s", REPL_226 );
+
+ /* To get some statistics about the performance. */
+ pxClient->xStartTime = xTaskGetTickCount( );
+ if( uxFileSize == 0ul )
+ {
+ FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
+ }
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvRetrieveFileWork( FTPClient_t *pxClient )
+{
+size_t uxSpace;
+size_t uxCount, uxItemsRead;
+BaseType_t xRc = 0;
+BaseType_t xSetEvent = pdFALSE;
+
+ do
+ {
+ #if( ipconfigFTP_TX_ZERO_COPY != 0 )
+ char *pcBuffer;
+ BaseType_t xBufferLength;
+ #endif /* ipconfigFTP_TX_ZERO_COPY */
+
+ /* Take the lesser of the two: tx_space (number of bytes that can be
+ queued for transmission) and uxBytesLeft (the number of bytes left to
+ read from the file) */
+ uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
+
+ if( uxSpace == 0 )
+ {
+ FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE | eSELECT_EXCEPT );
+ xRc = FreeRTOS_select( pxClient->pxParent->xSocketSet, 200 );
+ uxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
+ }
+
+ uxCount = FreeRTOS_min_uint32( pxClient->uxBytesLeft, uxSpace );
+
+ if( uxCount == 0 )
+ {
+ break;
+ }
+
+ #if( ipconfigFTP_TX_ZERO_COPY == 0 )
+ {
+ if( uxCount > sizeof( pcFILE_BUFFER ) )
+ {
+ uxCount = sizeof( pcFILE_BUFFER );
+ }
+ uxItemsRead = ff_fread( pcFILE_BUFFER, 1, uxCount, pxClient->pxReadHandle );
+ if( uxItemsRead != uxCount )
+ {
+ FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned ) uxCount ) );
+ xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
+ pxClient->uxBytesLeft = 0u;
+ break;
+ }
+ pxClient->uxBytesLeft -= uxCount;
+
+ if( pxClient->uxBytesLeft == 0u )
+ {
+ BaseType_t xTrueValue = 1;
+
+ FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
+ }
+
+ xRc = FreeRTOS_send( pxClient->xTransferSocket, pcFILE_BUFFER, uxCount, 0 );
+ }
+ #else /* ipconfigFTP_TX_ZERO_COPY != 0 */
+ {
+ /* Use zero-copy transmission:
+ FreeRTOS_get_tx_head() returns a direct pointer to the TX stream and
+ set xBufferLength to know how much space there is left. */
+ pcBuffer = ( char * )FreeRTOS_get_tx_head( pxClient->xTransferSocket, &xBufferLength );
+ if( ( pcBuffer != NULL ) && ( xBufferLength >= 512 ) )
+ {
+ /* Will read disk data directly to the TX stream of the socket. */
+ uxCount = FreeRTOS_min_uint32( uxCount, ( uint32_t )xBufferLength );
+ if( uxCount > ( size_t ) 0x40000u )
+ {
+ uxCount = ( size_t ) 0x40000u;
+ }
+ }
+ else
+ {
+ /* Use the normal file i/o buffer. */
+ pcBuffer = pcFILE_BUFFER;
+ if( uxCount > sizeof( pcFILE_BUFFER ) )
+ {
+ uxCount = sizeof( pcFILE_BUFFER );
+ }
+ }
+
+ if ( pxClient->uxBytesLeft >= 1024u )
+ {
+ uxCount &= ~( ( size_t ) 512u - 1u );
+ }
+
+ if( uxCount <= 0u )
+ {
+ /* Nothing to send after rounding down to a multiple of a sector size. */
+ break;
+ }
+
+ uxItemsRead = ff_fread( pcBuffer, 1, uxCount, pxClient->pxReadHandle );
+
+ if( uxCount != uxItemsRead )
+ {
+ FreeRTOS_printf( ( "prvRetrieveFileWork: Got %u Expected %u\n", ( unsigned )uxItemsRead, ( unsigned )uxCount ) );
+ xRc = FreeRTOS_shutdown( pxClient->xTransferSocket, FREERTOS_SHUT_RDWR );
+ pxClient->uxBytesLeft = 0u;
+ break;
+ }
+ pxClient->uxBytesLeft -= uxCount;
+
+ if( pxClient->uxBytesLeft == 0u )
+ {
+ BaseType_t xTrueValue = 1;
+
+ FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
+ }
+ if( pcBuffer != pcFILE_BUFFER )
+ {
+ pcBuffer = NULL;
+ }
+ xRc = FreeRTOS_send( pxClient->xTransferSocket, pcBuffer, uxCount, 0 );
+ }
+ #endif /* ipconfigFTP_TX_ZERO_COPY */
+
+ if( xRc < 0 )
+ {
+ break;
+ }
+
+ pxClient->ulRecvBytes += xRc;
+ if( pxClient->uxBytesLeft == 0u )
+ {
+ break;
+ }
+ } while( uxCount > 0u );
+
+ if( xRc < 0 )
+ {
+ FreeRTOS_printf( ( "prvRetrieveFileWork: already disconnected\n" ) );
+ }
+ else if( pxClient->uxBytesLeft <= 0u )
+ {
+ BaseType_t x;
+
+ for( x = 0; x < 5; x++ )
+ {
+ xRc = FreeRTOS_recv( pxClient->xTransferSocket, pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), 0 );
+ if( xRc < 0 )
+ {
+ break;
+ }
+ }
+// FreeRTOS_printf( ( "prvRetrieveFileWork: %s all sent: xRc %ld\n", pxClient->pcFileName, xRc ) );
+ }
+ else
+ {
+ FreeRTOS_FD_SET( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
+ xSetEvent = pdTRUE;
+ }
+ if( xSetEvent == pdFALSE )
+ {
+ FreeRTOS_FD_CLR( pxClient->xTransferSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
+ }
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+/*
+### ##### #### #####
+ # # # # # # #
+ # # # # #
+ # # # #
+ # # ## #
+ # # # ## #
+ # # # # # #
+ # # # # # #
+####### ##### #### ####
+*/
+/* Prepare sending a directory LIST */
+static BaseType_t prvListSendPrep( FTPClient_t *pxClient )
+{
+BaseType_t xFindResult;
+int iErrorNo;
+
+ if( pxClient->bits1.bIsListen != pdFALSE_UNSIGNED )
+ {
+ /* True if PASV is used */
+ snprintf( pxClient->pcConnectionAck, sizeof( pxClient->pcConnectionAck ),
+ "150 Accepted data connection from %%xip:%%u\r\n" );
+ }
+ else
+ {
+ BaseType_t xLength;
+
+ /* Here the FTP server is supposed to connect() */
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "150 Opening ASCII mode data connection to for /bin/ls \r\n" );
+
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ /* Clear the current connection acknowledge message */
+ pxClient->pcConnectionAck[ 0 ] = '\0';
+ prvTransferStart( pxClient );
+ }
+
+ pxClient->xDirCount = 0;
+ xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pxClient->pcCurrentDir );
+
+ xFindResult = ff_findfirst( pcNEW_DIR, &pxClient->xFindData );
+
+ pxClient->bits1.bDirHasEntry = ( xFindResult >= 0 );
+
+ iErrorNo = stdioGET_ERRNO();
+ if( ( xFindResult < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE ) )
+ {
+ FreeRTOS_printf( ("prvListSendPrep: Empty directory? (%s)\n", pxClient->pcCurrentDir ) );
+ prvSendReply( pxClient->xTransferSocket, "total 0\r\n", 0 );
+ pxClient->xDirCount++;
+ }
+ else if( xFindResult < 0 )
+ {
+ FreeRTOS_printf( ( "prvListSendPrep: rc = %ld iErrorNo = %d\n", xFindResult, iErrorNo ) );
+ prvSendReply( pxClient->xSocket, REPL_451, 0 );
+ }
+ pxClient->pcClientAck[ 0 ] = '\0';
+
+ return pxClient->xDirCount;
+}
+/*-----------------------------------------------------------*/
+
+#define MAX_DIR_LIST_ENTRY_SIZE 256
+
+static BaseType_t prvListSendWork( FTPClient_t *pxClient )
+{
+BaseType_t xTxSpace;
+
+ while( pxClient->bits1.bClientConnected != pdFALSE_UNSIGNED )
+ {
+ char *pcWritePtr = pcCOMMAND_BUFFER;
+ BaseType_t xWriteLength;
+
+ xTxSpace = FreeRTOS_tx_space( pxClient->xTransferSocket );
+
+ if( xTxSpace > ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
+ {
+ xTxSpace = sizeof( pcCOMMAND_BUFFER );
+ }
+
+ while( ( xTxSpace >= MAX_DIR_LIST_ENTRY_SIZE ) && ( pxClient->bits1.bDirHasEntry != pdFALSE_UNSIGNED ) )
+ {
+ BaseType_t xLength, xEndOfDir;
+ int32_t iRc;
+ int iErrorNo;
+
+ xLength = prvGetFileInfoStat( &( pxClient->xFindData.xDirectoryEntry ), pcWritePtr, xTxSpace );
+
+ pxClient->xDirCount++;
+ pcWritePtr += xLength;
+ xTxSpace -= xLength;
+
+ iRc = ff_findnext( &pxClient->xFindData );
+ iErrorNo = stdioGET_ERRNO();
+
+ xEndOfDir = ( iRc < 0 ) && ( iErrorNo == pdFREERTOS_ERRNO_ENMFILE );
+
+ pxClient->bits1.bDirHasEntry = ( xEndOfDir == pdFALSE ) && ( iRc >= 0 );
+
+ if( ( iRc < 0 ) && ( xEndOfDir == pdFALSE ) )
+ {
+ FreeRTOS_printf( ("prvListSendWork: %s (rc %08x)\n",
+ ( const char * ) strerror( iErrorNo ),
+ ( unsigned )iRc ) );
+ }
+ }
+ xWriteLength = ( BaseType_t ) ( pcWritePtr - pcCOMMAND_BUFFER );
+
+ if( xWriteLength == 0 )
+ {
+ break;
+ }
+
+ if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
+ {
+ uint32_t ulTotalCount;
+ uint32_t ulFreeCount;
+ uint32_t ulPercentage;
+
+ ulTotalCount = 1;
+ ulFreeCount = ff_diskfree( pxClient->pcCurrentDir, &ulTotalCount );
+ ulPercentage = ( uint32_t ) ( ( 100ULL * ulFreeCount + ulTotalCount / 2 ) / ulTotalCount );
+
+ /* Prepare the ACK which will be sent when all data has been sent. */
+ snprintf( pxClient->pcClientAck, sizeof( pxClient->pcClientAck ),
+ "226-Options: -l\r\n"
+ "226-%ld matches total\r\n"
+ "226 Total %lu KB (%lu %% free)\r\n",
+ pxClient->xDirCount, ulTotalCount /1024, ulPercentage );
+ }
+
+ if( xWriteLength )
+ {
+ if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
+ {
+ BaseType_t xTrueValue = 1;
+
+ FreeRTOS_setsockopt( pxClient->xTransferSocket, 0, FREERTOS_SO_CLOSE_AFTER_SEND, ( void * ) &xTrueValue, sizeof( xTrueValue ) );
+ }
+
+ prvSendReply( pxClient->xTransferSocket, pcCOMMAND_BUFFER, xWriteLength );
+ }
+
+ if( pxClient->bits1.bDirHasEntry == pdFALSE_UNSIGNED )
+ {
+ prvSendReply( pxClient->xSocket, pxClient->pcClientAck, 0 );
+ break;
+ }
+
+ } /* while( pxClient->bits1.bClientConnected ) */
+
+ return 0;
+}
+/*-----------------------------------------------------------*/
+
+static const char *pcMonthAbbrev( BaseType_t xMonth )
+{
+static const char pcMonthList[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+ if( xMonth < 1 || xMonth > 12 )
+ xMonth = 12;
+
+ return pcMonthList + 3 * ( xMonth - 1 );
+};
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvGetFileInfoStat( FF_DirEnt_t *pxEntry, char *pcLine, BaseType_t xMaxLength )
+{
+ char date[ 16 ];
+ char mode[ 11 ] = "----------";
+ BaseType_t st_nlink = 1;
+ const char user[ 9 ] = "freertos";
+ const char group[ 8 ] = "plusfat";
+
+/*
+ * Creates a unix-style listing, understood by most FTP clients:
+ *
+ * -rw-rw-r-- 1 freertos FreeRTOS+FAT 10564588 Sep 01 00:17 03. Metaharmoniks - Star (Instrumental).mp3
+ * -rw-rw-r-- 1 freertos FreeRTOS+FAT 19087839 Sep 01 00:17 04. Simon Le Grec - Dimitri (Wherever U Are) (Cosmos Mix).mp3
+ * -rw-rw-r-- 1 freertos FreeRTOS+FAT 11100621 Sep 01 00:16 05. D-Chill - Mistake (feat. Katy Blue).mp3
+ */
+
+ #if ( ffconfigTIME_SUPPORT == 1 )
+ const FF_SystemTime_t *pxCreateTime = &( pxEntry->xCreateTime );
+ #else
+ #warning Do not use this.
+ FF_SystemTime_t xCreateTime;
+ const FF_SystemTime_t *pxCreateTime = &xCreateTime;
+ #endif
+ size_t ulSize = ( size_t )pxEntry->ulFileSize;
+ const char *pcFileName = pxEntry->pcFileName;
+
+ mode[ 0 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) != 0 ) ? 'd' : '-';
+ #if( ffconfigDEV_SUPPORT != 0 )
+ {
+ if( ( pxEntry->ucAttrib & FF_FAT_ATTR_DIR ) == 0 )
+ {
+ switch( pxEntry->ucIsDeviceDir )
+ {
+ case FF_DEV_CHAR_DEV:
+ mode[ 0 ] = 'c';
+ break;
+ case FF_DEV_BLOCK_DEV:
+ mode[ 0 ] = 'b';
+ break;
+ }
+ }
+ }
+ #endif /* ffconfigDEV_SUPPORT != 0 */
+
+ mode[ 1 ] = 'r'; /* Owner. */
+ mode[ 2 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';
+ mode[ 3 ] = '-'; /* x for executable. */
+
+ mode[ 4 ] = 'r'; /* group. */
+ mode[ 5 ] = ( ( pxEntry->ucAttrib & FF_FAT_ATTR_READONLY ) != 0 ) ? '-' : 'w';
+ mode[ 6 ] = '-'; /* x for executable. */
+
+ mode[ 7 ] = 'r'; /* world. */
+ mode[ 8 ] = '-';
+ mode[ 9 ] = '-'; /* x for executable. */
+
+ if( pxCreateTime->Month && pxCreateTime->Day )
+ {
+ snprintf( date, sizeof( date ), "%-3.3s %02d %02d:%02d",
+ pcMonthAbbrev( pxCreateTime->Month ),
+ pxCreateTime->Day,
+ pxCreateTime->Hour,
+ pxCreateTime->Minute );
+ }
+ else
+ {
+ snprintf (date, sizeof( date ), "Jan 01 1970");
+ }
+ return snprintf( pcLine, xMaxLength, "%s %3ld %-4s %-4s %8d %12s %s\r\n",
+ mode, st_nlink, user, group, ( int ) ulSize, date, pcFileName );
+}
+/*-----------------------------------------------------------*/
+
+/*
+ #### # # #####
+ # # # # # #
+# # # # # #
+# # # # # #
+# # # # # #
+# # # # # #
+# # ## ## # #
+ # # ## ## # #
+ #### ## ## #####
+*/
+static BaseType_t prvChangeDir( FTPClient_t *pxClient, char *pcDirectory )
+{
+BaseType_t xResult;
+BaseType_t xIsRootDir, xLength, xValid;
+BaseType_t xIsDotDir = 0;
+
+ if( pcDirectory[ 0 ] == '.' )
+ {
+ if( ( pcDirectory[ 1 ] == '.' ) &&
+ ( pcDirectory[ 2 ] == '\0' ) )
+ {
+ xIsDotDir = 2;
+ }
+ else if( pcDirectory[ 1 ] == '\0' )
+ {
+ xIsDotDir = 1;
+ }
+ }
+
+ if( xIsDotDir != 0 )
+ {
+ strcpy( pcFILE_BUFFER, pxClient->pcCurrentDir );
+
+ if( pcDirectory[ 1 ] == '.' )
+ {
+ char *p = strrchr( pcFILE_BUFFER, '/' );
+ if( p != NULL )
+ {
+ if( p == pcFILE_BUFFER )
+ {
+ p[ 1 ] = '\0';
+ }
+ else
+ {
+ p[ 0 ] = '\0';
+ }
+ }
+ }
+ }
+ else
+ {
+ if(pcDirectory[ 0 ] != '/' )
+ {
+ BaseType_t xCurLength;
+
+ xCurLength = strlen( pxClient->pcCurrentDir );
+ snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s%s%s",
+ pxClient->pcCurrentDir,
+ pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",
+ pcDirectory );
+ }
+ else
+ {
+ snprintf( pcFILE_BUFFER, sizeof( pcFILE_BUFFER ), "%s", pcDirectory );
+ }
+ }
+
+ xIsRootDir = ( pcFILE_BUFFER[ 0 ] == '/' ) && ( pcFILE_BUFFER[ 1 ] == '\0' );
+ xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFILE_BUFFER );
+
+ if( ( ( xIsRootDir == pdFALSE ) || ( FF_FS_Count() == 0 ) ) && ( ff_finddir( pcNEW_DIR ) == pdFALSE ) )
+ {
+ xValid = pdFALSE;
+ }
+ else
+ {
+ xValid = pdTRUE;
+ }
+
+ if( xValid == pdFALSE )
+ {
+ /* Get the directory cluster, if it exists. */
+ FreeRTOS_printf( ("FTP: chdir \"%s\": No such dir\n", pcNEW_DIR ) );
+ //#define REPL_550 "550 Requested action not taken.\r\n"
+ //550 /home/hein/arch/h8300: No such file or directory
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "550 %s: No such file or directory\r\n",
+ pcNEW_DIR );
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ xResult = pdFALSE;
+ }
+ else
+ {
+ memcpy( pxClient->pcCurrentDir, pcNEW_DIR, sizeof( pxClient->pcCurrentDir ) );
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "250 Changed to %s\r\n", pcNEW_DIR );
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ xResult = pdTRUE;
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+/*
+###### ## # ####### ######
+ # # ## # # ## # #
+ # # ## # # # # #
+ # # ### # # # # #
+ ###### # ## # ##### ######
+ # ## # ## # # # # ##
+ # # # ### # # #
+ # # # ## # # #
+### ## # ## #### ### ##
+*/
+static BaseType_t prvRenameFrom( FTPClient_t *pxClient, const char *pcFileName )
+{
+const char *myReply;
+FF_FILE *fh;
+
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
+
+ myReply = NULL;
+
+ fh = ff_fopen( pxClient->pcFileName, "rb" );
+
+ if( fh != NULL )
+ {
+ ff_fclose( fh );
+ /* REPL_350; "350 Requested file action pending further information." */
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "350 Rename '%s' ...\r\n", pxClient->pcFileName );
+ myReply = pcCOMMAND_BUFFER;
+ pxClient->bits.bInRename = pdTRUE_UNSIGNED;
+ }
+ else if( stdioGET_ERRNO() == pdFREERTOS_ERRNO_EISDIR )
+ {
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "350 Rename directory '%s' ...\r\n", pxClient->pcFileName );
+ myReply = pcCOMMAND_BUFFER;
+ pxClient->bits.bInRename = pdTRUE_UNSIGNED;
+ }
+ else
+ {
+ FreeRTOS_printf( ("ftp::renameFrom[%s]\n%s\n", pxClient->pcFileName, strerror( stdioGET_ERRNO() ) ) );
+ myReply = REPL_451; /* "451 Requested action aborted. Local error in processing." */
+ }
+ if( myReply )
+ {
+ prvSendReply( pxClient->xSocket, myReply, 0 );
+ }
+
+ return pdTRUE;
+}
+/*-----------------------------------------------------------*/
+
+/*
+###### ## # ##### ###
+ # # ## # # # # ## ##
+ # # ## # # ## ##
+ # # ### # # # #
+ ###### # ## # # # #
+ # ## # ## # # # #
+ # # # ### # ## ##
+ # # # ## # ## ##
+### ## # ## #### ###
+*/
+static BaseType_t prvRenameTo( FTPClient_t *pxClient, const char *pcFileName )
+{
+const char *myReply = NULL;
+int iResult;
+
+ xMakeAbsolute( pxClient, pcNEW_DIR, sizeof( pcNEW_DIR ), pcFileName );
+
+ /* FreeRTOS+FAT rename has an extra parameter: "remove target if already
+ exists". */
+ iResult = ff_rename( pxClient->pcFileName, pcNEW_DIR, pdFALSE );
+
+ if( iResult < 0 )
+ {
+ iResult = stdioGET_ERRNO();
+ }
+ else
+ {
+ iResult = 0;
+ }
+
+ switch( iResult )
+ {
+ case 0:
+ FreeRTOS_printf( ( "ftp::renameTo[%s,%s]: Ok\n", pxClient->pcFileName, pcNEW_DIR ) );
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "250 Rename successful to '%s'\r\n", pcNEW_DIR );
+ myReply = pcCOMMAND_BUFFER;
+ break;
+ case pdFREERTOS_ERRNO_EEXIST:
+ /* the destination file already exists.
+ "450 Requested file action not taken.\r\n"*/
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "450 Already exists '%s'\r\n", pcNEW_DIR );
+ myReply = pcCOMMAND_BUFFER;
+ break;
+ case pdFREERTOS_ERRNO_EIO: /* FF_ERR_FILE_COULD_NOT_CREATE_DIRENT */
+ /* if dirent creation failed (fatal error!).
+ "553 Requested action not taken.\r\n" */
+ FreeRTOS_printf( ("ftp::renameTo[%s,%s]: Error creating DirEnt\n",
+ pxClient->pcFileName, pcNEW_DIR ) );
+ myReply = REPL_553;
+ break;
+ case pdFREERTOS_ERRNO_ENXIO:
+ case pdFREERTOS_ERRNO_ENOENT:
+ /* if the source file was not found.
+ "450 Requested file action not taken.\r\n" */
+ snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "450 No such file '%s'\r\n", pxClient->pcFileName );
+ myReply = pcCOMMAND_BUFFER;
+ break;
+ default:
+ FreeRTOS_printf( ("ftp::renameTo[%s,%s]: %s\n", pxClient->pcFileName, pcNEW_DIR,
+ (const char*)strerror( stdioGET_ERRNO() ) ) );
+ myReply = REPL_451; /* "451 Requested action aborted. Local error in processing." */
+ break;
+ }
+ prvSendReply( pxClient->xSocket, myReply, 0 );
+
+ return pdTRUE;
+}
+/*-----------------------------------------------------------*/
+
+/*
+ #### #
+# # # #
+# # #
+# ### ###### ####
+ ## # # # #
+ ## # # ######
+# # # # #
+# # # # ## # ##
+ #### ##### ## ####
+*/
+static BaseType_t prvSiteCmd( FTPClient_t *pxClient, char *pcRestCommand )
+{
+ ( void ) pxClient;
+ ( void ) pcRestCommand;
+
+ return 0;
+}
+/*-----------------------------------------------------------*/
+
+/*
+##### ###
+ # # # #
+ # # # #
+ # # #### # #### ###### ####
+ # # # # # # # # # #
+ # # ###### # ###### # ######
+ # # # # # # #
+ # # # ## # # ## # ## # ##
+##### #### ##### #### ## ####
+*/
+static BaseType_t prvDeleteFile( FTPClient_t *pxClient, char *pcFileName )
+{
+BaseType_t xResult, xLength;
+int32_t iRc;
+int iErrorNo;
+
+ /* DELE: Delete a file. */
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
+
+ iRc = ff_remove( pxClient->pcFileName );
+
+ if (iRc >= 0 )
+ {
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "250 File \"%s\" removed\r\n", pxClient->pcFileName );
+ xResult = pdTRUE;
+ }
+ else
+ {
+ const char *errMsg = "other error";
+
+ iErrorNo = stdioGET_ERRNO();
+ switch( iErrorNo )
+ { /*_RB_ What do these negative numbers relate to? */
+ case pdFREERTOS_ERRNO_ENOENT: errMsg = "No such file"; break; /* -31 File was not found. */
+ case pdFREERTOS_ERRNO_EALREADY: errMsg = "File still open"; break; /* -30 File is in use. */
+ case pdFREERTOS_ERRNO_EISDIR: errMsg = "Is a dir"; break; /* -32 Tried to FF_Open() a Directory. */
+ case pdFREERTOS_ERRNO_EROFS: errMsg = "Read-only"; break; /* -33 Tried to FF_Open() a file marked read only. */
+ case pdFREERTOS_ERRNO_ENOTDIR: errMsg = "Invalid path"; break; /* -34 The path of the file was not found. */
+ }
+ FreeRTOS_printf( ( "ftp::delFile: '%s' because %s\n",
+ pxClient->pcFileName, strerror( iErrorNo ) ) );
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "521-\"%s\" %s;\r\n"
+ "521 taking no action\r\n",
+ pxClient->pcFileName, errMsg );
+
+ xResult = pdFALSE;
+ }
+
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+/*
+ #### # #####
+# # # # # #
+# # # # #
+# ### ###### #### # # #### ###### ####
+ ## # # # # # # # # # # #
+ ## # # ###### # # ##### # ######
+# # # # # # # # # # #
+# # # # # ## # # # # # ## # ##
+ #### ##### ###### #### ##### ### ## ## ####
+*/
+static BaseType_t prvSizeDateFile( FTPClient_t *pxClient, char *pcFileName, BaseType_t xSendDate )
+{
+BaseType_t xResult = pdFALSE;
+char *pcPtr;
+
+ /* SIZE: get the size of a file (xSendDate = 0)
+ MDTM: get data and time properties (xSendDate = 1) */
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcFileName );
+
+ pcPtr = strrchr( pxClient->pcFileName, '/' );
+
+ if( ( pcPtr != NULL ) && ( pcPtr[ 1 ] != '\0' ) )
+ {
+ FF_Stat_t xStatBuf;
+ int32_t iRc = ff_stat( pxClient->pcFileName, &xStatBuf );
+ if (iRc < 0 )
+ FreeRTOS_printf( ("In %s: %s\n", pxClient->pcFileName,
+ ( const char* )strerror( stdioGET_ERRNO() ) ) );
+
+ if( iRc == 0 )
+ {
+ BaseType_t xLength;
+ /* "YYYYMMDDhhmmss" */
+ if( xSendDate != pdFALSE )
+ {
+ #if( ffconfigTIME_SUPPORT != 0 )
+ {
+ FF_TimeStruct_t tmStruct;
+ time_t secs = xStatBuf.st_mtime;
+ FreeRTOS_gmtime_r( &secs, &tmStruct );
+
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %04u%02u%02u%02u%02u%02u\r\n",
+ tmStruct.tm_year + 1900,
+ tmStruct.tm_mon+1,
+ tmStruct.tm_mday,
+ tmStruct.tm_hour,
+ tmStruct.tm_min,
+ tmStruct.tm_sec );
+ }
+ #else
+ {
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 19700101000000\r\n",
+ }
+ #endif
+ }
+ else
+ {
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "213 %lu\r\n", xStatBuf.st_size );
+ }
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+ xResult = pdTRUE;
+ }
+ else
+ {
+ FreeRTOS_printf( ("ftp::sizeDateFile: No such file %s\n", pxClient->pcFileName ) );
+ }
+ } else {
+ FreeRTOS_printf( ("ftp::sizeDateFile: Invalid file name: %s ?\n", pxClient->pcFileName ) );
+ }
+ if( xResult == pdFALSE )
+ {
+ prvSendReply( pxClient->xSocket, REPL_450, 0 ); /* "Requested file action not taken". */
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+/*
+## ## ## ## ##### ###### ## ## #####
+### ### # # # # # # ### ### # #
+# ### # # # # # # # # ### # # #
+# # # # # # # # # # # # # #
+# # # #### # # ###### # # # # #
+# # # # # # # ## # # # #
+# # # # # # # # # # # #
+# # # # # # # # # # # #
+# # ### ## ##### ### ## # # #####
+*/
+static BaseType_t prvMakeRemoveDir( FTPClient_t *pxClient, const char *pcDirectory, BaseType_t xDoRemove )
+{
+BaseType_t xResult;
+BaseType_t xLength;
+int32_t iRc;
+int iErrorNo;
+
+ /* MKD: Make / create a directory (xDoRemove = 0)
+ RMD: Remove a directory (xDoRemove = 1) */
+ xMakeAbsolute( pxClient, pxClient->pcFileName, sizeof( pxClient->pcFileName ), pcDirectory );
+
+ if( xDoRemove )
+ {
+ iRc = ff_rmdir( pxClient->pcFileName );
+ }
+ else
+ {
+ #if( ffconfigMKDIR_RECURSIVE != 0 )
+ {
+ iRc = ff_mkdir( pxClient->pcFileName, pdFALSE );
+ }
+ #else
+ {
+ iRc = ff_mkdir( pxClient->pcFileName );
+ }
+ #endif /* ffconfigMKDIR_RECURSIVE */
+ }
+ xResult = pdTRUE;
+
+ if( iRc >= 0 )
+ {
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), "257 \"%s\" directory %s\r\n",
+ pxClient->pcFileName, xDoRemove ? "removed" : "created" );
+ }
+ else
+ {
+ const char *errMsg = "other error";
+ BaseType_t xFTPCode = 521;
+
+ xResult = pdFALSE;
+ iErrorNo = stdioGET_ERRNO();
+ switch( iErrorNo )
+ {
+ case pdFREERTOS_ERRNO_EEXIST: errMsg = "Directory already exists"; break;
+ 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? */
+ case pdFREERTOS_ERRNO_ENOTEMPTY:errMsg = "Dir not empty"; break;
+ case pdFREERTOS_ERRNO_EROFS: errMsg = "Read-only"; break; /* -33 Tried to FF_Open() a file marked read only. */
+ default: errMsg = strerror( iErrorNo ); break;
+ }
+ if( iErrorNo == pdFREERTOS_ERRNO_ENOSPC )
+ {
+ xFTPCode = 552;
+ }
+ xLength = snprintf( pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ),
+ "%ld-\"%s\" %s;\r\n"
+ "%ld taking no action\r\n",
+ xFTPCode, pxClient->pcFileName, errMsg, xFTPCode );
+ FreeRTOS_printf( ( "%sdir '%s': %s\n", xDoRemove ? "rm" : "mk", pxClient->pcFileName, errMsg ) );
+ }
+ prvSendReply( pxClient->xSocket, pcCOMMAND_BUFFER, xLength );
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+static portINLINE BaseType_t IsDigit( char cChar )
+{
+BaseType_t xResult;
+
+ if( cChar >= '0' && cChar <= '9' )
+ {
+ xResult = pdTRUE;
+ }
+ else
+ {
+ xResult = pdFALSE;
+ }
+ return xResult;
+}
+
+static BaseType_t prvSendReply( Socket_t xSocket, const char *pcBuffer, BaseType_t xLength )
+{
+BaseType_t xResult;
+
+ if( xLength == 0 )
+ {
+ xLength = strlen( pcBuffer );
+ }
+ xResult = FreeRTOS_send( xSocket, ( const void * )pcBuffer, ( size_t ) xLength, 0 );
+ if( IsDigit( ( int ) pcBuffer[ 0 ] ) &&
+ IsDigit( ( int ) pcBuffer[ 1 ] ) &&
+ IsDigit( ( int ) pcBuffer[ 2 ] ) &&
+ IsDigit( ( int ) pcBuffer[ 3 ] ) )
+ {
+ const char *last = pcBuffer + strlen( pcBuffer );
+ int iLength;
+ while( ( last > pcBuffer ) && ( ( last[ -1 ] == ftpASCII_CR ) || ( last[ -1 ] == ftpASCII_LF ) ) )
+ {
+ last--;
+ }
+ iLength = ( int )( last - pcBuffer );
+ FF_PRINTF( " %-*.*s", iLength, iLength, pcBuffer );
+ }
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+#if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )
+
+ /*
+ * The following function is called for every file received:
+ * void vApplicationFTPReceivedHook( pcFileName, ulSize, pxFTPClient );
+ * This callback function may do a callback to vFTPReplyMessage() to send messages
+ * to the FTP client like:
+ * 200-Please wait: Received new firmware
+ * 200-Please wait: Please wait a few seconds for reboot
+ */
+ void vFTPReplyMessage( struct xFTP_CLIENT *pxFTPClient, const char *pcMessage )
+ {
+ if( ( pxFTPClient != NULL ) && ( pxFTPClient->xSocket != NULL ) )
+ {
+ prvSendReply( pxFTPClient->xSocket, pcMessage, 0 );
+ }
+ }
+ /*-----------------------------------------------------------*/
+
+#endif /* ipconfigFTP_HAS_RECEIVED_HOOK != 0 */
+
+/*
+ * Some explanation:
+ * The FTP client may send: "DELE readme.txt"
+ * Here the complete path is constructed consisting of 3 parts:
+ *
+ * pxClient->pcRootDir + pxClient->pcCurrentDir + pcFileName
+ *
+ * 'pcCurrentDir' will not be applied for an absolute path like in "DELE /.htaccess"
+ */
+BaseType_t xMakeAbsolute( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )
+{
+BaseType_t xLength = strlen( pxClient->pcRootDir );
+
+ if( pcFileName[ 0 ] != '/' )
+ {
+ char *pcNewDirBuffer = pcNEW_DIR;
+ BaseType_t xCurLength;
+
+ xCurLength = strlen( pxClient->pcCurrentDir );
+ if( pcBuffer == pcNEW_DIR )
+ {
+ /* In one call, the result already goes into pcNEW_DIR.
+ Use pcFILE_BUFFER in that case */
+ pcNewDirBuffer = pcFILE_BUFFER;
+ }
+ snprintf( pcNewDirBuffer, sizeof( pcNEW_DIR ), "%s%s%s",
+ pxClient->pcCurrentDir,
+ pxClient->pcCurrentDir[ xCurLength - 1 ] == '/' ? "" : "/",
+ pcFileName );
+ pcFileName = pcNewDirBuffer;
+ }
+ if( strncasecmp( pxClient->pcRootDir, pcFileName, xLength ) == 0 )
+ {
+ xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );
+ }
+ else
+ {
+ xLength = snprintf( pcBuffer, xBufferLength, "%s/%s",
+ pxClient->pcRootDir,
+ pcFileName[ 0 ] == '/' ? ( pcFileName + 1 ) : pcFileName );
+ }
+
+ #if( ipconfigFTP_FS_USES_BACKSLAH == 1 )
+ for( pcPtr = pcBuffer; *pcPtr; pcPtr++ )
+ {
+ if( pcPtr[ 0 ] == '/' )
+ {
+ pcPtr[ 0 ] = '\\';
+ }
+ }
+ #endif
+
+ return xLength;
+}
+/*-----------------------------------------------------------*/
+
+BaseType_t xMakeRelative( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName )
+{
+BaseType_t xLength = strlen( pxClient->pcRootDir );
+
+ if( strncasecmp ( pxClient->pcRootDir, pcFileName, xLength ) == 0 )
+ {
+ xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName + xLength );
+ }
+ else
+ {
+ xLength = snprintf( pcBuffer, xBufferLength, "%s", pcFileName );
+ }
+
+ return xLength;
+}
+/*-----------------------------------------------------------*/
+
+#endif /* ipconfigUSE_FTP */
+
+
+
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_commands.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_commands.c
new file mode 100644
index 000000000..f8b5ac250
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_commands.c
@@ -0,0 +1,71 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+
+/* Standard includes. */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+
+#include "FreeRTOS_HTTP_commands.h"
+
+const struct xWEB_COMMAND xWebCommands[ WEB_CMD_COUNT ] =
+{
+ { 3, "GET", ECMD_GET },
+ { 4, "HEAD", ECMD_HEAD },
+ { 4, "POST", ECMD_POST },
+ { 3, "PUT", ECMD_PUT },
+ { 6, "DELETE", ECMD_DELETE },
+ { 5, "TRACE", ECMD_TRACE },
+ { 7, "OPTIONS", ECMD_OPTIONS },
+ { 7, "CONNECT", ECMD_CONNECT },
+ { 5, "PATCH", ECMD_PATCH },
+ { 4, "UNKN", ECMD_UNK },
+};
+
+const char *webCodename (int aCode)
+{
+ switch (aCode) {
+ case WEB_REPLY_OK: // = 200,
+ return "OK";
+ case WEB_NO_CONTENT: // 204
+ return "No content";
+ case WEB_BAD_REQUEST: // = 400,
+ return "Bad request";
+ case WEB_UNAUTHORIZED: // = 401,
+ return "Authorization Required";
+ case WEB_NOT_FOUND: // = 404,
+ return "Not Found";
+ case WEB_GONE: // = 410,
+ return "Done";
+ case WEB_PRECONDITION_FAILED: // = 412,
+ return "Precondition Failed";
+ case WEB_INTERNAL_SERVER_ERROR: // = 500,
+ return "Internal Server Error";
+ }
+ return "Unknown";
+}
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_server.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_server.c
new file mode 100644
index 000000000..ee69fd11e
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_server.c
@@ -0,0 +1,428 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+/* Standard includes. */
+#include <stdio.h>
+#include <stdlib.h>
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+#include "task.h"
+
+/* FreeRTOS+TCP includes. */
+#include "FreeRTOS_IP.h"
+#include "FreeRTOS_Sockets.h"
+
+/* FreeRTOS Protocol includes. */
+#include "FreeRTOS_HTTP_commands.h"
+#include "FreeRTOS_TCP_server.h"
+#include "FreeRTOS_server_private.h"
+
+/* Remove the whole file if HTTP is not supported. */
+#if( ipconfigUSE_HTTP == 1 )
+
+/* FreeRTOS+FAT includes. */
+#include "ff_stdio.h"
+
+#ifndef HTTP_SERVER_BACKLOG
+ #define HTTP_SERVER_BACKLOG ( 12 )
+#endif
+
+#ifndef USE_HTML_CHUNKS
+ #define USE_HTML_CHUNKS ( 0 )
+#endif
+
+#if !defined( ARRAY_SIZE )
+ #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
+#endif
+
+/* Some defines to make the code more readbale */
+#define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
+#define pcNEW_DIR pxClient->pxParent->pcNewDir
+#define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
+
+#ifndef ipconfigHTTP_REQUEST_CHARACTER
+ #define ipconfigHTTP_REQUEST_CHARACTER '?'
+#endif
+
+/*_RB_ Need comment block, although fairly self evident. */
+static void prvFileClose( HTTPClient_t *pxClient );
+static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );
+static const char *pcGetContentsType( const char *apFname );
+static BaseType_t prvOpenURL( HTTPClient_t *pxClient );
+static BaseType_t prvSendFile( HTTPClient_t *pxClient );
+static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );
+
+static const char pcEmptyString[1] = { '\0' };
+
+typedef struct xTYPE_COUPLE
+{
+ const char *pcExtension;
+ const char *pcType;
+} TypeCouple_t;
+
+static TypeCouple_t pxTypeCouples[ ] =
+{
+ { "html", "text/html" },
+ { "css", "text/css" },
+ { "js", "text/javascript" },
+ { "png", "image/png" },
+ { "jpg", "image/jpeg" },
+ { "gif", "image/gif" },
+ { "txt", "text/plain" },
+ { "mp3", "audio/mpeg3" },
+ { "wav", "audio/wav" },
+ { "flac", "audio/ogg" },
+ { "pdf", "application/pdf" },
+ { "ttf", "application/x-font-ttf" },
+ { "ttc", "application/x-font-ttf" }
+};
+
+void vHTTPClientDelete( TCPClient_t *pxTCPClient )
+{
+HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
+
+ /* This HTTP client stops, close / release all resources. */
+ if( pxClient->xSocket != FREERTOS_NO_SOCKET )
+ {
+ FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
+ FreeRTOS_closesocket( pxClient->xSocket );
+ pxClient->xSocket = FREERTOS_NO_SOCKET;
+ }
+ prvFileClose( pxClient );
+}
+/*-----------------------------------------------------------*/
+
+static void prvFileClose( HTTPClient_t *pxClient )
+{
+ if( pxClient->pxFileHandle != NULL )
+ {
+ FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );
+ ff_fclose( pxClient->pxFileHandle );
+ pxClient->pxFileHandle = NULL;
+ }
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )
+{
+struct xTCP_SERVER *pxParent = pxClient->pxParent;
+BaseType_t xRc;
+
+ /* A normal command reply on the main socket (port 21). */
+ char *pcBuffer = pxParent->pcFileBuffer;
+
+ xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),
+ "HTTP/1.1 %d %s\r\n"
+#if USE_HTML_CHUNKS
+ "Transfer-Encoding: chunked\r\n"
+#endif
+ "Content-Type: %s\r\n"
+ "Connection: keep-alive\r\n"
+ "%s\r\n",
+ ( int ) xCode,
+ webCodename (xCode),
+ pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",
+ pxParent->pcExtraContents );
+
+ pxParent->pcContentsType[0] = '\0';
+ pxParent->pcExtraContents[0] = '\0';
+
+ xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );
+ pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
+
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvSendFile( HTTPClient_t *pxClient )
+{
+size_t uxSpace;
+size_t uxCount;
+BaseType_t xRc = 0;
+
+ if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )
+ {
+ pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
+
+ strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );
+ snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
+ "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );
+
+ /* "Requested file action OK". */
+ xRc = prvSendReply( pxClient, WEB_REPLY_OK );
+ }
+
+ if( xRc >= 0 ) do
+ {
+ uxSpace = FreeRTOS_tx_space( pxClient->xSocket );
+
+ if( pxClient->uxBytesLeft < uxSpace )
+ {
+ uxCount = pxClient->uxBytesLeft;
+ }
+ else
+ {
+ uxCount = uxSpace;
+ }
+
+ if( uxCount > 0u )
+ {
+ if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )
+ {
+ uxCount = sizeof( pxClient->pxParent->pcFileBuffer );
+ }
+ ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );
+ pxClient->uxBytesLeft -= uxCount;
+
+ xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );
+ if( xRc < 0 )
+ {
+ break;
+ }
+ }
+ } while( uxCount > 0u );
+
+ if( pxClient->uxBytesLeft == 0u )
+ {
+ /* Writing is ready, no need for further 'eSELECT_WRITE' events. */
+ FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
+ prvFileClose( pxClient );
+ }
+ else
+ {
+ /* Wake up the TCP task as soon as this socket may be written to. */
+ FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
+ }
+
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvOpenURL( HTTPClient_t *pxClient )
+{
+BaseType_t xRc;
+char pcSlash[ 2 ];
+
+ pxClient->bits.ulFlags = 0;
+
+ #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
+ {
+ if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )
+ {
+ size_t xResult;
+
+ xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );
+ if( xResult > 0 )
+ {
+ strcpy( pxClient->pxParent->pcContentsType, "text/html" );
+ snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
+ "Content-Length: %d\r\n", ( int ) xResult );
+ xRc = prvSendReply( pxClient, WEB_REPLY_OK ); /* "Requested file action OK" */
+ if( xRc > 0 )
+ {
+ xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );
+ }
+ /* Although against the coding standard of FreeRTOS, a return is
+ done here to simplify this conditional code. */
+ return xRc;
+ }
+ }
+ }
+ #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
+
+ if( pxClient->pcUrlData[ 0 ] != '/' )
+ {
+ /* Insert a slash before the file name. */
+ pcSlash[ 0 ] = '/';
+ pcSlash[ 1 ] = '\0';
+ }
+ else
+ {
+ /* The browser provided a starting '/' already. */
+ pcSlash[ 0 ] = '\0';
+ }
+ snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",
+ pxClient->pcRootDir,
+ pcSlash,
+ pxClient->pcUrlData);
+
+ pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );
+
+ FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,
+ pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );
+
+ if( pxClient->pxFileHandle == NULL )
+ {
+ /* "404 File not found". */
+ xRc = prvSendReply( pxClient, WEB_NOT_FOUND );
+ }
+ else
+ {
+ pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;
+ xRc = prvSendFile( pxClient );
+ }
+
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )
+{
+BaseType_t xResult = 0;
+
+ /* A new command has been received. Process it. */
+ switch( xIndex )
+ {
+ case ECMD_GET:
+ xResult = prvOpenURL( pxClient );
+ break;
+
+ case ECMD_HEAD:
+ case ECMD_POST:
+ case ECMD_PUT:
+ case ECMD_DELETE:
+ case ECMD_TRACE:
+ case ECMD_OPTIONS:
+ case ECMD_CONNECT:
+ case ECMD_PATCH:
+ case ECMD_UNK:
+ {
+ FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",
+ xWebCommands[xIndex].pcCommandName ) );
+ }
+ break;
+ }
+
+ return xResult;
+}
+/*-----------------------------------------------------------*/
+
+BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )
+{
+BaseType_t xRc;
+HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
+
+ if( pxClient->pxFileHandle != NULL )
+ {
+ prvSendFile( pxClient );
+ }
+
+ xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
+
+ if( xRc > 0 )
+ {
+ BaseType_t xIndex;
+ const char *pcEndOfCmd;
+ const struct xWEB_COMMAND *curCmd;
+ char *pcBuffer = pcCOMMAND_BUFFER;
+
+ if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
+ {
+ pcBuffer[ xRc ] = '\0';
+ }
+ while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )
+ {
+ pcBuffer[ --xRc ] = '\0';
+ }
+ pcEndOfCmd = pcBuffer + xRc;
+
+ curCmd = xWebCommands;
+
+ /* Pointing to "/index.html HTTP/1.1". */
+ pxClient->pcUrlData = pcBuffer;
+
+ /* Pointing to "HTTP/1.1". */
+ pxClient->pcRestData = pcEmptyString;
+
+ /* Last entry is "ECMD_UNK". */
+ for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )
+ {
+ BaseType_t xLength;
+
+ xLength = curCmd->xCommandLength;
+ if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )
+ {
+ char *pcLastPtr;
+
+ pxClient->pcUrlData += xLength + 1;
+ for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )
+ {
+ char ch = *pcLastPtr;
+ if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )
+ {
+ *pcLastPtr = '\0';
+ pxClient->pcRestData = pcLastPtr + 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if( xIndex < ( WEB_CMD_COUNT - 1 ) )
+ {
+ xRc = prvProcessCmd( pxClient, xIndex );
+ }
+ }
+ else if( xRc < 0 )
+ {
+ /* The connection will be closed and the client will be deleted. */
+ FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );
+ }
+ return xRc;
+}
+/*-----------------------------------------------------------*/
+
+static const char *pcGetContentsType (const char *apFname)
+{
+ const char *slash = NULL;
+ const char *dot = NULL;
+ const char *ptr;
+ const char *pcResult = "text/html";
+ BaseType_t x;
+
+ for( ptr = apFname; *ptr; ptr++ )
+ {
+ if (*ptr == '.') dot = ptr;
+ if (*ptr == '/') slash = ptr;
+ }
+ if( dot > slash )
+ {
+ dot++;
+ for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )
+ {
+ if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )
+ {
+ pcResult = pxTypeCouples[ x ].pcType;
+ break;
+ }
+ }
+ }
+ return pcResult;
+}
+
+#endif /* ipconfigUSE_HTTP */
+
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/NTP/NTPDemo.c b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/NTP/NTPDemo.c
new file mode 100644
index 000000000..7795c41da
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/NTP/NTPDemo.c
@@ -0,0 +1,440 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+/*
+ * NTPDemo.c
+ *
+ * An example of how to lookup a domain using DNS
+ * And also how to send and receive UDP messages to get the NTP time
+ *
+ */
+
+/* Standard includes. */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+#include "task.h"
+#include "semphr.h"
+
+/* FreeRTOS+TCP includes. */
+#include "FreeRTOS_IP.h"
+#include "FreeRTOS_Sockets.h"
+#include "FreeRTOS_DNS.h"
+#include "FreeRTOS_Stream_Buffer.h"
+
+/* Use the date & time functions from +FAT. */
+#include "ff_time.h"
+
+#include "NTPDemo.h"
+#include "ntpClient.h"
+
+#include "date_and_time.h"
+
+enum EStatus {
+ EStatusLookup,
+ EStatusAsking,
+ EStatusPause,
+ EStatusFailed,
+};
+
+static struct SNtpPacket xNTPPacket;
+
+#if( ipconfigUSE_CALLBACKS == 0 )
+ static char cRecvBuffer[ sizeof( struct SNtpPacket ) + 64 ];
+#endif
+
+static enum EStatus xStatus = EStatusLookup;
+
+static const char *pcTimeServers[] = {
+ "0.asia.pool.ntp.org",
+ "0.europe.pool.ntp.org",
+ "0.id.pool.ntp.org",
+ "0.south-america.pool.ntp.org",
+ "0.oceania.pool.ntp.org",
+ "0.north-america.pool.ntp.org"
+};
+
+static SemaphoreHandle_t xNTPWakeupSem = NULL;
+static uint32_t ulIPAddressFound;
+static Socket_t xUDPSocket = NULL;
+static TaskHandle_t xNTPTaskhandle = NULL;
+static TickType_t uxSendTime;
+
+static void prvNTPTask( void *pvParameters );
+
+static void vSignalTask( void )
+{
+ #if( ipconfigUSE_CALLBACKS == 0 )
+ if( xUDPSocket != NULL )
+ {
+ /* Send a signal to the socket so that the
+ FreeRTOS_recvfrom will get interrupted. */
+ FreeRTOS_SignalSocket( xUDPSocket );
+ }
+ else
+ #endif
+ if( xNTPWakeupSem != NULL )
+ {
+ xSemaphoreGive( xNTPWakeupSem );
+ }
+}
+
+void vStartNTPTask( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority )
+{
+ /* The only public function in this module: start a task to contact
+ some NTP server. */
+
+ if( xNTPTaskhandle != NULL )
+ {
+ switch( xStatus )
+ {
+ case EStatusPause:
+ xStatus = EStatusAsking;
+ vSignalTask();
+ break;
+ case EStatusLookup:
+ FreeRTOS_printf( ( "NTP looking up server\n" ) );
+ break;
+ case EStatusAsking:
+ FreeRTOS_printf( ( "NTP still asking\n" ) );
+ break;
+ case EStatusFailed:
+ FreeRTOS_printf( ( "NTP failed somehow\n" ) );
+ ulIPAddressFound = 0ul;
+ xStatus = EStatusLookup;
+ vSignalTask();
+ break;
+ }
+ }
+ else
+ {
+ xUDPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
+ if( xUDPSocket != NULL )
+ {
+ struct freertos_sockaddr xAddress;
+ #if( ipconfigUSE_CALLBACKS != 0 )
+ BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 0 );
+ #else
+ BaseType_t xReceiveTimeOut = pdMS_TO_TICKS( 5000 );
+ #endif
+
+ xAddress.sin_addr = 0ul;
+ xAddress.sin_port = FreeRTOS_htons( NTP_PORT );
+
+ FreeRTOS_bind( xUDPSocket, &xAddress, sizeof( xAddress ) );
+ FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
+ xTaskCreate( prvNTPTask, /* The function that implements the task. */
+ ( const char * ) "NTP client", /* Just a text name for the task to aid debugging. */
+ usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */
+ NULL, /* The task parameter, not used in this case. */
+ uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
+ &xNTPTaskhandle ); /* The task handle. */
+ }
+ else
+ {
+ FreeRTOS_printf( ( "Creating socket failed\n" ) );
+ }
+ }
+}
+/*-----------------------------------------------------------*/
+
+static void vDNS_callback( const char *pcName, void *pvSearchID, uint32_t ulIPAddress )
+{
+char pcBuf[16];
+
+ /* The DNS lookup has a result, or it has reached the time-out. */
+ FreeRTOS_inet_ntoa( ulIPAddress, pcBuf );
+ FreeRTOS_printf( ( "IP address of %s found: %s\n", pcName, pcBuf ) );
+ if( ulIPAddressFound == 0ul )
+ {
+ ulIPAddressFound = ulIPAddress;
+ }
+ /* For testing: in case DNS doen't respond, still try some NTP server
+ with a known IP-address. */
+ if( ulIPAddressFound == 0ul )
+ {
+ ulIPAddressFound = FreeRTOS_inet_addr_quick( 184, 105, 182, 7 );
+/* ulIPAddressFound = FreeRTOS_inet_addr_quick( 103, 242, 70, 4 ); */
+ }
+ xStatus = EStatusAsking;
+
+ vSignalTask();
+}
+/*-----------------------------------------------------------*/
+
+static void prvSwapFields( struct SNtpPacket *pxPacket)
+{
+ /* NTP messages are big-endian */
+ pxPacket->rootDelay = FreeRTOS_htonl( pxPacket->rootDelay );
+ pxPacket->rootDispersion = FreeRTOS_htonl( pxPacket->rootDispersion );
+
+ pxPacket->referenceTimestamp.seconds = FreeRTOS_htonl( pxPacket->referenceTimestamp.seconds );
+ pxPacket->referenceTimestamp.fraction = FreeRTOS_htonl( pxPacket->referenceTimestamp.fraction );
+
+ pxPacket->originateTimestamp.seconds = FreeRTOS_htonl( pxPacket->originateTimestamp.seconds );
+ pxPacket->originateTimestamp.fraction = FreeRTOS_htonl( pxPacket->originateTimestamp.fraction );
+
+ pxPacket->receiveTimestamp.seconds = FreeRTOS_htonl( pxPacket->receiveTimestamp.seconds );
+ pxPacket->receiveTimestamp.fraction = FreeRTOS_htonl( pxPacket->receiveTimestamp.fraction );
+
+ pxPacket->transmitTimestamp.seconds = FreeRTOS_htonl( pxPacket->transmitTimestamp.seconds );
+ pxPacket->transmitTimestamp.fraction = FreeRTOS_htonl( pxPacket->transmitTimestamp.fraction );
+}
+/*-----------------------------------------------------------*/
+
+static void prvNTPPacketInit( )
+{
+ memset (&xNTPPacket, '\0', sizeof( xNTPPacket ) );
+
+ xNTPPacket.flags = 0xDB; /* value 0xDB : mode 3 (client), version 3, leap indicator unknown 3 */
+ xNTPPacket.poll = 10; /* 10 means 1 << 10 = 1024 seconds */
+ xNTPPacket.precision = 0xFA; /* = 250 = 0.015625 seconds */
+ xNTPPacket.rootDelay = 0x5D2E; /* 0x5D2E = 23854 or (23854/65535)= 0.3640 sec */
+ xNTPPacket.rootDispersion = 0x0008CAC8; /* 0x0008CAC8 = 8.7912 seconds */
+
+ /* use the recorded NTP time */
+ time_t uxSecs = FreeRTOS_time( NULL );/* apTime may be NULL, returns seconds */
+
+ xNTPPacket.referenceTimestamp.seconds = uxSecs; /* Current time */
+ xNTPPacket.transmitTimestamp.seconds = uxSecs + 3;
+
+ /* Transform the contents of the fields from native to big endian. */
+ prvSwapFields( &xNTPPacket );
+}
+/*-----------------------------------------------------------*/
+
+static void prvReadTime( struct SNtpPacket * pxPacket )
+{
+ FF_TimeStruct_t xTimeStruct;
+ time_t uxPreviousSeconds;
+ time_t uxPreviousMS;
+
+ time_t uxCurrentSeconds;
+ time_t uxCurrentMS;
+
+ const char *pcTimeUnit;
+ int32_t ilDiff;
+ TickType_t uxTravelTime;
+
+ uxTravelTime = xTaskGetTickCount() - uxSendTime;
+
+ /* Transform the contents of the fields from big to native endian. */
+ prvSwapFields( pxPacket );
+
+ uxCurrentSeconds = pxPacket->receiveTimestamp.seconds - TIME1970;
+ uxCurrentMS = pxPacket->receiveTimestamp.fraction / 4294967;
+ uxCurrentSeconds += uxCurrentMS / 1000;
+ uxCurrentMS = uxCurrentMS % 1000;
+
+ // Get the last time recorded
+ uxPreviousSeconds = FreeRTOS_get_secs_msec( &uxPreviousMS );
+
+ // Set the new time with precision in msec. */
+ FreeRTOS_set_secs_msec( &uxCurrentSeconds, &uxCurrentMS );
+
+ if( uxCurrentSeconds >= uxPreviousSeconds )
+ {
+ ilDiff = ( int32_t ) ( uxCurrentSeconds - uxPreviousSeconds );
+ }
+ else
+ {
+ ilDiff = 0 - ( int32_t ) ( uxPreviousSeconds - uxCurrentSeconds );
+ }
+
+ if( ( ilDiff < -5 ) || ( ilDiff > 5 ) )
+ {
+ /* More than 5 seconds difference. */
+ pcTimeUnit = "sec";
+ }
+ else
+ {
+ /* Less than or equal to 5 second difference. */
+ pcTimeUnit = "ms";
+ uint32_t ulLowest = ( uxCurrentSeconds <= uxPreviousSeconds ) ? uxCurrentSeconds : uxPreviousSeconds;
+ int32_t iCurMS = 1000 * ( uxCurrentSeconds - ulLowest ) + uxCurrentMS;
+ int32_t iPrevMS = 1000 * ( uxPreviousSeconds - ulLowest ) + uxPreviousMS;
+ ilDiff = iCurMS - iPrevMS;
+ }
+ uxCurrentSeconds -= iTimeZone;
+
+ FreeRTOS_gmtime_r( &uxCurrentSeconds, &xTimeStruct );
+
+ /*
+ 378.067 [NTP client] NTP time: 9/11/2015 16:11:19.559 Diff -20 ms (289 ms)
+ 379.441 [NTP client] NTP time: 9/11/2015 16:11:20.933 Diff 0 ms (263 ms)
+ */
+
+ FreeRTOS_printf( ("NTP time: %d/%d/%02d %2d:%02d:%02d.%03u Diff %d %s (%lu ms)\n",
+ xTimeStruct.tm_mday,
+ xTimeStruct.tm_mon + 1,
+ xTimeStruct.tm_year + 1900,
+ xTimeStruct.tm_hour,
+ xTimeStruct.tm_min,
+ xTimeStruct.tm_sec,
+ ( unsigned )uxCurrentMS,
+ ( unsigned )ilDiff,
+ pcTimeUnit,
+ uxTravelTime ) );
+
+ /* Remove compiler warnings in case FreeRTOS_printf() is not used. */
+ ( void ) pcTimeUnit;
+ ( void ) uxTravelTime;
+}
+/*-----------------------------------------------------------*/
+
+#if( ipconfigUSE_CALLBACKS != 0 )
+
+ static BaseType_t xOnUDPReceive( Socket_t xSocket, void * pvData, size_t xLength,
+ const struct freertos_sockaddr *pxFrom, const struct freertos_sockaddr *pxDest )
+ {
+ if( xLength >= sizeof( xNTPPacket ) )
+ {
+ prvReadTime( ( struct SNtpPacket *)pvData );
+ if( xStatus != EStatusPause )
+ {
+ xStatus = EStatusPause;
+ }
+ }
+ vSignalTask();
+ /* Tell the driver not to store the RX data */
+ return 1;
+ }
+ /*-----------------------------------------------------------*/
+
+#endif /* ipconfigUSE_CALLBACKS != 0 */
+
+static void prvNTPTask( void *pvParameters )
+{
+BaseType_t xServerIndex = 3;
+struct freertos_sockaddr xAddress;
+#if( ipconfigUSE_CALLBACKS != 0 )
+ F_TCP_UDP_Handler_t xHandler;
+#endif /* ipconfigUSE_CALLBACKS != 0 */
+
+ xStatus = EStatusLookup;
+ #if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 ) || ( ipconfigUSE_CALLBACKS != 0 )
+ {
+ xNTPWakeupSem = xSemaphoreCreateBinary();
+ }
+ #endif
+
+ #if( ipconfigUSE_CALLBACKS != 0 )
+ {
+ memset( &xHandler, '\0', sizeof( xHandler ) );
+ xHandler.pxOnUDPReceive = xOnUDPReceive;
+ FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_UDP_RECV_HANDLER, ( void * ) &xHandler, sizeof( xHandler ) );
+ }
+ #endif
+ #if( ipconfigSOCKET_HAS_USER_SEMAPHORE != 0 )
+ {
+ FreeRTOS_setsockopt( xUDPSocket, 0, FREERTOS_SO_SET_SEMAPHORE, ( void * ) &xNTPWakeupSem, sizeof( xNTPWakeupSem ) );
+ }
+ #endif
+ for( ; ; )
+ {
+ switch( xStatus )
+ {
+ case EStatusLookup:
+ if( ( ulIPAddressFound == 0ul ) || ( ulIPAddressFound == ~0ul ) )
+ {
+ if( ++xServerIndex == sizeof( pcTimeServers ) / sizeof( pcTimeServers[ 0 ] ) )
+ {
+ xServerIndex = 0;
+ }
+ FreeRTOS_printf( ( "Looking up server '%s'\n", pcTimeServers[ xServerIndex ] ) );
+ FreeRTOS_gethostbyname_a( pcTimeServers[ xServerIndex ], vDNS_callback, (void *)NULL, 1200 );
+ }
+ else
+ {
+ xStatus = EStatusAsking;
+ }
+ break;
+
+ case EStatusAsking:
+ {
+ char pcBuf[16];
+
+ prvNTPPacketInit( );
+ xAddress.sin_addr = ulIPAddressFound;
+ xAddress.sin_port = FreeRTOS_htons( NTP_PORT );
+
+ FreeRTOS_inet_ntoa( xAddress.sin_addr, pcBuf );
+ FreeRTOS_printf( ( "Sending UDP message to %s:%u\n",
+ pcBuf,
+ FreeRTOS_ntohs( xAddress.sin_port ) ) );
+
+ uxSendTime = xTaskGetTickCount( );
+ FreeRTOS_sendto( xUDPSocket, ( void * )&xNTPPacket, sizeof( xNTPPacket ), 0, &xAddress, sizeof( xAddress ) );
+ }
+ break;
+
+ case EStatusPause:
+ break;
+
+ case EStatusFailed:
+ break;
+ }
+
+ #if( ipconfigUSE_CALLBACKS != 0 )
+ {
+ xSemaphoreTake( xNTPWakeupSem, 5000 );
+ }
+ #else
+ {
+ uint32_t xAddressSize;
+ BaseType_t xReturned;
+
+ xAddressSize = sizeof( xAddress );
+ xReturned = FreeRTOS_recvfrom( xUDPSocket, ( void * ) cRecvBuffer, sizeof( cRecvBuffer ), 0, &xAddress, &xAddressSize );
+ switch( xReturned )
+ {
+ case 0:
+ case -pdFREERTOS_ERRNO_EAGAIN:
+ case -pdFREERTOS_ERRNO_EINTR:
+ break;
+ default:
+ if( xReturned < sizeof( xNTPPacket ) )
+ {
+ FreeRTOS_printf( ( "FreeRTOS_recvfrom: returns %ld\n", xReturned ) );
+ }
+ else
+ {
+ prvReadTime( ( struct SNtpPacket *)cRecvBuffer );
+ if( xStatus != EStatusPause )
+ {
+ xStatus = EStatusPause;
+ }
+ }
+ break;
+ }
+ }
+ #endif
+ }
+}
+/*-----------------------------------------------------------*/
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_FTP_commands.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_FTP_commands.h
new file mode 100644
index 000000000..6ae2384d9
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_FTP_commands.h
@@ -0,0 +1,133 @@
+/*
+ * FreeRTOS+TCP V2.0.1
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+#ifndef __FTPCMD_H__
+
+#define __FTPCMD_H__
+
+#define REPL_110 "110 Restart marker reply.\r\n"
+#define REPL_120 "120 Try again in 2 minutes.\r\n"
+#define REPL_125 "125 Data connection already open; transfer starting.\r\n"
+#define REPL_150 "150 File status okay; about to open data connection.\r\n"
+#define REPL_200 "200 NOOP command successful.\r\n"
+#define REPL_200_PROGRESS "200 NOOP: data transfer in progress.\r\n"
+#define REPL_202 "202 Command not implemented, superfluous at this site.\r\n"
+#define REPL_211 "221 System status, or system help reply.\r\n"
+#define REPL_211_STATUS "221-status of %s.\r\n"
+#define REPL_211_END "221 End of status.\r\n"
+#define REPL_212 "212 Directory status.\r\n"
+#define REPL_213 "213 File status.\r\n"
+#define REPL_214 "214 Help message.\r\n"
+#define REPL_214_END "214 End Help message.\r\n"
+#define REPL_215 "215 %s system type.\r\n"
+#define REPL_220 "220 Service ready for new user.\r\n"
+#define REPL_221 "221 Service closing control connection.\r\n"
+#define REPL_225 "225 Data connection open; no transfer in progress.\r\n"
+#define REPL_226 "226 Closing data connection.\r\n"
+#define REPL_227 "227 Entering Passive Mode (%s,%s,%s,%s,%s,%s).\r\n"
+#define REPL_227_D "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u).\r\n"
+#define REPL_230 "230 User logged in, proceed.\r\n"
+#define REPL_250 "250 Requested file action okay, completed.\r\n"
+#define REPL_257 "257 %s created.\r\n"
+// #define REPL_257_PWD "257 \"%s\" is current working dir.\r\n"
+#define REPL_257_PWD "257 \"%s\"\r\n"
+#define REPL_331 "331 Only anonymous user is accepted.\r\n"
+#define REPL_331_ANON "331 Anonymous login okay\r\n"
+#define REPL_332 "332 Need account for login.\r\n"
+#define REPL_350 "350 Requested file action pending further information.\r\n"
+#define REPL_421 "421 Service not available, closing control connection.\r\n"
+#define REPL_425 "425 Can't open data connection.\r\n"
+#define REPL_426 "426 Connection closed; transfer aborted.\r\n"
+#define REPL_450 "450 Requested file action not taken.\r\n"
+#define REPL_451 "451 Requested action aborted. Local error in processing.\r\n"
+#define REPL_452 "452 Requested action not taken.\r\n"
+#define REPL_500 "500 Syntax error, command unrecognized.\r\n"
+#define REPL_501 "501 Syntax error in parameters or arguments.\r\n"
+#define REPL_502 "502 Command not implemented.\r\n"
+#define REPL_503 "503 Bad sequence of commands.\r\n"
+#define REPL_504 "504 Command not implemented for that parameter.\r\n"
+#define REPL_530 "530 Not logged in.\r\n"
+#define REPL_532 "532 Need account for storing files.\r\n"
+#define REPL_550 "550 Requested action not taken.\r\n"
+#define REPL_551 "551 Requested action aborted. Page type unknown.\r\n"
+#define REPL_552 "552 Requested file action aborted.\r\n"
+#define REPL_553 "553 Requested action not taken.\r\n"
+#define REPL_553_READ_ONLY "553 Read-only file-system.\r\n"
+
+enum EFTPCommand {
+ ECMD_USER,
+ ECMD_PASS,
+ ECMD_ACCT,
+ ECMD_CWD,
+ ECMD_CDUP,
+ ECMD_SMNT,
+ ECMD_QUIT,
+ ECMD_REIN,
+ ECMD_PORT,
+ ECMD_PASV,
+ ECMD_TYPE,
+ ECMD_STRU,
+ ECMD_MODE,
+ ECMD_RETR,
+ ECMD_STOR,
+ ECMD_STOU,
+ ECMD_APPE,
+ ECMD_ALLO,
+ ECMD_REST,
+ ECMD_RNFR,
+ ECMD_RNTO,
+ ECMD_ABOR,
+ ECMD_SIZE,
+ ECMD_MDTM,
+ ECMD_DELE,
+ ECMD_RMD,
+ ECMD_MKD,
+ ECMD_PWD,
+ ECMD_LIST,
+ ECMD_NLST,
+ ECMD_SITE,
+ ECMD_SYST,
+ ECMD_FEAT,
+ ECMD_STAT,
+ ECMD_HELP,
+ ECMD_NOOP,
+ ECMD_EMPTY,
+ ECMD_CLOSE,
+ ECMD_UNKNOWN,
+};
+
+typedef struct xFTP_COMMAND {
+ BaseType_t xCommandLength;
+ const char pcCommandName[7];
+ const unsigned char ucCommandType;
+ const unsigned char checkLogin;
+ const unsigned char checkNullArg;
+} FTPCommand_t;
+
+#define FTP_CMD_COUNT (ECMD_UNKNOWN+1)
+
+extern const FTPCommand_t xFTPCommands[ FTP_CMD_COUNT ];
+
+#endif // __FTPCMD_H__
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_HTTP_commands.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_HTTP_commands.h
new file mode 100644
index 000000000..75eaf5d9f
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_HTTP_commands.h
@@ -0,0 +1,67 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+#ifndef FREERTOS_HTTP_COMMANDS_H
+#define FREERTOS_HTTP_COMMANDS_H
+
+enum {
+ WEB_REPLY_OK = 200,
+ WEB_NO_CONTENT = 204,
+ WEB_BAD_REQUEST = 400,
+ WEB_UNAUTHORIZED = 401,
+ WEB_NOT_FOUND = 404,
+ WEB_GONE = 410,
+ WEB_PRECONDITION_FAILED = 412,
+ WEB_INTERNAL_SERVER_ERROR = 500,
+};
+
+enum EWebCommand {
+ ECMD_GET,
+ ECMD_HEAD,
+ ECMD_POST,
+ ECMD_PUT,
+ ECMD_DELETE,
+ ECMD_TRACE,
+ ECMD_OPTIONS,
+ ECMD_CONNECT,
+ ECMD_PATCH,
+ ECMD_UNK,
+};
+
+struct xWEB_COMMAND
+{
+ BaseType_t xCommandLength;
+ const char *pcCommandName;
+ const unsigned char ucCommandType;
+};
+
+#define WEB_CMD_COUNT (ECMD_UNK+1)
+
+extern const struct xWEB_COMMAND xWebCommands[WEB_CMD_COUNT];
+
+extern const char *webCodename (int aCode);
+
+#endif /* FREERTOS_HTTP_COMMANDS_H */
+
+
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_TCP_server.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_TCP_server.h
new file mode 100644
index 000000000..d8140ce82
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_TCP_server.h
@@ -0,0 +1,125 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+/*
+ Some code which is common to TCP servers like HTTP en FTP
+*/
+
+#ifndef FREERTOS_TCP_SERVER_H
+#define FREERTOS_TCP_SERVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef FTP_SERVER_USES_RELATIVE_DIRECTORY
+ #define FTP_SERVER_USES_RELATIVE_DIRECTORY 0
+#endif
+
+enum eSERVER_TYPE
+{
+ eSERVER_NONE,
+ eSERVER_HTTP,
+ eSERVER_FTP,
+};
+
+struct xFTP_CLIENT;
+
+#if( ipconfigFTP_HAS_RECEIVED_HOOK != 0 )
+ extern void vApplicationFTPReceivedHook( const char *pcFileName, uint32_t ulSize, struct xFTP_CLIENT *pxFTPClient );
+ extern void vFTPReplyMessage( struct xFTP_CLIENT *pxFTPClient, const char *pcMessage );
+#endif /* ipconfigFTP_HAS_RECEIVED_HOOK != 0 */
+
+#if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )
+ /*
+ * Function is called when a user name has been submitted.
+ * The function may return a string such as: "331 Please enter your password"
+ * or return NULL to use the default reply.
+ */
+ extern const char *pcApplicationFTPUserHook( const char *pcUserName );
+#endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */
+
+#if( ipconfigFTP_HAS_USER_PASSWORD_HOOK != 0 )
+ /*
+ * Function is called when a password was received.
+ * Return positive value to allow the user
+ */
+ extern BaseType_t xApplicationFTPPasswordHook( const char *pcUserName, const char *pcPassword );
+#endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */
+
+#if( ipconfigFTP_HAS_USER_PROPERTIES_HOOK != 0 )
+ /*
+ * The FTP server is asking for user-specific properties
+ */
+ typedef struct
+ {
+ uint16_t usPortNumber; /* For reference only. Host-endian. */
+ const char *pcRootDir;
+ BaseType_t xReadOnly;
+ }
+ FTPUserProperties_t;
+ extern void vApplicationFTPUserPropertiesHook( const char *pcUserName, FTPUserProperties_t *pxProperties );
+#endif /* ipconfigFTP_HAS_USER_PASSWORD_HOOK */
+
+#if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
+ /*
+ * A GET request is received containing a special character,
+ * usually a question mark.
+ * const char *pcURLData; // A request, e.g. "/request?limit=75"
+ * char *pcBuffer; // Here the answer can be written
+ * size_t uxBufferLength; // Size of the buffer
+ *
+ */
+ extern size_t uxApplicationHTTPHandleRequestHook( const char *pcURLData, char *pcBuffer, size_t uxBufferLength );
+#endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
+
+struct xSERVER_CONFIG
+{
+ enum eSERVER_TYPE eType; /* eSERVER_HTTP | eSERVER_FTP */
+ BaseType_t xPortNumber; /* e.g. 80, 8080, 21 */
+ BaseType_t xBackLog; /* e.g. 10, maximum number of connected TCP clients */
+ const char * const pcRootDir; /* Treat this directory as the root directory */
+};
+
+struct xTCP_SERVER;
+typedef struct xTCP_SERVER TCPServer_t;
+
+TCPServer_t *FreeRTOS_CreateTCPServer( const struct xSERVER_CONFIG *pxConfigs, BaseType_t xCount );
+void FreeRTOS_TCPServerWork( TCPServer_t *pxServer, TickType_t xBlockingTime );
+
+#if( ipconfigSUPPORT_SIGNALS != 0 )
+ /* FreeRTOS_TCPServerWork() calls select().
+ The two functions below provide a possibility to interrupt
+ the call to select(). After the interruption, resume
+ by calling FreeRTOS_TCPServerWork() again. */
+ BaseType_t FreeRTOS_TCPServerSignal( TCPServer_t *pxServer );
+ BaseType_t FreeRTOS_TCPServerSignalFromISR( TCPServer_t *pxServer, BaseType_t *pxHigherPriorityTaskWoken );
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* FREERTOS_TCP_SERVER_H */
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_server_private.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_server_private.h
new file mode 100644
index 000000000..73768040e
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/FreeRTOS_server_private.h
@@ -0,0 +1,185 @@
+/*
+ * FreeRTOS+TCP V2.0.3
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * http://aws.amazon.com/freertos
+ * http://www.FreeRTOS.org
+ */
+
+ /*
+ Some code which is common to TCP servers like HTTP and FTP
+*/
+
+#ifndef FREERTOS_SERVER_PRIVATE_H
+#define FREERTOS_SERVER_PRIVATE_H
+
+#define FREERTOS_NO_SOCKET NULL
+
+/* FreeRTOS+FAT */
+#include "ff_stdio.h"
+
+/* Each HTTP server has 1, at most 2 sockets */
+#define HTTP_SOCKET_COUNT 2
+
+/*
+ * ipconfigTCP_COMMAND_BUFFER_SIZE sets the size of:
+ * pcCommandBuffer': a buffer to receive and send TCP commands
+ *
+ * ipconfigTCP_FILE_BUFFER_SIZE sets the size of:
+ * pcFileBuffer' : a buffer to access the file system: read or write data.
+ *
+ * The buffers are both used for FTP as well as HTTP.
+ */
+
+#ifndef ipconfigTCP_COMMAND_BUFFER_SIZE
+ #define ipconfigTCP_COMMAND_BUFFER_SIZE ( 2048 )
+#endif
+
+#ifndef ipconfigTCP_FILE_BUFFER_SIZE
+ #define ipconfigTCP_FILE_BUFFER_SIZE ( 2048 )
+#endif
+
+struct xTCP_CLIENT;
+
+typedef BaseType_t ( * FTCPWorkFunction ) ( struct xTCP_CLIENT * /* pxClient */ );
+typedef void ( * FTCPDeleteFunction ) ( struct xTCP_CLIENT * /* pxClient */ );
+
+#define TCP_CLIENT_FIELDS \
+ enum eSERVER_TYPE eType; \
+ struct xTCP_SERVER *pxParent; \
+ Socket_t xSocket; \
+ const char *pcRootDir; \
+ FTCPWorkFunction fWorkFunction; \
+ FTCPDeleteFunction fDeleteFunction; \
+ struct xTCP_CLIENT *pxNextClient
+
+typedef struct xTCP_CLIENT
+{
+ /* This define contains fields which must come first within each of the client structs */
+ TCP_CLIENT_FIELDS;
+ /* --- Keep at the top --- */
+
+} TCPClient_t;
+
+struct xHTTP_CLIENT
+{
+ /* This define contains fields which must come first within each of the client structs */
+ TCP_CLIENT_FIELDS;
+ /* --- Keep at the top --- */
+
+ const char *pcUrlData;
+ const char *pcRestData;
+ char pcCurrentFilename[ ffconfigMAX_FILENAME ];
+ size_t uxBytesLeft;
+ FF_FILE *pxFileHandle;
+ union {
+ struct {
+ uint32_t
+ bReplySent : 1;
+ };
+ uint32_t ulFlags;
+ } bits;
+};
+
+typedef struct xHTTP_CLIENT HTTPClient_t;
+
+struct xFTP_CLIENT
+{
+ /* This define contains fields which must come first within each of the client structs */
+ TCP_CLIENT_FIELDS;
+ /* --- Keep at the top --- */
+
+ uint32_t ulRestartOffset;
+ uint32_t ulRecvBytes;
+ size_t uxBytesLeft; /* Bytes left to send */
+ uint32_t ulClientIP;
+ TickType_t xStartTime;
+ uint16_t usClientPort;
+ Socket_t xTransferSocket;
+ BaseType_t xTransType;
+ BaseType_t xDirCount;
+ FF_FindData_t xFindData;
+ FF_FILE *pxReadHandle;
+ FF_FILE *pxWriteHandle;
+ char pcCurrentDir[ ffconfigMAX_FILENAME ];
+ char pcFileName[ ffconfigMAX_FILENAME ];
+ char pcConnectionAck[ 128 ];
+ char pcClientAck[ 128 ];
+ union {
+ struct {
+ uint32_t
+ bHelloSent : 1,
+ bLoggedIn : 1,
+ bStatusUser : 1,
+ bInRename : 1,
+ bReadOnly : 1;
+ };
+ uint32_t ulFTPFlags;
+ } bits;
+ union {
+ struct {
+ uint32_t
+ bIsListen : 1, /* pdTRUE for passive data connections (using list()). */
+ bDirHasEntry : 1, /* pdTRUE if ff_findfirst() was successful. */
+ bClientConnected : 1, /* pdTRUE after connect() or accept() has succeeded. */
+ bEmptyFile : 1, /* pdTRUE if a connection-without-data was received. */
+ bHadError : 1; /* pdTRUE if a transfer got aborted because of an error. */
+ };
+ uint32_t ulConnFlags;
+ } bits1;
+};
+
+typedef struct xFTP_CLIENT FTPClient_t;
+
+BaseType_t xHTTPClientWork( TCPClient_t *pxClient );
+BaseType_t xFTPClientWork( TCPClient_t *pxClient );
+
+void vHTTPClientDelete( TCPClient_t *pxClient );
+void vFTPClientDelete( TCPClient_t *pxClient );
+
+BaseType_t xMakeAbsolute( struct xFTP_CLIENT *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName );
+BaseType_t xMakeRelative( FTPClient_t *pxClient, char *pcBuffer, BaseType_t xBufferLength, const char *pcFileName );
+
+struct xTCP_SERVER
+{
+ SocketSet_t xSocketSet;
+ /* A buffer to receive and send TCP commands, either HTTP of FTP. */
+ char pcCommandBuffer[ ipconfigTCP_COMMAND_BUFFER_SIZE ];
+ /* A buffer to access the file system: read or write data. */
+ char pcFileBuffer[ ipconfigTCP_FILE_BUFFER_SIZE ];
+
+ #if( ipconfigUSE_FTP != 0 )
+ char pcNewDir[ ffconfigMAX_FILENAME ];
+ #endif
+ #if( ipconfigUSE_HTTP != 0 )
+ char pcContentsType[40]; /* Space for the msg: "text/javascript" */
+ char pcExtraContents[40]; /* Space for the msg: "Content-Length: 346500" */
+ #endif
+ BaseType_t xServerCount;
+ TCPClient_t *pxClients;
+ struct xSERVER
+ {
+ enum eSERVER_TYPE eType; /* eSERVER_HTTP | eSERVER_FTP */
+ const char *pcRootDir;
+ Socket_t xSocket;
+ } xServers[ 1 ];
+};
+
+#endif /* FREERTOS_SERVER_PRIVATE_H */
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPClient.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPClient.h
new file mode 100644
index 000000000..813539e6e
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPClient.h
@@ -0,0 +1,71 @@
+//
+// ntpClient.h
+//
+
+#ifndef __NTPCLIENT_H__
+
+#define __NTPCLIENT_H__
+
+#define NTP_PORT 123
+
+typedef uint32_t quint32;
+typedef int32_t qint32;
+typedef uint8_t quint8;
+typedef int8_t qint8;
+
+typedef union _SNtpFlags SNtpFlags;
+
+/**
+ * 64-bit NTP timestamp.
+ */
+struct __attribute__ ((__packed__)) _SNtpTimestamp {
+ /** Number of seconds passed since Jan 1 1900, in big-endian format. */
+ quint32 seconds;
+
+ /** Fractional time part, in <tt>1/0xFFFFFFFF</tt>s of a second. */
+ quint32 fraction;
+};
+
+typedef struct _SNtpTimestamp SNtpTimestamp;
+/**
+ * Mandatory part of an NTP packet
+ */
+struct SNtpPacket {
+ /** Flags. */
+ unsigned char flags; // value 0xDB : mode 3 (client), version 3, leap indicator unknown 3
+
+ /** Stratum of the clock. */
+ quint8 stratum; // value 0 : unspecified
+
+ /** Maximum interval between successive messages, in log2 seconds. Note that the value is signed. */
+ qint8 poll; // 10 means 1 << 10 = 1024 seconds
+
+ /** Precision of the clock, in log2 seconds. Note that the value is signed. */
+ qint8 precision; // 0xFA = 250 = 0.015625 seconds
+
+ /** Round trip time to the primary reference source, in NTP short format. */
+ qint32 rootDelay; // 0x5D2E = 23854 or (23854/65535)= 0.3640 sec
+
+ /** Nominal error relative to the primary reference source. */
+ qint32 rootDispersion; // 0x0008 CAC8 = 8.7912 seconds
+
+ /** Reference identifier (either a 4 character string or an IP address). */
+ qint8 referenceID[4]; // or just 0000
+
+ /** The time at which the clock was last set or corrected. */
+ SNtpTimestamp referenceTimestamp; // Current time
+
+ /** The time at which the request departed the client for the server. */
+ SNtpTimestamp originateTimestamp; // Keep 0
+
+ /** The time at which the request arrived at the server. */
+ SNtpTimestamp receiveTimestamp; // Keep 0
+
+ /** The time at which the reply departed the server for client. */
+ SNtpTimestamp transmitTimestamp;
+};
+
+/* Add this number to get secs since 1-1-1900 */
+#define TIME1970 2208988800UL
+
+#endif // __NTPCLIENT_H__
diff --git a/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPDemo.h b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPDemo.h
new file mode 100644
index 000000000..e75fb76aa
--- /dev/null
+++ b/FreeRTOS-Plus/Demo/Common/Demo_IP_Protocols/include/NTPDemo.h
@@ -0,0 +1,11 @@
+/*
+ * A simple demo for NTP using FreeRTOS+TCP
+ */
+
+#ifndef NTPDEMO_H
+
+#define NTPDEMO_H
+
+void vStartNTPTask( uint16_t usTaskStackSize, UBaseType_t uxTaskPriority );
+
+#endif \ No newline at end of file