/*******************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation 
 * All rights reserved. 
 * Copyright (c) 2012 France Telecom All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 *
 * - Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer. 
 * - Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * - Neither name of Intel Corporation nor the names of its contributors 
 * may be used to endorse or promote products derived from this software 
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/*!
 * \file
 *
 * Purpose: This file defines the functionality making use of the http.
 * It defines functions to receive messages, process messages, send messages.
 */

#include "config.h"

#include "httpreadwrite.h"

#include "unixutil.h"
#include "upnp.h"
#include "upnpapi.h"
#include "membuffer.h"
#include "uri.h"
#include "statcodes.h"
#include "sock.h"
#include "UpnpInet.h"
#include "UpnpIntTypes.h"
#include "UpnpStdInt.h"
#include "webserver.h"

#include <assert.h>
#include <stdarg.h>

#ifdef __AMN_OS_LINUX__
#ifdef WIN32
	#include <malloc.h>
	#define fseeko fseek
	#define snprintf _snprintf
#else
	#include <arpa/inet.h>
	#include <sys/types.h>
	#include <sys/time.h>
	#include <sys/wait.h>
	#include <sys/utsname.h>
#endif
#endif

/* 
 * Please, do not change these to const int while MSVC cannot understand
 * const int in array dimensions.
 */
/*
const int CHUNK_HEADER_SIZE = 10;
const int CHUNK_TAIL_SIZE = 10;
*/
#define CHUNK_HEADER_SIZE (size_t)10
#define CHUNK_TAIL_SIZE (size_t)10
#define	SECTOR_SIZE				0x0200		// the size should be 2^n
#define	SECTOR_FRAGMENT(s)		(s & (SECTOR_SIZE - 1))

#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS

/* in seconds */
#define DEFAULT_TCP_CONNECT_TIMEOUT 5

/*!
 * \brief Checks socket connection and wait if it is not connected.
 * It should be called just after connect.
 *
 * \return 0 if successful, else -1.
 */
static int Check_Connect_And_Wait_Connection(
	/*! [in] socket. */
	SOCKET sock,
	/*! [in] result of connect. */
	int connect_res)
{
	struct timeval tmvTimeout = {DEFAULT_TCP_CONNECT_TIMEOUT, 0};
	int result;
#ifdef WIN32
	struct fd_set fdSet;
#else
	fd_set fdSet;
#endif
	FD_ZERO(&fdSet);
	FD_SET(sock, &fdSet);

	if (connect_res < 0) {
#ifdef WIN32
		if (WSAEWOULDBLOCK == WSAGetLastError() )
#else
		if (EINPROGRESS == errno )
#endif
		{
			result = select(sock + 1, NULL, &fdSet, NULL, &tmvTimeout);
			if (result < 0) {
#ifdef WIN32
				/* WSAGetLastError(); */
#else
				/* errno */
#endif
				return -1;
			} else if (result == 0) {
				/* timeout */
				return -1;
#ifndef WIN32
			} else {
				int valopt = 0;
				socklen_t len = sizeof(valopt);
				if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *) &valopt, &len) < 0) {
					/* failed to read delayed error */
					return -1;
				} else if (valopt) {
					/* delayed error = valopt */
					return -1;
				}
#endif
			}
		}
	}

	return 0;
}
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */

static int private_connect(
	SOCKET sockfd,
	const struct sockaddr *serv_addr,
	socklen_t addrlen)
{
#ifndef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS
	int ret = sock_make_no_blocking(sockfd);
	if (ret != - 1) {
		ret = net_connect(sockfd, serv_addr, addrlen);
		ret = Check_Connect_And_Wait_Connection(sockfd, ret);
		if (ret != - 1) {
			ret = sock_make_blocking(sockfd);
		}
	}

	return ret;
#else
	return net_connect(sockfd, serv_addr, addrlen);
#endif /* UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */
}

#ifdef WIN32
struct tm *http_gmtime_r(const time_t *clock, struct tm *result)
{
	if (clock == NULL || *clock < 0 || result == NULL)
		return NULL;

	/* gmtime in VC runtime is thread safe. */
	*result = *gmtime(clock);
	return result;
}
#endif

#if(ENHANCE_FILE_TX_BY_MULTI_THREAD)

#pragma arm section code = "NetFileTxSpeedUp", rwdata = "NetFileTxSpeedUp",  zidata = "NetFileTxSpeedUp"

#define HTTP_MRF_FILE_MAX_JOB	4

typedef struct _HTTP_MRF_RING {
	unsigned int 	readsize;
	void			*pNext;
}HTTP_MRF_RING;

typedef struct _HTTP_MRF_JOB {
	OSAL_FILE 		*Fp;
	unsigned int 	NumofBlock;
	unsigned int 	BlockSize;
	unsigned int 	size_to_be_read;
	sys_sem_t 		pWriteSem;
	sys_sem_t 		pReadSem;
	unsigned char	*pHead;
	unsigned char	*pCurReadBlock;
	unsigned char	*pCurWriteBlock;
}HTTP_MRF_JOB;

static sys_sem_t semMRF = NULL;
static HTTP_MRF_JOB gpHttpMRFileJobList[HTTP_MRF_FILE_MAX_JOB];
extern MMPF_OS_FLAGID	Http_RFile_Flag;

HTTP_MRF_JOB * httpMRFile_AddJob(int blocknum, int blocksize, unsigned int bytestoread, OSAL_FILE *Fp)
{
	int i = 0;
	
	for( ; i < HTTP_MRF_FILE_MAX_JOB ; i++){
		u8_t	ret;
		
		OSSemPend (semMRF,(u16_t)0, (u8_t *)&ret);
		if(ret != OS_NO_ERR) printc("%s : semMRF pending error\n",__func__);
			
		if(gpHttpMRFileJobList[i].Fp == 0){
			int j;
			unsigned char *pBlockBuf , *pTemp ;
			
			//Step 1 : allocate ring buffer and link the ring.
			pBlockBuf = osal_malloc((size_t) blocknum * (sizeof(HTTP_MRF_RING) + blocksize));
			if(!pBlockBuf){
				printc("Not enough memory for blocked reading!\n");
				ret = OSSemPost(semMRF);
				if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
				return NULL;
			}
			
			gpHttpMRFileJobList[i].pHead = pBlockBuf;
			gpHttpMRFileJobList[i].pCurReadBlock = pBlockBuf;
			gpHttpMRFileJobList[i].pCurWriteBlock = pBlockBuf;
			
			pTemp = pBlockBuf;
			for(j = 0 ; j < blocknum ; j++){
				
				((HTTP_MRF_RING *)pTemp)->readsize = 0;
				
				if(j == blocknum - 1 )
					((HTTP_MRF_RING *)pTemp)->pNext = pBlockBuf;
				else
					((HTTP_MRF_RING *)pTemp)->pNext = pTemp + sizeof(HTTP_MRF_RING) + blocksize;
				
				pTemp = ((HTTP_MRF_RING *)pTemp)->pNext;
			}
				
			//Step 2 : allocate semphore
			gpHttpMRFileJobList[i].pWriteSem = OSSemCreate((u16_t)blocknum);
			if(gpHttpMRFileJobList[i].pWriteSem == NULL){
				printc("create sem failed for blocked reading!\n");
				osal_free( pBlockBuf );
				ret = OSSemPost(semMRF);
				if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
				return NULL;
			}
			
			gpHttpMRFileJobList[i].pReadSem = OSSemCreate((u16_t)0);
			if(gpHttpMRFileJobList[i].pReadSem == NULL){
				printc("create sem failed for blocked reading!\n");
				osal_free( pBlockBuf );
				ret = OSSemPost(semMRF);
				if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
				return NULL;
			}
			
			//Step 3 : assign parameters
			gpHttpMRFileJobList[i].NumofBlock = blocknum;
			gpHttpMRFileJobList[i].BlockSize = blocksize;
			gpHttpMRFileJobList[i].size_to_be_read = bytestoread;
			gpHttpMRFileJobList[i].Fp = Fp;
			
			ret = OSSemPost(semMRF);
			if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
			
			MMPF_OS_SetFlags(Http_RFile_Flag, SYS_FLAG_MSG_HTTP_FILE_TRIGGER_READING, MMPF_OS_FLAG_SET);
				
			return &gpHttpMRFileJobList[i];
		}
		
		ret = OSSemPost(semMRF);
		if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
	}
	
	return NULL;
}

int httpMRFile_ReleaseBlock(HTTP_MRF_JOB *pMRFJob)
{
	u8_t	ret;
	
	if(!pMRFJob)	return -1;
		
	pMRFJob->pCurReadBlock = ((HTTP_MRF_RING *)pMRFJob->pCurReadBlock)->pNext;
	
	ret = OSSemPost(pMRFJob->pWriteSem);
	if(ret != OS_NO_ERR){
		printc("%s : post error\n",__func__);
		return -1;
	}
	
	return 0;
}

u8_t * httpMRFile_ReadBlock(HTTP_MRF_JOB *pMRFJob, unsigned int *pReadCount)
{
	u8_t	ret;
	u8_t 	*pCurReadBuf;
	
	if(!pMRFJob){
		printc("%s : Invaild pMRFJob!\n",__func__);
		return NULL;
	}
	
	OSSemPend (pMRFJob->pReadSem,(u16_t)0, (u8_t *)&ret);
	if(ret != OS_NO_ERR){
		printc("%s : pending error\n",__func__);
		return NULL;
	}
	
	pCurReadBuf = pMRFJob->pCurReadBlock;
	*pReadCount = ((HTTP_MRF_RING *)pCurReadBuf)->readsize;
	return pCurReadBuf + sizeof(HTTP_MRF_RING);
}

int httpMRFile_WriteBlock(HTTP_MRF_JOB * pMRFJob)
{
	u8_t			ret;
	size_t			pos,n;
	int 			num_read;
	
	if(!pMRFJob) return -1;
		
	OSSemPend (pMRFJob->pWriteSem,(u16_t)0, (u8_t *)&ret);
	if(ret != OS_NO_ERR) {
		printc("%s : pending error\n",__func__); 
		return -1;
	}
	
	pos = osal_ftell(pMRFJob->Fp);
                        	
	if (SECTOR_FRAGMENT(pos)) {
		// pos is not at alignment of sector, read the fragment to
		// escape the bug that data MAY BE wrong when reading file not at begin of a sector
		n = SECTOR_SIZE - SECTOR_FRAGMENT(pos);
	}
	else{
		n = pMRFJob->BlockSize;
	}
	
	if (n > pMRFJob->size_to_be_read) 
		n = pMRFJob->size_to_be_read;
		
	osal_flush_dcache();
	num_read = osal_fread(pMRFJob->pCurWriteBlock + sizeof(HTTP_MRF_RING), (size_t)1, n, pMRFJob->Fp);
    osal_invalidate_dcache(pMRFJob->pCurWriteBlock + sizeof(HTTP_MRF_RING), num_read);
    
	((HTTP_MRF_RING *)pMRFJob->pCurWriteBlock)->readsize = num_read;
	pMRFJob->size_to_be_read -= num_read;
	pMRFJob->pCurWriteBlock = ((HTTP_MRF_RING *)pMRFJob->pCurWriteBlock)->pNext;
	
	ret = OSSemPost(pMRFJob->pReadSem);
	if(ret != OS_NO_ERR){
		printc("%s : post error\n",__func__);
		return -1;
	}

	return 0;
}

