text: /*
   * 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_INVALID_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 ) ) */
  