void httpMRFile_FreeJob(HTTP_MRF_JOB * pMRFJob)
{
	u8_t	ret;
	
	if(!pMRFJob) return ;
	
	OSSemPend (semMRF,(u16_t)0, (u8_t *)&ret);
	if(ret != OS_NO_ERR) printc("%s : semMRF pending error\n",__func__);
	
	//Step 1 : free ring buffer.
	osal_free( pMRFJob->pHead );
	//Step 2 : delete semphore
	OSSemDel(pMRFJob->pWriteSem, OS_DEL_ALWAYS, &ret);
    if (ret != OS_NO_ERR) printc("%s : delete sem error = %d\n",__func__,ret);
    OSSemDel(pMRFJob->pReadSem, OS_DEL_ALWAYS, &ret);
    if (ret != OS_NO_ERR) printc("%s : delete sem error = %d\n",__func__,ret);
    //step 3 : reset global array
	memset(pMRFJob,0,sizeof(HTTP_MRF_JOB));
	
	ret = OSSemPost(semMRF);
	if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
}

static osal_thread_t http_file_reading_task(void *arg)
{
	MMPF_OS_FLAGS		flags;
    const MMPF_OS_FLAGS wfs = SYS_FLAG_MSG_HTTP_FILE_TRIGGER_READING ;
	
    while (1) {

		MMPF_OS_WaitFlags(Http_RFile_Flag,
		                  wfs,
						  MMPF_OS_FLAG_WAIT_SET_ANY | MMPF_OS_FLAG_CONSUME,
						  0, &flags);          
        
        if (flags) {
        	
        	if (flags & SYS_FLAG_MSG_HTTP_FILE_TRIGGER_READING){
        		
        		int i = 0;
				unsigned long totalleftbytes = 0;
				
				while(1){
					 
					if(gpHttpMRFileJobList[i].Fp){
						
						if(gpHttpMRFileJobList[i].size_to_be_read){
							u8_t	ret;
							
							OSSemPend (semMRF,(u16_t)0, (u8_t *)&ret);
							if(ret != OS_NO_ERR) printc("%s : semMRF pending error\n",__func__);
								
							//Consider mult-Job, Need to be fair
							httpMRFile_WriteBlock(&gpHttpMRFileJobList[i]);
							
							ret = OSSemPost(semMRF);
							if(ret != OS_NO_ERR) printc("%s : semMRF post error\n",__func__);
								
							totalleftbytes += gpHttpMRFileJobList[i].size_to_be_read;
 						}
					}
					
					if(++i == HTTP_MRF_FILE_MAX_JOB){
						if(totalleftbytes){
							//Still have Job to do
							i = 0;
							totalleftbytes = 0;
							continue;
						}
						break;
					}
						
				}
        		
        	}
        }
    }
}

void httpMRFile_OpenTask(void)
{
	static bIsMRFTaskRunning = 0;
	
	if(bIsMRFTaskRunning == 0){
		
		semMRF = OSSemCreate((u16_t)1);
		
		if(!semMRF){
			printc("httpMRFile_OpenTask : Create semMRF error! Stop creating MFR task!\n");
			return;
		}
		
		memset(gpHttpMRFileJobList,0,sizeof(gpHttpMRFileJobList));
		//osal_thread_new( http_file_reading_task, 0 );
		sys_thread_new("http_file_reading_task",http_file_reading_task,0,LWIP_STK_SIZE, HTTP_FILE_READING_PRIO);
		bIsMRFTaskRunning = 1;
	}
}
#pragma arm section code, rwdata,  zidata
#endif//ENHANCE_FILE_TX_BY_MULTI_THREAD

int http_FixUrl(IN uri_type *url, OUT uri_type *fixed_url)
{
	const char *temp_path = "/";

	*fixed_url = *url;
	if (token_string_casecmp(&fixed_url->scheme, "http") != 0) {
		return UPNP_E_INVALID_URL;
	}
	if( fixed_url->hostport.text.size == ( size_t ) 0 ) {
		return UPNP_E_INVALID_URL;
	}
	/* set pathquery to "/" if it is empty */
	if (fixed_url->pathquery.size == (size_t)0) {
		fixed_url->pathquery.buff = temp_path;
		fixed_url->pathquery.size = (size_t)1;
	}

	return UPNP_E_SUCCESS;
}

int http_FixStrUrl(
	IN const char *urlstr,
	IN size_t urlstrlen,
	OUT uri_type *fixed_url)
{
	uri_type url;

	if (parse_uri(urlstr, urlstrlen, &url) != HTTP_SUCCESS) {
		return UPNP_E_INVALID_URL;
	}

	return http_FixUrl(&url, fixed_url);
}

/************************************************************************
 * Function: http_Connect
 *
 * Parameters:
 *	IN uri_type* destination_url;	URL containing destination information
 *	OUT uri_type *url;		Fixed and corrected URL
 *
 * Description:
 *	Gets destination address from URL and then connects to the remote end
 *
 *  Returns:
 *	socket descriptor on success
 *	UPNP_E_OUTOF_SOCKET
 *	UPNP_E_SOCKET_CONNECT on error
 ************************************************************************/
SOCKET http_Connect(
	IN uri_type *destination_url,
	OUT uri_type *url)
{
	SOCKET connfd;
	socklen_t sockaddr_len;
	int ret_connect;
	char *errorBuffer = NULL;  // char errorBuffer[ERROR_BUFFER_LEN];

	http_FixUrl(destination_url, url);

	connfd = net_socket((int)url->hostport.IPaddress.ss_family,
		SOCK_STREAM, 0);
	if (connfd == INVALID_SOCKET) {
		return (SOCKET)(UPNP_E_OUTOF_SOCKET);
	}
	sockaddr_len = (socklen_t)(url->hostport.IPaddress.ss_family == AF_INET6 ?
		sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
	ret_connect = private_connect(connfd,
		(struct sockaddr *)&url->hostport.IPaddress, sockaddr_len);
	if (ret_connect == -1) {
#ifdef WIN32
		UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
			"connect error: %d\n", WSAGetLastError());
#endif
		if (net_shutdown(connfd, SD_BOTH) == -1) {
			errorBuffer = osal_malloc( ERROR_BUFFER_LEN );
			strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
			UpnpPrintf(UPNP_WARN, HTTP, __FILE__, __LINE__,
				   "Error in shutdown: %s\n", errorBuffer);
			osal_free( errorBuffer );
		}
		UpnpCloseSocket(connfd);
		return (SOCKET)(UPNP_E_SOCKET_CONNECT);
	}

	return connfd;
}


/*!
 * \brief Get the data on the socket and take actions based on the read data to
 * modify the parser objects buffer.
 *
 * If an error is reported while parsing the data, the error code is passed in
 * the http_errr_code parameter.
 *
 * Parameters:
 *	IN SOCKINFO *info;			Socket information object
 *	OUT http_parser_t* parser;		HTTP parser object
 *	IN http_method_t request_method;	HTTP request method
 *	IN OUT int* timeout_secs;		time out
 *	OUT int* http_error_code;		HTTP error code returned
 *
 * \return
 * 	 UPNP_E_SUCCESS
 *	 UPNP_E_BAD_HTTPMSG
 */
int http_RecvMessage(
	IN SOCKINFO *info,
	OUT http_parser_t *parser,
	IN http_method_t request_method,
	IN OUT int *timeout_secs,
	OUT int *http_error_code)
{
	int ret = UPNP_E_SUCCESS;
	int line = 0;
	parse_status_t status;
	int num_read;
	int ok_on_close = FALSE;
	char *buf = osal_malloc(2048);   // char buf[2 * 1024];

	if (request_method == (http_method_t)HTTPMETHOD_UNKNOWN) {
		parser_request_init(parser);
	} else {
		parser_response_init(parser, request_method);
	}

	while (TRUE) {
		num_read = sock_read(info, buf, 2048, timeout_secs);
		if (num_read > 0) {
			/* got data */
			status = parser_append(parser, buf, (size_t)num_read);
			switch (status) {
			case PARSE_SUCCESS:
				UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
					"<<< (RECVD) <<<\n%s\n-----------------\n",
					parser->msg.msg.buf );
				print_http_headers( &parser->msg );
				if (g_maxContentLength > (size_t)0 && parser->content_length > (unsigned int)g_maxContentLength) {
					*http_error_code = HTTP_REQ_ENTITY_TOO_LARGE;
					line = __LINE__;
					ret = UPNP_E_OUTOF_BOUNDS;
					goto ExitFunction;
				}
				line = __LINE__;
				ret = 0;
				goto ExitFunction;
			case PARSE_FAILURE:
				*http_error_code = parser->http_error_code;
				line = __LINE__;
				ret = UPNP_E_BAD_HTTPMSG;
				goto ExitFunction;
			case PARSE_INCOMPLETE_ENTITY:
				/* read until close */
				ok_on_close = TRUE;
				break;
			case PARSE_CONTINUE_1:
				/* Web post request. */
				line = __LINE__;
				ret = PARSE_SUCCESS;
				goto ExitFunction;
			default:
				break;
			}
		} else if (num_read == 0) {
			if (ok_on_close) {
				UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
					"<<< (RECVD) <<<\n%s\n-----------------\n",
					parser->msg.msg.buf );
				print_http_headers(&parser->msg);
				line = __LINE__;
				ret = 0;
				goto ExitFunction;
			} else {
				/* partial msg */
				*http_error_code = HTTP_BAD_REQUEST;    /* or response */
				line = __LINE__;
				// no data!, but not timeout, the socket should be something wrong (remote closed)
				ret = UPNP_E_SOCKET_ERROR;
				goto ExitFunction;
			}
		} else {
			*http_error_code = parser->http_error_code;
			line = __LINE__;
			ret = num_read;
			goto ExitFunction;
		}
	}

ExitFunction:
	if (ret != UPNP_E_SUCCESS) {
		UpnpPrintf(UPNP_ALL, HTTP, __FILE__, line,
			"(http_RecvMessage): Error %d, http_error_code = %d.\n",
			ret,
			*http_error_code);
	}

	osal_free(buf);
	return ret;
}

struct osal_ev_download_parm ev_download_param;
int make_osal_msg(struct osal_ev_download_parm* param, char* filename, int offset)
{
	if (param == NULL)
		return -1;
	strncpy(param->filename, filename, MAX_OSAL_DOWNLOAD_FN_LEN);
	param->offset = offset;
	return 0;
}

#pragma arm section code = "NetFileTxSpeedUp", rwdata = "NetFileTxSpeedUp",  zidata = "NetFileTxSpeedUp"
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
{
#define HTTP_MRF_DBG	0
#if EXCLUDE_WEB_SERVER == 0
	OSAL_FILE *Fp;
	struct SendInstruction *Instr = NULL;
	char *filename = NULL;
	char *file_buf = NULL;
	char *ChunkBuf = NULL;
	/* 10 byte allocated for chunk header. */
	char Chunk_Header[CHUNK_HEADER_SIZE];
	size_t num_read;
	size_t amount_to_be_read = (size_t)0;
	size_t Data_Buf_Size = WEB_SERVER_BUF_SIZE;
#endif /* EXCLUDE_WEB_SERVER */
	va_list argp;
	char *buf = NULL;
	char c;
	int nw;
	int RetVal = 0;
	size_t buf_length;
	size_t num_written;
	int    sto;		// store original timeout
	int offset = 0;
	sto = *TimeOut;
#if EXCLUDE_WEB_SERVER == 0
	memset(Chunk_Header, 0, sizeof(Chunk_Header));
#endif /* EXCLUDE_WEB_SERVER */
	va_start(argp, fmt);
	while ((c = *fmt++) != 0) {
		/* *Timeout restored the original value, it may changed in sock_write. */
		*TimeOut = sto;
#if EXCLUDE_WEB_SERVER == 0
		if (c == 'I') {
			Instr = va_arg(argp, struct SendInstruction *);
			amount_to_be_read = (size_t)Instr->ReadSendSize;
			if (amount_to_be_read < WEB_SERVER_BUF_SIZE)
				Data_Buf_Size = amount_to_be_read;
			ChunkBuf = osal_malloc((size_t)(Data_Buf_Size +
											CHUNK_HEADER_SIZE +
											CHUNK_TAIL_SIZE));
			if (!ChunkBuf) {
				RetVal = UPNP_E_OUTOF_MEMORY;
				goto ExitFunction;
			}
			file_buf = ChunkBuf + CHUNK_HEADER_SIZE;
		} else if (c == 'f') {
			int isVirtualFile = 0;
			void *pMRFHandler = NULL;
			/* file name */
			filename = va_arg(argp, char *);
			if (Instr && Instr->IsVirtualFile)
				isVirtualFile = 1;
			if (isVirtualFile)
				Fp = (virtualDirCallback.open)(filename, UPNP_READ);
			else
				Fp = osal_fopen(filename, "rb");
			if (Fp == NULL) {
				RetVal = UPNP_E_FILE_READ_ERROR;
				goto ExitFunction;
			}
			if (isVirtualFile && Instr->IsRangeActive) {
				if (virtualDirCallback.seek(Fp, Instr->RangeOffset,
				    SEEK_CUR) != 0) {
					RetVal = UPNP_E_FILE_READ_ERROR;
					goto Cleanup_File;
				}
			} else if (Instr && Instr->IsRangeActive) {
				offset = Instr->RangeOffset;
				
				#if(HTTP_MRF_DBG)
				printc("Seek to %d ,Fp=%d\n",offset,Fp);
				#endif
				
				if (osal_fseek(Fp, Instr->RangeOffset, SEEK_CUR) != 0) {
					RetVal = UPNP_E_FILE_READ_ERROR;
					goto Cleanup_File;
				}
			}
			if ((!isVirtualFile) && (make_osal_msg(&ev_download_param, filename, offset) == 0)) {
				int ret;
				ev_download_param.start = 1;
				ret = osal_handle_ev(OSAL_EV_DOWNLOAD, &ev_download_param);
				if (ret != 0) {
					osal_dprint(ERR, "Handle event ret=%d", ret);
				}
			}
			
			#if(ENHANCE_FILE_TX_BY_MULTI_THREAD)
			if(gaEnableMRF && Instr && !Instr->IsChunkActive && !isVirtualFile && amount_to_be_read >= 16384 /*16KB*/){
				/*
				8 Blocks, each one is 8 KBytes , it's by experiment ,
				if someone want to fine tune block number or block size,
				you can open HTTP_MRF_DBG to monitor the elapsed time from httpMRFile_ReadBlock
				In noisy environment ,the elapsed time must be almost 0 
				*/
				if((pMRFHandler = httpMRFile_AddJob(8, 8*1024, amount_to_be_read, Fp)) == NULL)
					printc("Fail to add MRF Job!\n");
			}
			#endif
			
			#if(HTTP_MRF_DBG)
			printc("amount_to_be_read=%d Fp=%d\n",amount_to_be_read,Fp);
			#endif
			
			while (amount_to_be_read) {
				if (Instr) {
					int nr;
					size_t	pos;
					
					size_t n = amount_to_be_read >= Data_Buf_Size ?
					    	Data_Buf_Size : amount_to_be_read;
					if (isVirtualFile) {
						nr = virtualDirCallback.read(Fp, file_buf, n);
						num_read = (size_t)nr;
					} else {
						
						if(pMRFHandler){
							
							#if(HTTP_MRF_DBG)
							unsigned long ulStime;
							#endif
							
							#if(HTTP_MRF_DBG)
							ulStime = OSTimeGet();
							#endif
							
							#if(ENHANCE_FILE_TX_BY_MULTI_THREAD)
							file_buf = (char *)httpMRFile_ReadBlock(pMRFHandler,&num_read);
							#endif
							
							if(!file_buf)
								goto Cleanup_File;
								
							#if(HTTP_MRF_DBG)
							printc("-%d-\n",OSTimeGet() - ulStime);
							#endif
						}
						else{
							
							pos = osal_ftell(Fp);
                        	
							if (SECTOR_FRAGMENT(pos)) {
								// pos is not at alignment of sector, read the fragment to
								// escape the bug that data MAY BE wrong when reading file not at begin of a sector
								n = SECTOR_SIZE - SECTOR_FRAGMENT(pos);
								if (n > amount_to_be_read) n = amount_to_be_read;
							}
							
							osal_flush_dcache();
							num_read = osal_fread(file_buf, (size_t)1, n, Fp);
                        	osal_invalidate_dcache(ChunkBuf, CHUNK_HEADER_SIZE + num_read);
                      	}
					}
					amount_to_be_read -= num_read;
				} else {
					osal_flush_dcache();
					num_read = osal_fread(file_buf, (size_t)1, Data_Buf_Size, Fp);
                    osal_invalidate_dcache(ChunkBuf, CHUNK_HEADER_SIZE + num_read);
				}

				if (num_read == (size_t)0) {
					/* EOF so no more to send. */
					if (Instr && Instr->IsChunkActive) {
						const char *str = "0\r\n\r\n";
						nw = sock_write(info, str,
							       strlen(str),
							       TimeOut);
					} else {
						RetVal = UPNP_E_FILE_READ_ERROR;
					}
					goto Cleanup_File;
				}
				/* Create chunk for the current buffer. */
				if (Instr && Instr->IsChunkActive) {
					int rc;
					/* Copy CRLF at the end of the chunk */
					memcpy(file_buf + num_read, "\r\n", (size_t)2);
					/* Hex length for the chunk size. */
					memset(Chunk_Header, 0,
						sizeof(Chunk_Header));
					rc = snprintf(Chunk_Header,
						sizeof(Chunk_Header) - strlen ("\r\n"),
						"%" PRIzx, num_read);
					if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header) - strlen ("\r\n")) {
						RetVal = UPNP_E_INTERNAL_ERROR;
						goto Cleanup_File;
					}
					strncat(Chunk_Header, "\r\n", strlen ("\r\n"));
					/* Copy the chunk size header  */
					memcpy(file_buf - strlen(Chunk_Header),
					       Chunk_Header,
					       strlen(Chunk_Header));
					/* on the top of the buffer. */
					/*file_buf[num_read+strlen(Chunk_Header)] = NULL; */
					/*printf("Sending %s\n",file_buf-strlen(Chunk_Header)); */
					
					if((nw = sock_write(info,
					 	file_buf - strlen(Chunk_Header), 
						num_read + strlen(Chunk_Header) + (size_t)2, 
						TimeOut)) == -1){
						UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
						   ">>> (SENT) >>> write error fd = %d ------------\n", info->socket);
						RetVal = -1;
						goto Cleanup_File;
					}
					
					num_written = (size_t)nw;
					if (nw <= 0 || num_written != num_read + strlen(Chunk_Header) + (size_t)2)
						/* Send error nothing we can do. */
						goto Cleanup_File;
				} else {
					/* write data */
					if((nw = sock_write(info, file_buf, num_read, TimeOut)) == -1){
						UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
						   ">>> (SENT) >>> write error fd = %d ------------\n", info->socket);
						RetVal = -1;
						goto Cleanup_File;
					}
					UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
						   ">>> (SENT) >>> num_written=%d  num_read=%d ------------\n", nw, num_read);
					/* Send error nothing we can do */
					num_written = (size_t)nw;
					if (nw <= 0 || num_written != num_read) {
						goto Cleanup_File;
					}
					
					#if(ENHANCE_FILE_TX_BY_MULTI_THREAD)
					if(pMRFHandler)
						httpMRFile_ReleaseBlock(pMRFHandler);
					#endif
				}
			} /* while */
Cleanup_File:
			#if(ENHANCE_FILE_TX_BY_MULTI_THREAD)
			if(pMRFHandler){
				httpMRFile_ReleaseBlock(pMRFHandler);
				httpMRFile_FreeJob(pMRFHandler);
			}
			#endif
			if (isVirtualFile) {
				virtualDirCallback.close(Fp);
			} else {
				int ret;
				osal_fclose(Fp);
				ev_download_param.start = 0;
				ret = osal_handle_ev(OSAL_EV_DOWNLOAD, &ev_download_param);
				if (ret != 0) {
					osal_dprint(ERR, "Handle event ret=%d", ret);
				}
			}
			goto ExitFunction;
		} else
#endif /* EXCLUDE_WEB_SERVER */
		if (c == 'b') {
			/* memory buffer */
			buf = va_arg(argp, char *);
			buf_length = va_arg(argp, size_t);
			if (buf_length > (size_t)0) {
				
				if((nw = sock_write(info, buf, buf_length, TimeOut)) < 0){
					UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
						   ">>> (SENT) >>> write error fd = %d ------------\n", info->socket);
					RetVal = -1;
					goto ExitFunction;
				}
				
				num_written = (size_t)nw;
				UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
					   ">>> (SENT) >>>\n"
					   "%.*s\nbuf_length=%" PRIzd ", num_written=%" PRIzd "\n"
					   "------------\n",
					   (int)buf_length, buf, buf_length, num_written);
				if (num_written != buf_length) {
					RetVal = 0;
					goto ExitFunction;
				}
			}
		}
		if (c == 'x')
		{
			filename = va_arg(argp, char *);
			Fp = (virtualDirCallback.open)(filename, UPNP_READ);
				
			while (amount_to_be_read) {
				int nr;
				size_t n = amount_to_be_read >= Data_Buf_Size ?
					    	Data_Buf_Size : amount_to_be_read;
				if (Instr->IsVirtualFile) {
						nr = virtualDirCallback.read(Fp, file_buf, n);
						num_read = (size_t)nr;
				}
				amount_to_be_read -= num_read;	
			}
			{
					/* write data */
				if((nw = sock_write(info, file_buf, num_read, TimeOut)) == -1){
					UpnpPrintf(UPNP_CRITICAL, HTTP, __FILE__, __LINE__,
					   ">>> (SENT) >>> write error fd = %d ------------\n", info->socket);
					RetVal = -1;
					//goto Cleanup_File;
				}
				UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
					   ">>> (SENT) >>> num_written=%d  num_read=%d ------------\n", nw, num_read);

				if (Fp)
					virtualDirCallback.close(Fp);
				/* Send error nothing we can do */
//				num_written = (size_t)nw;
//				if (nw <= 0 || num_written != num_read) {
//					goto Cleanup_File;
//				}
				
//				goto ExitFunction;
			}
			
		}
	}

ExitFunction:
	va_end(argp);
#if EXCLUDE_WEB_SERVER == 0
    #ifndef	_RELEASE_
	if (ChunkBuf) {
	  	osal_free(ChunkBuf);
	}
    #endif
#endif /* EXCLUDE_WEB_SERVER */
	return RetVal;
}
#pragma arm section code, rwdata,  zidata

/************************************************************************
 * Function: http_RequestAndResponse
 *
 * Parameters:
 *	IN uri_type* destination;	Destination URI object which contains
 *					remote IP address among other elements
 *	IN const char* request;		Request to be sent
 *	IN size_t request_length;	Length of the request
 *	IN http_method_t req_method;	HTTP Request method
 *	IN int timeout_secs;		time out value
 *	OUT http_parser_t* response;	Parser object to receive the repsonse
 *
 * Description:
 *	Initiates socket, connects to the destination, sends a
 *	request and waits for the response from the remote end
 *
 * Returns:
 *	UPNP_E_SOCKET_ERROR
 * 	UPNP_E_SOCKET_CONNECT
 *	Error Codes returned by http_SendMessage
 *	Error Codes returned by http_RecvMessage
 ************************************************************************/
int http_RequestAndResponse(
	IN uri_type *destination,
	IN const char *request,
	IN size_t request_length,
	IN http_method_t req_method,
	IN int timeout_secs,
	OUT http_parser_t *response)
{
	SOCKET tcp_connection;
	int ret_code;
	size_t sockaddr_len;
	int http_error_code;
	SOCKINFO info;

	tcp_connection = net_socket(
		(int)destination->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
	if (tcp_connection == INVALID_SOCKET) {
		parser_response_init(response, req_method);
		return UPNP_E_SOCKET_ERROR;
	}
	if (sock_init(&info, tcp_connection) != UPNP_E_SUCCESS) {
		parser_response_init(response, req_method);
		ret_code = UPNP_E_SOCKET_ERROR;
		goto end_function;
	}
	/* connect */
	sockaddr_len = destination->hostport.IPaddress.ss_family == AF_INET6 ?
		sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
	ret_code = private_connect(info.socket,
		(struct sockaddr *)&(destination->hostport.IPaddress),
		(socklen_t)sockaddr_len);
	if (ret_code == -1) {
		parser_response_init(response, req_method);
		ret_code = UPNP_E_SOCKET_CONNECT;
		goto end_function;
	}
	/* send request */
	ret_code = http_SendMessage(&info, &timeout_secs, "b",
		request, request_length);
	if (ret_code != 0) {
		parser_response_init(response, req_method);
		goto end_function;
	}
	/* recv response */
	ret_code = http_RecvMessage(&info, response, req_method,
		&timeout_secs, &http_error_code);

end_function:
	/* should shutdown completely */
	sock_destroy(&info, SD_BOTH);

	return ret_code;
}


/************************************************************************
 * Function: http_Download
 *
 * Parameters:
 *	IN const char* url_str;	String as a URL
 *	IN int timeout_secs;	time out value
 *	OUT char** document;	buffer to store the document extracted
 *				from the donloaded message.
 *	OUT int* doc_length;	length of the extracted document
 *	OUT char* content_type;	Type of content
 *
 * Description:
 *	Download the document message and extract the document 
 *	from the message.
 *
 * Return: int
 *	UPNP_E_SUCCESS
 *	UPNP_E_INVALID_URL
 ************************************************************************/
int http_Download( IN const char *url_str,
               IN int timeout_secs,
               OUT char **document,
               OUT size_t *doc_length,
               OUT char *content_type )
{
	int ret_code;
	uri_type url;
	char *msg_start;
	char *entity_start;
	char *hoststr;
	char *temp;
	http_parser_t *response = osal_malloc( sizeof(http_parser_t) ); // http_parser_t response;
	size_t msg_length;
	size_t hostlen;
	memptr ctype;
	size_t copy_len;
	membuffer request;
	char *urlPath = osal_malloc(strlen(url_str) + (size_t)1); // alloca

	/*ret_code = parse_uri( (char*)url_str, strlen(url_str), &url ); */
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "DOWNLOAD URL : %s\n", url_str);
	ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), &url);
	if (ret_code != UPNP_E_SUCCESS)
		goto L_exit;
	/* make msg */
	membuffer_init(&request);
	memset(urlPath, 0, strlen(url_str) + (size_t)1);
	strncpy(urlPath, url_str, strlen(url_str));
	hoststr = strstr(urlPath, "//");
	if (hoststr == NULL) {
		ret_code = UPNP_E_INVALID_URL;
		goto L_exit;
	}
	hoststr += 2;
	temp = strchr(hoststr, '/');
	if (temp) {
		*temp = '\0';
		hostlen = strlen(hoststr);
		*temp = '/';
	} else {
		hostlen = strlen(hoststr);
	}
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
	ret_code = http_MakeMessage(&request, 1, 1,
				    "Q" "s" "bcDCUc",
				    HTTPMETHOD_GET, url.pathquery.buff,
				    url.pathquery.size, "HOST: ", hoststr,
				    hostlen);
	if (ret_code != 0) {
		UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
			   "HTTP Makemessage failed\n");
		membuffer_destroy(&request);
		goto L_exit;
	}
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HTTP Buffer:\n%s\n" "----------END--------\n", request.buf);
	/* get doc msg */
	ret_code =
	    http_RequestAndResponse(&url, request.buf, request.length,
				    HTTPMETHOD_GET, timeout_secs, response);

	if (ret_code != 0) {
		httpmsg_destroy(&response->msg);
		membuffer_destroy(&request);
		goto L_exit;
	}
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__, "Response\n");
	print_http_headers(&response->msg);
	/* optional content-type */
	if (content_type) {
		if (httpmsg_find_hdr(&response->msg, HDR_CONTENT_TYPE, &ctype) ==
		    NULL) {
			*content_type = '\0';	/* no content-type */
		} else {
			/* safety */
			copy_len = ctype.length < LINE_SIZE - (size_t)1 ?
			    ctype.length : LINE_SIZE - (size_t)1;

			memcpy(content_type, ctype.buf, copy_len);
			content_type[copy_len] = '\0';
		}
	}
	/* extract doc from msg */
	if ((*doc_length = response->msg.entity.length) == (size_t)0) {
		/* 0-length msg */
		*document = NULL;
	} else if (response->msg.status_code == HTTP_OK) {
		/*LEAK_FIX_MK */
		/* copy entity */
		entity_start = response->msg.entity.buf;	/* what we want */
		msg_length = response->msg.msg.length;	/* save for posterity    */
		msg_start = membuffer_detach(&response->msg.msg);	/* whole msg */
		/* move entity to the start; copy null-terminator too */
		memmove(msg_start, entity_start, *doc_length + (size_t)1);
		/* save mem for body only */
		*document = osal_realloc(msg_start, *doc_length + (size_t)1);	/*LEAK_FIX_MK */
		/* *document = Realloc( msg_start,msg_length, *doc_length + 1 ); LEAK_FIX_MK */
		/* shrink can't fail */
		assert(msg_length > *doc_length);
		assert(*document != NULL);
		if (msg_length <= *doc_length || *document == NULL)
			UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
				"msg_length(%" PRIzu ") <= *doc_length(%"
				PRIzu ") or document is NULL",
				msg_length, *doc_length);
	}
	if (response->msg.status_code == HTTP_OK) {
		ret_code = 0;	/* success */
	} else {
		/* server sent error msg (not requested doc) */
		ret_code = response->msg.status_code;
	}
	httpmsg_destroy(&response->msg);
	membuffer_destroy(&request);

L_exit:
	osal_free(urlPath);
	osal_free(response);
	return ret_code;
}

typedef struct HTTPPOSTHANDLE {
	SOCKINFO sock_info;
	int contentLength;
} http_post_handle_t;

/************************************************************************
 * Function: MakePostMessage
 *
 * Parameters:
 *	const char *url_str;		String as a URL
 *	membuffer *request;		Buffer containing the request
 *	uri_type *url; 			URI object containing the scheme,
 *					path query token, etc.
 *	int contentLength;		length of content
 *	const char *contentType;	Type of content
 *
 * Description:
 *	Makes the message for the HTTP POST message
 *
 * Returns:
 *	UPNP_E_INVALID_URL
 * 	UPNP_E_INVALID_PARAM
 *	UPNP_E_SUCCESS
 ************************************************************************/
int MakePostMessage(const char *url_str, membuffer *request,
	uri_type *url, int contentLength, const char *contentType)
{
	int ret_code = 0;
	char *urlPath = osal_malloc(strlen(url_str) + (size_t)1); // alloca
	size_t hostlen = (size_t)0;
	char *hoststr;
	char *temp;

	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "DOWNLOAD URL : %s\n", url_str);
	ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), url);
	if (ret_code != UPNP_E_SUCCESS)
		goto L_exit;
	/* make msg */
	membuffer_init(request);
	memset(urlPath, 0, strlen(url_str) + (size_t)1);
	strncpy(urlPath, url_str, strlen(url_str));
	hoststr = strstr(urlPath, "//");
	if (hoststr == NULL) {
		ret_code = UPNP_E_INVALID_URL;
		goto L_exit;
	}
	hoststr += 2;
	temp = strchr(hoststr, '/');
	if (temp == NULL) {
		ret_code = UPNP_E_INVALID_URL;
		goto L_exit;
	}
	*temp = '\0';
	hostlen = strlen(hoststr);
	*temp = '/';
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
	if (contentLength >= 0)
		ret_code = http_MakeMessage(request, 1, 1,
					    "Q" "s" "bcDCU" "T" "Nc",
					    HTTPMETHOD_POST,
					    url->pathquery.buff,
					    url->pathquery.size, "HOST: ",
					    hoststr, hostlen, contentType,
					    (off_t) contentLength);
	else if (contentLength == UPNP_USING_CHUNKED)
		ret_code = http_MakeMessage(request, 1, 1,
					    "Q" "s" "bcDCU" "TKc",
					    HTTPMETHOD_POST,
					    url->pathquery.buff,
					    url->pathquery.size, "HOST: ",
					    hoststr, hostlen, contentType);
	else if (contentLength == UPNP_UNTIL_CLOSE)
		ret_code = http_MakeMessage(request, 1, 1,
					    "Q" "s" "bcDCU" "Tc",
					    HTTPMETHOD_POST,
					    url->pathquery.buff,
					    url->pathquery.size, "HOST: ",
					    hoststr, hostlen, contentType);
	else
		ret_code = UPNP_E_INVALID_PARAM;
	if (ret_code != 0) {
		UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
			   "HTTP Makemessage failed\n");
		membuffer_destroy(request);
		goto L_exit;
	}
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HTTP Buffer:\n%s\n" "----------END--------\n",
		   request->buf);

	ret_code = UPNP_E_SUCCESS;

L_exit:
	osal_free(urlPath);
	return ret_code;
}

/************************************************************************
 * Function: http_WriteHttpPost
 *
 * Parameters:
 *	IN void *Handle:	Handle to the http post object
 *	IN char *buf:		Buffer to send to peer, if format used
 *				is not UPNP_USING_CHUNKED, 
 *	IN unsigned int *size:	Size of the data to be sent.
 *	IN int timeout:		time out value
 *
 * Description:
 *	Formats data if format used is UPNP_USING_CHUNKED.
 *	Writes data on the socket connected to the peer.
 *
 * Return: int
 *	UPNP_E_SUCCESS - On Success
 *	UPNP_E_INVALID_PARAM - Invalid Parameter
 *	-1 - On Socket Error.
 ************************************************************************/
int http_WriteHttpPost( IN void *Handle,
                    IN char *buf,
                    IN size_t *size,
                    IN int timeout )
{
	http_post_handle_t *handle = (http_post_handle_t *)Handle;
	char *tempbuf = NULL;
	size_t tempbufSize = (size_t)0;
	int freeTempbuf = 0;
	int numWritten = 0;

	if (!handle || !size || !buf) {
		if (size)
			*size = (size_t)0;
		return UPNP_E_INVALID_PARAM;
	}
	if (handle->contentLength == UPNP_USING_CHUNKED) {
		if (*size) {
			size_t tempSize = *size +
				CHUNK_HEADER_SIZE + CHUNK_TAIL_SIZE;
			tempbuf = osal_malloc(tempSize);
			if (!tempbuf)
				return UPNP_E_OUTOF_MEMORY;
			/* begin chunk */
			snprintf(tempbuf, tempSize, "%" PRIzx "\r\n", *size);
			tempSize = strlen(tempbuf);
			memcpy(tempbuf + tempSize, buf, *size);
			memcpy(tempbuf + tempSize + *size, "\r\n", (size_t)2);
			/* end of chunk */
			tempbufSize = tempSize + *size + (size_t)2;
			freeTempbuf = 1;
		}
	} else {
		tempbuf = buf;
		tempbufSize = *size;
	}
	numWritten =
	    sock_write(&handle->sock_info, tempbuf, tempbufSize, &timeout);
	if (freeTempbuf)
		osal_free(tempbuf);
	if (numWritten < 0) {
		*size = (size_t)0;
		return numWritten;
	} else {
		*size = (size_t)numWritten;
		return UPNP_E_SUCCESS;
	}
}

/************************************************************************
 * Function: http_CloseHttpPost
 *
 * Parameters:
 *	IN void *Handle;	Handle to the http post object
 *	IN OUT int *httpStatus;	HTTP status returned on receiving a
 *				response message
 *	IN int timeout;		time out value
 *
 * Description:
 *	Sends remaining data if using  UPNP_USING_CHUNKED 
 *	format. Receives any more messages. Destroys socket and any socket
 *	associated memory. Frees handle associated with the HTTP POST msg.
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On success
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 ************************************************************************/
int http_CloseHttpPost(IN void *Handle, IN OUT int *httpStatus, IN int timeout)
{
	int retc = 0;
	http_parser_t *response = NULL; // http_parser_t response;
	int http_error_code;
	const char *zcrlf = "0\r\n\r\n";

	http_post_handle_t *handle = Handle;
	if ((!handle) || (!httpStatus))
		return UPNP_E_INVALID_PARAM;
	if (handle->contentLength == UPNP_USING_CHUNKED)
		/*send last chunk */
		retc = sock_write(&handle->sock_info, zcrlf, strlen(zcrlf), &timeout);
	/*read response */
	response = osal_malloc( sizeof(http_parser_t) );
	parser_response_init(response, HTTPMETHOD_POST);
	retc = http_RecvMessage(&handle->sock_info, response,
		HTTPMETHOD_POST, &timeout, &http_error_code);
	*httpStatus = http_error_code;
	/*should shutdown completely */
	sock_destroy(&handle->sock_info, SD_BOTH);
	httpmsg_destroy(&response->msg);
	osal_free(response);
	osal_free(handle);

	return retc;
}

/************************************************************************
 * Function: http_OpenHttpPost
 *
 * Parameters:
 *	IN const char *url_str;		String as a URL	
 *	IN OUT void **Handle;		Pointer to buffer to store HTTP
 *					post handle
 *	IN const char *contentType;	Type of content
 *	IN int contentLength;		length of content
 *	IN int timeout;			time out value
 *
 * Description:
 *	Makes the HTTP POST message, connects to the peer, 
 *	sends the HTTP POST request. Adds the post handle to buffer of 
 *	such handles
 *
 * Return : int;
 *	UPNP_E_SUCCESS		- On success
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 *	UPNP_E_OUTOF_MEMORY
 *	UPNP_E_SOCKET_ERROR
 *	UPNP_E_SOCKET_CONNECT
 ************************************************************************/
int http_OpenHttpPost(
	IN const char *url_str,
	IN OUT void **Handle,
	IN const char *contentType,
	IN int contentLength,
	IN int timeout)
{
	int ret_code;
	size_t sockaddr_len;
	SOCKET tcp_connection;
	membuffer request;
	http_post_handle_t *handle = NULL;
	uri_type url;

	if (!url_str || !Handle || !contentType)
		return UPNP_E_INVALID_PARAM;
	*Handle = handle;
	ret_code = MakePostMessage(url_str, &request, &url,
				   contentLength, contentType);
	if (ret_code != UPNP_E_SUCCESS)
		return ret_code;
	handle = osal_malloc(sizeof(http_post_handle_t));
	if (!handle)
		return UPNP_E_OUTOF_MEMORY;
	handle->contentLength = contentLength;
	tcp_connection = net_socket((int)url.hostport.IPaddress.ss_family,
		SOCK_STREAM, 0);
	if (tcp_connection == INVALID_SOCKET) {
		ret_code = UPNP_E_SOCKET_ERROR;
		goto errorHandler;
	}
	if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
		sock_destroy(&handle->sock_info, SD_BOTH);
		ret_code = UPNP_E_SOCKET_ERROR;
		goto errorHandler;
	}
	sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
		sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
	ret_code = private_connect(handle->sock_info.socket,
		(struct sockaddr *)&(url.hostport.IPaddress),
		(socklen_t)sockaddr_len);
	if (ret_code == -1) {
		sock_destroy(&handle->sock_info, SD_BOTH);
		ret_code = UPNP_E_SOCKET_CONNECT;
		goto errorHandler;
	}
	/* send request */
	ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
				    request.buf, request.length);
	if (ret_code != 0)
		sock_destroy(&handle->sock_info, SD_BOTH);

 errorHandler:
	membuffer_destroy(&request);
	*Handle = handle;

	return ret_code;
}

typedef struct HTTPGETHANDLE {
	http_parser_t response;
	SOCKINFO sock_info;
	int entity_offset;
	int cancel;
} http_get_handle_t;

/************************************************************************
* Function: MakeGetMessage
*
* Parameters:
*	const char *url_str ;	String as a URL
*	const char *proxy_str ;	String as a URL of proxy to use
*	membuffer *request ;	Buffer containing the request
*	uri_type *url ; 	URI object containing the scheme, path 
*				query token, etc.
*
* Description:
*	Makes the message for the HTTP GET method
*
* Returns:
*	UPNP_E_INVALID_URL
* 	Error Codes returned by http_MakeMessage
*	UPNP_E_SUCCESS
************************************************************************/
int MakeGetMessage(const char *url_str, const char *proxy_str,
	membuffer *request, uri_type *url)
{
	int ret_code;
	char *urlPath = osal_malloc(strlen(url_str) + (size_t)1); // alloca
	size_t querylen = (size_t)0;
	const char *querystr;
	size_t hostlen = (size_t)0;
	char *hoststr, *temp;

	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "DOWNLOAD URL : %s\n", url_str);
	ret_code = http_FixStrUrl((char *)url_str, strlen(url_str), url);
	if (ret_code != UPNP_E_SUCCESS)
		goto L_exit;
	/* make msg */
	membuffer_init(request);
	memset(urlPath, 0, strlen(url_str) + (size_t)1);
	strncpy(urlPath, url_str, strlen(url_str));
	hoststr = strstr(urlPath, "//");
	if (hoststr == NULL) {
		ret_code = UPNP_E_INVALID_URL;
		goto L_exit;
	}
	hoststr += 2;
	temp = strchr(hoststr, '/');
	if (temp == NULL) {
		ret_code = UPNP_E_INVALID_URL;
		goto L_exit;
	}
	*temp = '\0';
	hostlen = strlen(hoststr);
	*temp = '/';
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HOSTNAME : %s Length : %" PRIzu "\n", hoststr, hostlen);
	if (proxy_str) {
		querystr = url_str;
		querylen = strlen(querystr);
	} else {
		querystr = url->pathquery.buff;
		querylen = url->pathquery.size;
	}
	ret_code = http_MakeMessage(request, 1, 1,
				    "Q" "s" "bcDCUc",
				    HTTPMETHOD_GET, querystr, querylen,
				    "HOST: ", hoststr, hostlen);
	if (ret_code != 0) {
		UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
			   "HTTP Makemessage failed\n");
		membuffer_destroy(request);

		goto L_exit;
	}
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HTTP Buffer:\n%s\n" "----------END--------\n",
		   request->buf);

	ret_code = UPNP_E_SUCCESS;

L_exit:
	osal_free(urlPath);
	return ret_code;
}

/*!
 * \brief Parses already exiting data. If not complete reads more 
 * data on the connected socket. The read data is then parsed. The 
 * same methid is carried out for headers.
 *
 * \return integer:
 *	\li \c PARSE_OK - On Success
 *	\li \c PARSE_FAILURE - Failure to parse data correctly
 *	\li \c UPNP_E_BAD_HTTPMSG - Socker read() returns an error
 */
static int ReadResponseLineAndHeaders(
	/*! Socket information object. */
	IN SOCKINFO *info,
	/*! HTTP Parser object. */
	IN OUT http_parser_t *parser,
	/*! Time out value. */
	IN OUT int *timeout_secs,
	/*! HTTP errror code returned. */
	IN OUT int *http_error_code)
{
	parse_status_t status;
	int num_read;
	char *buf = NULL; // char buf[2 * 1024];
	int done = 0;
	int ret_code = 0;

	/*read response line */
	status = parser_parse_responseline(parser);
	switch (status) {
	case PARSE_OK:
		done = 1;
		break;
	case PARSE_INCOMPLETE:
		done = 0;
		break;
	default:
		/*error */
		return status;
	}

	buf = osal_malloc( 2048 );
	while (!done) {
		num_read = sock_read(info, buf, 2048, timeout_secs);
		if (num_read > 0) {
			/* append data to buffer */
			ret_code =
				membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
			if (ret_code != 0) {
				/* set failure status */
				parser->http_error_code =
				    HTTP_INTERNAL_SERVER_ERROR;
				status = PARSE_FAILURE;
				goto L_exit;
			}
			status = parser_parse_responseline(parser);
			switch (status) {
			case PARSE_OK:
				done = 1;
				break;
			case PARSE_INCOMPLETE:
				done = 0;
				break;
			default:
				/*error */
				goto L_exit;
			}
		} else if (num_read == 0) {
			/* partial msg */
			*http_error_code = HTTP_BAD_REQUEST;	/* or response */
			status = (parse_status_t)UPNP_E_BAD_HTTPMSG;//hacking value for more debugging info.
			goto L_exit;
		} else {
			*http_error_code = parser->http_error_code;
			status = num_read;
			goto L_exit;
		}
	}
	status = parser_parse_headers(parser);
	if ((status == (parse_status_t)PARSE_OK) &&
		(parser->position == (parser_pos_t)POS_ENTITY))
		done = 1;
	else if (status == (parse_status_t)PARSE_INCOMPLETE)
		done = 0;
	else
		/*error */
		goto L_exit;

	/*read headers */
	while (!done) {
		num_read = sock_read(info, buf, 2048, timeout_secs);
		if (num_read > 0) {
			/* append data to buffer */
			ret_code =
			    membuffer_append(&parser->msg.msg, buf, (size_t)num_read);
			if (ret_code != 0) {
				/* set failure status */
				parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
				status = PARSE_FAILURE;
				goto L_exit;
			}
			status = parser_parse_headers(parser);
			if (status == (parse_status_t)PARSE_OK &&
				parser->position == (parser_pos_t)POS_ENTITY)
				done = 1;
			else if (status == (parse_status_t)PARSE_INCOMPLETE)
				done = 0;
			else
				/*error */
				goto L_exit;
		} else if (num_read == 0) {
			/* partial msg */
			*http_error_code = HTTP_BAD_REQUEST;	/* or response */
			status = (parse_status_t) UPNP_E_BAD_HTTPMSG;//hacking value for more debugging info
			goto L_exit;
		} else {
			*http_error_code = parser->http_error_code;
			status = num_read;
			goto L_exit;
		}
	}

	status = PARSE_OK;

L_exit:
	if (buf) osal_free( buf );
	return status;
}

/************************************************************************
 * Function: http_ReadHttpGet
 *
 * Parameters:
 *	IN void *Handle;	Handle to the HTTP get object
 *	IN OUT char *buf;	Buffer to get the read and parsed data
 *	IN OUT size_t *size;	Size of the buffer passed
 *	IN int timeout;		time out value
 *
 * Description:
 *	Parses already existing data, then gets new data.
 *	Parses and extracts information from the new data.
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On success
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 *	UPNP_E_BAD_RESPONSE
 *	UPNP_E_BAD_HTTPMSG
 *	UPNP_E_CANCELED
 ************************************************************************/
int http_ReadHttpGet(
	IN void *Handle,
	IN OUT char *buf,
	IN OUT size_t *size,
	IN int timeout)
{
	http_get_handle_t *handle = Handle;
	parse_status_t status;
	int num_read;
	int ok_on_close = FALSE;
	char *tempbuf;  // char tempbuf[2 * 1024];
	int ret_code = 0;

	if (!handle || !size || (*size > (size_t)0 && !buf)) {
		if (size)
			*size = (size_t)0;
		return UPNP_E_INVALID_PARAM;
	}
	/* first parse what has already been gotten */
	if (handle->response.position != (parser_pos_t)POS_COMPLETE)
		status = parser_parse_entity(&handle->response);
	else
		status = PARSE_SUCCESS;
	switch (status) {
	case PARSE_INCOMPLETE_ENTITY:
		/* read until close */
		ok_on_close = TRUE;
		break;
	case PARSE_SUCCESS:
	case PARSE_CONTINUE_1:
	case PARSE_INCOMPLETE:
		break;
	default:
		/*error */
		*size = (size_t)0;
		return UPNP_E_BAD_RESPONSE;
	}

	tempbuf = osal_malloc(2048);
	/* read more if necessary entity */
	while (handle->response.msg.amount_discarded + *size >
	       handle->response.msg.entity.length &&
	       !handle->cancel &&
	       handle->response.position != (parser_pos_t)POS_COMPLETE) {
		num_read = sock_read(&handle->sock_info, tempbuf,
			2048, &timeout);
		if (num_read > 0) {
			/* append data to buffer */
			ret_code = membuffer_append(&handle->response.msg.msg,
				tempbuf, (size_t)num_read);
			if (ret_code != 0) {
				/* set failure status */
				handle->response.http_error_code =
				    HTTP_INTERNAL_SERVER_ERROR;
				*size = (size_t)0;
				ret_code = PARSE_FAILURE;
				goto L_exit;
			}
			status = parser_parse_entity(&handle->response);
			switch (status) {
			case PARSE_INCOMPLETE_ENTITY:
				/* read until close */
				ok_on_close = TRUE;
				break;
			case PARSE_SUCCESS:
			case PARSE_CONTINUE_1:
			case PARSE_INCOMPLETE:
				break;
			default:
				/*error */
				*size = (size_t)0;
				ret_code = UPNP_E_BAD_RESPONSE;
				goto L_exit;
			}
		} else if (num_read == 0) {
			if (ok_on_close) {
				UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
					   "<<< (RECVD) <<<\n%s\n-----------------\n",
					   handle->response.msg.msg.buf);
				handle->response.position = POS_COMPLETE;
			} else {
				/* partial msg */
				*size = (size_t)0;
				handle->response.http_error_code = HTTP_BAD_REQUEST;	/* or response */
				ret_code = UPNP_E_BAD_HTTPMSG;
				goto L_exit;
			}
		} else {
			*size = (size_t)0;
			ret_code = num_read;
			goto L_exit;
		}
	}
	if (handle->cancel) {
		ret_code = UPNP_E_CANCELED;
		goto L_exit;
	}
	/* truncate size to fall within available data */
	if (handle->response.msg.amount_discarded + *size >
	    handle->response.msg.entity.length)
		*size = handle->response.msg.entity.length -
			handle->response.msg.amount_discarded;
	/* copy data to user buffer. delete copied data */
	if (*size > (size_t)0) {
		memcpy(buf, &handle->response.msg.msg.buf[handle->response.entity_start_position],
			*size);
		membuffer_delete(&handle->response.msg.msg,
			handle->response.entity_start_position, *size);
		/* update scanner position. needed for chunked transfers */
		handle->response.scanner.cursor -= *size;
		/* update amount discarded */
		handle->response.msg.amount_discarded += *size;
	}

	ret_code = UPNP_E_SUCCESS;

L_exit:
	osal_free(tempbuf);
	return ret_code;
}

/************************************************************************
 * Function: http_HttpGetProgress
 *
 * Parameters:
 *	IN void *Handle;	Handle to the HTTP get object
 *	OUT size_t *length;	Buffer to get the read and parsed data
 *	OUT size_t *total;	Size of tge buffer passed
 *
 * Description:
 *	Extracts information from the Handle to the HTTP get object.
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On Sucess
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 ************************************************************************/
int http_HttpGetProgress(
	IN void *Handle, 
	OUT size_t *length,
	OUT size_t *total)
{
	http_get_handle_t *handle = Handle;

	if (!handle || !length || !total) {
		return UPNP_E_INVALID_PARAM;
	}
	*length = handle->response.msg.entity.length;
	*total = handle->response.content_length;

	return UPNP_E_SUCCESS;
}

/************************************************************************
 * Function: http_CancelHttpGet
 *
 * Parameters:
 *	IN void *Handle;	Handle to HTTP get object
 *
 * Description:
 *	Set the cancel flag of the HttpGet handle
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On Success
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 ************************************************************************/
int http_CancelHttpGet(IN void *Handle)
{
	http_get_handle_t *handle = Handle;

	if (!handle)
		return UPNP_E_INVALID_PARAM;
	handle->cancel = 1;

	return UPNP_E_SUCCESS;
}


/************************************************************************
 * Function: http_CloseHttpGet
 *
 * Parameters:
 *	IN void *Handle;	Handle to HTTP get object
 *
 * Description:
 *	Clears the handle allocated for the HTTP GET operation
 *	Clears socket states and memory allocated for socket operations. 
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On Success
 *	UPNP_E_INVALID_PARAM	- Invalid Parameter
 ************************************************************************/
int http_CloseHttpGet(IN void *Handle)
{
	http_get_handle_t *handle = Handle;

	if (!handle)
		return UPNP_E_INVALID_PARAM;
	/*should shutdown completely */
	sock_destroy(&handle->sock_info, SD_BOTH);
	httpmsg_destroy(&handle->response.msg);
	osal_free(handle);

	return UPNP_E_SUCCESS;
}

int http_OpenHttpGet( IN const char *url_str,
                  IN OUT void **Handle,
                  IN OUT char **contentType,
                  OUT int *contentLength,
                  OUT int *httpStatus,
                  IN int timeout)
{
	return http_OpenHttpGetProxy(
		url_str, NULL, Handle, contentType, contentLength, httpStatus,
		timeout);
}

int http_OpenHttpGetProxy(const char *url_str, const char *proxy_str,
	void **Handle, char **contentType, int *contentLength,
	int *httpStatus, int timeout)
{
	int ret_code;
	size_t sockaddr_len;
	int http_error_code;
	memptr ctype;
	SOCKET tcp_connection;
	membuffer request;
	http_get_handle_t *handle = NULL;
	uri_type url;
	uri_type proxy;
	uri_type *peer;
	parse_status_t status;

	if (!url_str || !Handle || !contentType || !httpStatus)
		return UPNP_E_INVALID_PARAM;
	*httpStatus = 0;
	*Handle = handle;
	*contentType = NULL;
	*contentLength = 0;
	ret_code = MakeGetMessage(url_str, proxy_str, &request, &url);
	if (ret_code != UPNP_E_SUCCESS)
		return ret_code;
	if (proxy_str) {
		ret_code =
		    http_FixStrUrl((char *)proxy_str, strlen(proxy_str),
				   &proxy);
		peer = &proxy;
	} else {
		peer = &url;
	}
	handle = osal_malloc(sizeof(http_get_handle_t));
	if (!handle)
		return UPNP_E_OUTOF_MEMORY;
	handle->cancel = 0;
	parser_response_init(&handle->response, HTTPMETHOD_GET);
	tcp_connection =
	    net_socket((int)peer->hostport.IPaddress.ss_family, SOCK_STREAM, 0);
	if (tcp_connection == INVALID_SOCKET) {
		ret_code = UPNP_E_SOCKET_ERROR;
		goto errorHandler;
	}
	if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
		sock_destroy(&handle->sock_info, SD_BOTH);
		ret_code = UPNP_E_SOCKET_ERROR;
		goto errorHandler;
	}
	sockaddr_len = peer->hostport.IPaddress.ss_family == AF_INET6 ?
	    sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
	ret_code = private_connect(handle->sock_info.socket,
		(struct sockaddr *)&(peer->hostport.IPaddress),
		(socklen_t) sockaddr_len);
	if (ret_code == -1) {
		sock_destroy(&handle->sock_info, SD_BOTH);
		ret_code = UPNP_E_SOCKET_CONNECT;
		goto errorHandler;
	}
	/* send request */
	ret_code = http_SendMessage(&handle->sock_info, &timeout, "b",
				    request.buf, request.length);
	if (ret_code) {
		sock_destroy(&handle->sock_info, SD_BOTH);
		goto errorHandler;
	}
	if (ReadResponseLineAndHeaders(&handle->sock_info,
				       &handle->response, &timeout,
				       &http_error_code) != (int)PARSE_OK) {
		ret_code = UPNP_E_BAD_RESPONSE;
		goto errorHandler;
	}
	status = parser_get_entity_read_method(&handle->response);
	switch (status) {
	case PARSE_CONTINUE_1:
	case PARSE_SUCCESS:
		break;
	default:
		ret_code = UPNP_E_BAD_RESPONSE;
		goto errorHandler;
	}
	*httpStatus = handle->response.msg.status_code;
	ret_code = UPNP_E_SUCCESS;
	if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
		/* no content-type */
		*contentType = NULL;
	else
		*contentType = ctype.buf;
	if (handle->response.position == (parser_pos_t)POS_COMPLETE)
		*contentLength = 0;
	else if (handle->response.ent_position == ENTREAD_USING_CHUNKED)
		*contentLength = UPNP_USING_CHUNKED;
	else if (handle->response.ent_position == ENTREAD_USING_CLEN)
		*contentLength = (int)handle->response.content_length;
	else if (handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
		*contentLength = UPNP_UNTIL_CLOSE;

 errorHandler:
	*Handle = handle;
	membuffer_destroy(&request);
	if (ret_code != UPNP_E_SUCCESS)
		httpmsg_destroy(&handle->response.msg);
	return ret_code;
}

/************************************************************************
 * Function: http_SendStatusResponse
 *
 * Parameters:
 *	IN SOCKINFO *info;		Socket information object
 *	IN int http_status_code;	error code returned while making 
 *					or sending the response message
 *	IN int request_major_version;	request major version
 *	IN int request_minor_version;	request minor version
 *
 * Description:
 *	Generate a response message for the status query and send the
 *	status response.
 *
 * Return: int
 *	0 -- success
 *	UPNP_E_OUTOF_MEMORY
 *	UPNP_E_SOCKET_WRITE
 *	UPNP_E_TIMEDOUT
 ************************************************************************/
int http_SendStatusResponse(IN SOCKINFO *info, IN int http_status_code,
	IN int request_major_version, IN int request_minor_version)
{
	int response_major, response_minor;
	membuffer membuf;
	int ret;
	int timeout;

	http_CalcResponseVersion(request_major_version, request_minor_version,
				 &response_major, &response_minor);
	membuffer_init(&membuf);
	membuf.size_inc = (size_t)70;
	/* response start line */
	ret = http_MakeMessage(&membuf, response_major, response_minor, "RSCB",
			       http_status_code, http_status_code);
	if (ret == 0) {
		timeout = HTTP_DEFAULT_TIMEOUT;
		ret = http_SendMessage(info, &timeout, "b",
		       membuf.buf, membuf.length);
	}
	membuffer_destroy(&membuf);

	return ret;
}

#define TEMPBUF_SIZE 200
int http_MakeMessage(membuffer *buf, int http_major_version,
	int http_minor_version, const char *fmt, ...)
{
	char c;
	char *s = NULL;
	size_t num;
	off_t bignum;
	size_t length;
	time_t *loc_time;
	time_t curr_time;
	struct tm date_storage;
	struct tm *date;
	const char *start_str;
	const char *end_str;
	int status_code;
	const char *status_msg;
	http_method_t method;
	const char *method_str;
	const char *url_str;
	const char *temp_str;
	uri_type url;
	uri_type *uri_ptr;
	int error_code = 0;
	va_list argp;
	char *tempbuf = osal_zmalloc(TEMPBUF_SIZE);  // char tempbuf[200];
	const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
	const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
	    "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
	int rc = 0;

	// memset(tempbuf, 0, sizeof(tempbuf));
	va_start(argp, fmt);
	while ((c = *fmt++) != 0) {
		if (c == 's') {
			/* C string */
			s = (char *)va_arg(argp, char *);
			assert(s);
			UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
				   "Adding a string : %s\n", s);
			if (membuffer_append(buf, s, strlen(s)))
				goto error_handler;
		} else if (c == 'K') {
			/* Add Chunky header */
			if (membuffer_append(buf, "TRANSFER-ENCODING: chunked\r\n",
				strlen("Transfer-Encoding: chunked\r\n")))
				goto error_handler;
		} else if (c == 'G') {
			/* Add Range header */
			struct SendInstruction *RespInstr;
			RespInstr = (struct SendInstruction *)
			    va_arg(argp, struct SendInstruction *);
			assert(RespInstr);
			/* connection header */
			if (membuffer_append(buf, RespInstr->RangeHeader,
				strlen(RespInstr->RangeHeader)))
				goto error_handler;
		} else if (c == 'b') {
			/* mem buffer */
			s = (char *)va_arg(argp, char *);
			UpnpPrintf(UPNP_ALL, HTTP, __FILE__, __LINE__,
				"Adding a char Buffer starting with: %c\n", (int)s[0]);
			assert(s);
			length = (size_t) va_arg(argp, size_t);
			if (membuffer_append(buf, s, length))
				goto error_handler;
		} else if (c == 'c') {
			/* crlf */
			if (membuffer_append(buf, "\r\n", (size_t)2))
				goto error_handler;
		} else if (c == 'd') {
			/* integer */
			num = (size_t)va_arg(argp, int);
			rc = snprintf(tempbuf, TEMPBUF_SIZE, "%" PRIzu, num);
			if (rc < 0 || (unsigned int) rc >= TEMPBUF_SIZE ||
				membuffer_append(buf, tempbuf, strlen(tempbuf)))
				goto error_handler;
		} else if (c == 'h') {
			/* off_t */
			bignum = (off_t) va_arg(argp, off_t);
			rc = snprintf(tempbuf, TEMPBUF_SIZE, "%" PRId64,
				(int64_t) bignum);
			if (rc < 0 || (unsigned int) rc >= TEMPBUF_SIZE ||
				membuffer_append(buf, tempbuf, strlen(tempbuf)))
				goto error_handler;
		} else if (c == 't' || c == 'D') {
			/* date */
			if (c == 'D') {
				/* header */
				start_str = "DATE: ";
				end_str = "\r\n";
				curr_time = time(NULL);
				loc_time = &curr_time;
			} else {
				/* date value only */
				start_str = end_str = "";
				loc_time = (time_t *)va_arg(argp, time_t *);
			}
			assert(loc_time);
			date = http_gmtime_r(loc_time, &date_storage);
			if (date == NULL)
				goto error_handler;
			rc = snprintf(tempbuf, TEMPBUF_SIZE,
				"%s%s, %02d %s %d %02d:%02d:%02d GMT%s",
				start_str, &weekday_str[date->tm_wday * 4],
				date->tm_mday, &month_str[date->tm_mon * 4],
				date->tm_year + 1900, date->tm_hour,
				date->tm_min, date->tm_sec, end_str);
			if (rc < 0 || (unsigned int) rc >= TEMPBUF_SIZE ||
				membuffer_append(buf, tempbuf, strlen(tempbuf)))
				goto error_handler;
		} else if (c == 'L') {
			/* Add CONTENT-LANGUAGE header only if WEB_SERVER_CONTENT_LANGUAGE */
			/* is not empty and if Accept-Language header is not empty */
			struct SendInstruction *RespInstr;
			RespInstr = (struct SendInstruction *)
			    va_arg(argp, struct SendInstruction *);
			assert(RespInstr);
			if (strcmp(RespInstr->AcceptLanguageHeader, "") &&
			    strcmp(WEB_SERVER_CONTENT_LANGUAGE, "") &&
			    http_MakeMessage(buf, http_major_version,
					http_minor_version, "ssc",
					"CONTENT-LANGUAGE: ",
					WEB_SERVER_CONTENT_LANGUAGE) != 0)
				goto error_handler;
		} else if (c == 'C') {
			if ((http_major_version > 1) ||
			    (http_major_version == 1 && http_minor_version == 1)
			    ) {
				/* connection header */
				if (membuffer_append_str(buf, "CONNECTION: close\r\n"))
					goto error_handler;
			}
		} else if (c == 'N') {
			/* content-length header */
			bignum = (off_t) va_arg(argp, off_t);
			assert(bignum >= 0);
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "shc", "CONTENT-LENGTH: ", bignum) != 0)
				goto error_handler;
		} else if (c == 'S' || c == 'U') {
			/* SERVER or USER-AGENT header */
			temp_str = (c == 'S') ? "SERVER: " : "USER-AGENT: ";
			get_sdk_info(tempbuf, TEMPBUF_SIZE);
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "ss", temp_str, tempbuf) != 0)
				goto error_handler;
		} else if (c == 'X') {
			/* C string */
			s = (char *)va_arg(argp, char *);
			assert(s);
			if (membuffer_append_str(buf, "X-User-Agent: ") != 0)
				goto error_handler;
			if (membuffer_append(buf, s, strlen(s)) != 0)
				goto error_handler;
		} else if (c == 'R') {
			/* HTTP response start line */
			/*   e.g.: 'HTTP/1.1 200 OK' code */
			status_code = (int)va_arg(argp, int);
			assert(status_code > 0);
			rc = snprintf(tempbuf, TEMPBUF_SIZE, "HTTP/%d.%d %d ",
				http_major_version, http_minor_version,
				status_code);
			/* str */
			status_msg = http_get_code_text(status_code);
			if (rc < 0 || (unsigned int) rc >= TEMPBUF_SIZE ||
				http_MakeMessage(buf, http_major_version, http_minor_version,
					     "ssc", tempbuf, status_msg) != 0)
				goto error_handler;
		} else if (c == 'r') {
			/* RTSP response start line */
			/*   e.g.: 'RTSP/1.0 200 OK' code */
			status_code = (int)va_arg(argp, int);
			assert(status_code > 0);
			rc = snprintf(tempbuf, TEMPBUF_SIZE, "RTSP/%d.%d %d ",
				http_major_version, http_minor_version,
				status_code);
			/* str */
			status_msg = (char *)va_arg(argp, char *);
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "ssc", tempbuf, status_msg)!= 0) {
				goto error_handler;
			}
		} else if (c == 'B') {
			/* body of a simple reply */
			status_code = (int)va_arg(argp, int);
			rc = snprintf(tempbuf, TEMPBUF_SIZE, "%s%d %s%s",
				"<html><body><h1>",
				status_code, http_get_code_text(status_code),
				"</h1></body></html>");
			if (rc < 0 || (unsigned int) rc >= TEMPBUF_SIZE)
				goto error_handler;
			bignum = (off_t)strlen(tempbuf);
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "NTcs", bignum,	/* content-length */
					     "text/html",	/* content-type */
					     tempbuf) != 0	/* body */
			    )
				goto error_handler;
		} else if (c == 'Q') {
			/* request start line */
			/* GET /foo/bar.html HTTP/1.1\r\n */
			//method = (http_method_t) va_arg(argp, http_method_t);
			method = (http_method_t) va_arg(argp, int);
			method_str = method_to_str(method);
			url_str = (const char *)va_arg(argp, const char *);
			num = (size_t) va_arg(argp, size_t);		/* length of url_str */
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "ssbsdsdc", method_str,	/* method */
					     " ", url_str, num,		/* url */
					     " HTTP/", http_major_version, ".",
					     http_minor_version) != 0)
				goto error_handler;
		} else if (c == 'q') {
			/* request start line and HOST header */
			//method = (http_method_t) va_arg(argp, http_method_t);
			method = (http_method_t) va_arg(argp, int);
			uri_ptr = (uri_type *) va_arg(argp, uri_type *);
			assert(uri_ptr);
			if (http_FixUrl(uri_ptr, &url) != 0) {
				error_code = UPNP_E_INVALID_URL;
				goto error_handler;
			}
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					"Q" "sbc", method, url.pathquery.buff,
					url.pathquery.size, "HOST: ",
					url.hostport.text.buff,
					url.hostport.text.size) != 0)
				goto error_handler;
		} else if (c == 'T') {
			/* content type header */
			temp_str = (const char *)va_arg(argp, const char *);	/* type/subtype format */
			if (http_MakeMessage(buf, http_major_version, http_minor_version,
					     "ssc", "CONTENT-TYPE: ", temp_str) != 0)
				goto error_handler;
		} else {
			assert(0);
		}
	}
	goto ExitFunction;

error_handler:
	/* Default is out of memory error. */
	if (!error_code)
		error_code = UPNP_E_OUTOF_MEMORY;
	membuffer_destroy(buf);

ExitFunction:
	osal_free(tempbuf);
	va_end(argp);
	return error_code;
}


/************************************************************************
 * Function: http_CalcResponseVersion
 *
 * Parameters:
 *	IN int request_major_vers;	Request major version
 *	IN int request_minor_vers;	Request minor version
 *	OUT int* response_major_vers;	Response mojor version
 *	OUT int* response_minor_vers;	Response minor version
 *
 * Description:
 *	Calculate HTTP response versions based on the request versions.
 *
 * Return: void
 ************************************************************************/
void http_CalcResponseVersion( IN int request_major_vers,
                          IN int request_minor_vers,
                          OUT int *response_major_vers,
                          OUT int *response_minor_vers)
{
	if ((request_major_vers > 1) ||
	    (request_major_vers == 1 && request_minor_vers >= 1)) {
		*response_major_vers = 1;
		*response_minor_vers = 1;
	} else {
		*response_major_vers = request_major_vers;
		*response_minor_vers = request_minor_vers;
	}
}

/************************************************************************
* Function: MakeGetMessageEx
*
* Parameters:
*	const char *url_str;	String as a URL
*	membuffer *request;	Buffer containing the request
*	uri_type *url; 		URI object containing the scheme, path
*				query token, etc.
*
* Description:
*	Makes the message for the HTTP GET method
*
* Returns:
*	UPNP_E_INVALID_URL
* 	Error Codes returned by http_MakeMessage
*	UPNP_E_SUCCESS
************************************************************************/
int MakeGetMessageEx( const char *url_str,
                  membuffer * request,
                  uri_type * url,
                  struct SendInstruction *pRangeSpecifier)
{
	int errCode = UPNP_E_SUCCESS;
	char *urlPath = NULL;
	size_t hostlen = (size_t)0;
	char *hoststr, *temp;

	do {
		UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
			   "DOWNLOAD URL : %s\n", url_str);
		if ((errCode = http_FixStrUrl((char *)url_str,
					      strlen(url_str),
					      url)) != UPNP_E_SUCCESS) {
			break;
		}
		/* make msg */
		membuffer_init(request);
		urlPath = osal_malloc(strlen(url_str) + (size_t)1); // alloca
		if (!urlPath) {
			errCode = UPNP_E_OUTOF_MEMORY;
			break;
		}
		memset(urlPath, 0, strlen(url_str) + (size_t)1);
		strncpy(urlPath, url_str, strlen(url_str));
		hoststr = strstr(urlPath, "//");
		if (hoststr == NULL) {
			errCode = UPNP_E_INVALID_URL;
			break;
		}
		hoststr += 2;
		temp = strchr(hoststr, '/');
		if (temp == NULL) {
			errCode = UPNP_E_INVALID_URL;
			break;
		}
		*temp = '\0';
		hostlen = strlen(hoststr);
		*temp = '/';
		UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
			   "HOSTNAME : %s Length : %" PRIzu "\n",
			   hoststr, hostlen);
		errCode = http_MakeMessage(request, 1, 1,
					   "Q" "s" "bc" "GDCUc",
					   HTTPMETHOD_GET, url->pathquery.buff,
					   url->pathquery.size, "HOST: ",
					   hoststr, hostlen, pRangeSpecifier);
		if (errCode != 0) {
			UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
				   "HTTP Makemessage failed\n");
			membuffer_destroy(request);
			goto L_exit;
		}
	} while (0);
	UpnpPrintf(UPNP_INFO, HTTP, __FILE__, __LINE__,
		   "HTTP Buffer:\n%s\n" "----------END--------\n",
		   request->buf);

L_exit:
	if (urlPath) osal_free(urlPath);
	return errCode;
}

#define SIZE_RANGE_BUFFER 50

/************************************************************************
 * Function: http_OpenHttpGetEx
 *
 * Parameters:
 *	IN const char *url_str;		String as a URL
 *	IN OUT void **Handle;		Pointer to buffer to store HTTP
 *					post handle
 *	IN OUT char **contentType;	Type of content
 *	OUT int *contentLength;		length of content
 *	OUT int *httpStatus;		HTTP status returned on receiving a
 *					response message
 *	IN int timeout;			time out value
 *
 * Description:
 *	Makes the HTTP GET message, connects to the peer, 
 *	sends the HTTP GET request, gets the response and parses the 
 *	response.
 *
 * Return: int
 *	UPNP_E_SUCCESS		- On Success
 *	UPNP_E_INVALID_PARAM	- Invalid Paramters
 *	UPNP_E_OUTOF_MEMORY
 *	UPNP_E_SOCKET_ERROR
 *	UPNP_E_BAD_RESPONSE
 ************************************************************************/
int http_OpenHttpGetEx(
	IN const char *url_str,
	IN OUT void **Handle,
	IN OUT char **contentType,
	OUT int *contentLength,
	OUT int *httpStatus,
	IN int lowRange,
	IN int highRange,
	IN int timeout)
{
	int http_error_code;
	memptr ctype;
	SOCKET tcp_connection;
	size_t sockaddr_len;
	membuffer request;
	http_get_handle_t *handle = NULL;
	uri_type url;
	parse_status_t status;
	int errCode = UPNP_E_SUCCESS;
	/* char rangeBuf[SIZE_RANGE_BUFFER]; */
	struct SendInstruction *rangeBuf = osal_malloc( sizeof(struct SendInstruction) ); // struct SendInstruction rangeBuf;
	int rc = 0;

	membuffer_init(&request);

	do {
		/* Checking Input parameters */
		if (!url_str || !Handle || !contentType || !httpStatus ) {
			errCode = UPNP_E_INVALID_PARAM;
			break;
		}
		/* Initialize output parameters */
		*httpStatus = 0;
		*Handle = handle;
		*contentType = NULL;
		*contentLength = 0;
		if (lowRange > highRange) {
			errCode = UPNP_E_INTERNAL_ERROR;
			break;
		}
		memset(rangeBuf, 0, sizeof(struct SendInstruction));
		rc = snprintf(rangeBuf->RangeHeader, sizeof(rangeBuf->RangeHeader),
			"Range: bytes=%d-%d\r\n", lowRange, highRange);
		if (rc < 0 || (unsigned int) rc >= sizeof(rangeBuf->RangeHeader))
			break;
		membuffer_init(&request);
		errCode = MakeGetMessageEx(url_str, &request, &url, rangeBuf);
		if (errCode != UPNP_E_SUCCESS)
			break;
		handle = (http_get_handle_t *)osal_malloc(sizeof(http_get_handle_t));
		if (!handle) {
			errCode = UPNP_E_OUTOF_MEMORY;
			break;
		}
		memset(handle, 0, sizeof(*handle));
		parser_response_init(&handle->response, HTTPMETHOD_GET);
		tcp_connection = net_socket((int)url.hostport.IPaddress.ss_family,
			SOCK_STREAM, 0);
		if (tcp_connection == INVALID_SOCKET) {
			errCode = UPNP_E_SOCKET_ERROR;
			osal_free(handle);
			break;
		}
		if (sock_init(&handle->sock_info, tcp_connection) != UPNP_E_SUCCESS) {
			sock_destroy(&handle->sock_info, SD_BOTH);
			errCode = UPNP_E_SOCKET_ERROR;
			osal_free(handle);
			break;
		}
		sockaddr_len = url.hostport.IPaddress.ss_family == AF_INET6 ?
			sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
		errCode  = private_connect(handle->sock_info.socket,
			(struct sockaddr *)&(url.hostport.IPaddress),
			(socklen_t)sockaddr_len);
		if (errCode == -1) {
			sock_destroy(&handle->sock_info, SD_BOTH);
			errCode = UPNP_E_SOCKET_CONNECT;
			osal_free(handle);
			break;
		}
		/* send request */
		errCode = http_SendMessage(&handle->sock_info, &timeout,
			"b", request.buf, request.length);
		if (errCode != UPNP_E_SUCCESS) {
			sock_destroy(&handle->sock_info, SD_BOTH);
			osal_free(handle);
			break;
		}
		if (ReadResponseLineAndHeaders(&handle->sock_info,
			&handle->response, &timeout, &http_error_code) != (int)PARSE_OK) {
			errCode = UPNP_E_BAD_RESPONSE;
			osal_free(handle);
			break;
		}
		status = parser_get_entity_read_method(&handle->response);
		if (status != (parse_status_t)PARSE_CONTINUE_1 &&
			status != (parse_status_t)PARSE_SUCCESS) {
			errCode = UPNP_E_BAD_RESPONSE;
			osal_free(handle);
			break;
		}
		*httpStatus = handle->response.msg.status_code;
		errCode = UPNP_E_SUCCESS;

		if (!httpmsg_find_hdr(&handle->response.msg, HDR_CONTENT_TYPE, &ctype))
			/* no content-type */
			*contentType = NULL;
		else
			*contentType = ctype.buf;
		if (handle->response.position == (parser_pos_t)POS_COMPLETE)
			*contentLength = 0;
		else if(handle->response.ent_position == ENTREAD_USING_CHUNKED)
			*contentLength = UPNP_USING_CHUNKED;
		else if(handle->response.ent_position == ENTREAD_USING_CLEN)
			*contentLength = (int)handle->response.content_length;
		else if(handle->response.ent_position == ENTREAD_UNTIL_CLOSE)
			*contentLength = UPNP_UNTIL_CLOSE;
		*Handle = handle;
	} while (0);

	osal_free( rangeBuf );
	membuffer_destroy(&request);

	return errCode;
}


/************************************************************************
 * Function: get_sdk_info
 *
 * Parameters:
 *	OUT char *info;	buffer to store the operating system information
 *	IN size_t infoSize; size of buffer
 *
 * Description:
 *	Returns the server information for the operating system
 *
 * Return:
 *	UPNP_INLINE void
 ************************************************************************/
/* 'info' should have a size of at least 100 bytes */
void get_sdk_info(OUT char *info, IN size_t infoSize)
{
#ifdef __AMN_OS_LINUX__
#ifdef UPNP_ENABLE_UNSPECIFIED_SERVER
	snprintf(info, infoSize, "Unspecified, UPnP/1.0, Unspecified\r\n");
#else /* UPNP_ENABLE_UNSPECIFIED_SERVER */
#ifdef WIN32
	OSVERSIONINFO versioninfo;
	versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	if (GetVersionEx(&versioninfo) != 0)
		snprintf(info, infoSize,
			"%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/"
			PACKAGE_VERSION "\r\n", versioninfo.dwMajorVersion,
			versioninfo.dwMinorVersion, versioninfo.dwBuildNumber,
			versioninfo.dwPlatformId, versioninfo.szCSDVersion);
	else
		*info = '\0';
#else
	int ret_code;
	struct utsname sys_info;

	ret_code = uname(&sys_info);
	if (ret_code == -1)
		*info = '\0';
	snprintf(info, infoSize,
		"%s/%s, UPnP/1.0, Portable SDK for UPnP devices/"
		PACKAGE_VERSION "\r\n", sys_info.sysname, sys_info.release);
#endif
#endif /* UPNP_ENABLE_UNSPECIFIED_SERVER */
#else // __AMN_OS_LINUX__
#ifdef HTTP_CUSTOMER_HEADER
	snprintf(info, infoSize, HTTP_CUSTOMER_HEADER"\r\n");
#else
	snprintf(info, infoSize, "AIT Multimedia Network Solution, UPnP/1.0 devices/"PACKAGE_VERSION"\r\n");
#endif
#endif
}

