#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <syslog.h>

#define LOGS "/mnt/flash/logs/usr1/curlTestApp.log"

#define TRUE 1
#define FALSE 0

# define SUCCESS 0
# define FAILURE -1


#define CA_CERT_FILE	"ca-bundle.crt"

FILE*		fptr			= NULL;
/*
 * ----------------------------------------------------------------------------
 * Some certificate information:
 *
 * PEM Format: .pem, .crt, .cer and .key
 *
 * DER Format:
 * ----------------------------------------------------------------------------
 */
static struct curl_slist *	headers			= NULL;
	char		szMsg[256]		= "";


typedef struct
{
	int		iTotSize;
	int		iWritten;
	char *	szMsg;
}
RESP_DATA_STYPE, * RESP_DATA_PTYPE;

static int initSSICURLHandle(CURL **, char*);
static void commonInit(CURL *, int);
static int sendDataToHost(CURL *, char *, int, char **, int *);
static size_t saveResponse(void *, size_t, size_t, void *);
static int initializeRespData(RESP_DATA_PTYPE);
								
int main(int argc, char *argv[])
{
 	int			rv				= SUCCESS;
	int			iCnt			= 0;
	char*		szHostUrl		= "https://xxxxxxxxxxx(url here)";
	CURL *		curlHandle		= NULL;
	char		req[256]		= "Sample Text";
	char		reqSize			= strlen(req);
	char*		resp			= NULL;
	int			respSize		= 0;
	
	
	fptr = fopen (LOGS, "w");
	if(fptr == NULL)
	{
		syslog (LOG_ERR, "Cannot open file \n");
		return -1;
	}
	sprintf(szMsg, "%s: Entered..\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
	
	resp = (char*)malloc(256);
	if (resp == NULL)
	{
		fprintf(fptr,"Malloc Failed\n");
		return -1;
	}
	
	
	while(iCnt < 2)
	{
		rv = initSSICURLHandle(&curlHandle, szHostUrl);

		if(rv == SUCCESS)
		{
			/*
			 * CURL Handler was initialized successfully
			 */
			rv = sendDataToHost(curlHandle, req, reqSize, &resp, &respSize);
			if(rv != SUCCESS)
			{
				sprintf(szMsg, "%s: Failed to post data to server\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
				 
			}
		}

		curl_easy_cleanup(curlHandle);

		if(rv == SUCCESS)
		{
			break;
		}

	
		iCnt++;

		sprintf(szMsg, "%s: Trying again..\n");
				syslog(LOG_USER, szMsg);
	}
	
	if (resp != NULL)
	{
		fprintf (fptr, "Response Received: %s \n", resp);
		free(resp);
	}
	
	sprintf(szMsg, "%s: Returning..\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
	fclose(fptr);
	return rv;
}

/*
 * ============================================================================
 * Function Name: initSSICURLHandle
 *
 * Description	:
 *
 * Input Params	:
 *
 * Output Params: SUCCESS / FAILURE
 * ============================================================================
 */
static int initSSICURLHandle(CURL ** handle, char* pszHostUrl)
{
	int					rv				= SUCCESS;
	char				szTmpURL[100]	= "";
	CURL *				locHandle		= NULL;
	int					certValdReq 	= TRUE;

	sprintf(szMsg,   "%s: --- enter ---\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
	 

	while(1)
	{
		/* Initialize the handle using the CURL library */
		locHandle = curl_easy_init();
		if(locHandle == NULL)
		{
			sprintf(szMsg,   "%s: Curl initialization for handle failed\n",
															__FUNCTION__);
				syslog(LOG_USER, szMsg);

			rv = FAILURE;
			break;
		}


		curl_easy_setopt(locHandle, CURLOPT_URL, pszHostUrl);

		sprintf(szMsg,   "%s: Setting URL = [%s]\n", __FUNCTION__, pszHostUrl);
				syslog(LOG_USER, szMsg);

		/* Set the connection timeout */
		curl_easy_setopt(locHandle, CURLOPT_CONNECTTIMEOUT, 10);

		/* Set the total timeout */

		curl_easy_setopt(locHandle, CURLOPT_TIMEOUT, 20);


		/* Set the other common features */
		commonInit(locHandle, certValdReq);
		*handle = locHandle;

		break;
	}

	sprintf(szMsg,   "%s: Returning [%d]\n", __FUNCTION__, rv);
				syslog(LOG_USER, szMsg);

	return rv;
}

/*
 * ============================================================================
 * Function Name: commonInit
 *
 * Description	: This function contains the code for common initialization of
 * 					all the three handles needed for communication with the PWC
 *
 * Input Params	: CURL * handle	-> curl handle that needs to be configured
 *
 * Output Params: void
 * ============================================================================
 */
static void commonInit(CURL * handle, int certValdReq)
{
	
	sprintf(szMsg,   "%s: --- enter ---\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);

	/* Set the NO SIGNAL option, This option is very important to set in case
	 * of multi-threaded applications like ours, because otherwise the libcurl
	 * uses signal handling for the communication and that causes the threads
	 * to go crazy and even crash. Fix done for issue 1659 (Church of LDS AmDocs
	 * Case# 130826-3983) */
	curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);

	/* Set the HTTP post option */
	curl_easy_setopt(handle, CURLOPT_POST, 1L);

	/* Add the headers */
	curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);

/*#ifdef DEBUG
	/* Add the debug function 
	curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curlDbgFunc);
#endif*/

	if (certValdReq == TRUE)
	{
		sprintf(szMsg,  "%s: Url starts with https. Doing certificate validation\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
				
		curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1L);
		curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 2L);
	}

	/* Set the CA certificate */
	curl_easy_setopt(handle, CURLOPT_CAINFO, CA_CERT_FILE);

	/* Set the write function */
	curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, saveResponse);

	/* Set the detection as TRUE for HTTP errors */
	curl_easy_setopt(handle, CURLOPT_FAILONERROR, TRUE);

	sprintf(szMsg,   "%s: --- returning ---\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
	return;
}

/*
 * ============================================================================
 * Function Name: sendDataToHost
 *
 * Description	: This function sends the XML data to the PWC server, using the
 * 					appropriate CURL handle.
 *
 * Input Params	:
 * 				PWC_URL_TYPE_ENUM urlType -> constant telling which handle to
 * 												use
 * 				unsigned char *	  data	  -> the XML data that needs to be sent
 * 				int				  size	  -> size of the data
 *
 * Output Params: SUCCESS / FAILURE
 * ============================================================================
 */
static int sendDataToHost(CURL * curlHandle, char * req, int reqSize,
												char ** resp, int * respSize)
{
	int				rv				   = SUCCESS;
	char			szCurlErrBuf[4096] = "";
	RESP_DATA_STYPE	stRespData;

#ifdef DEVDEBUG
	char			szDbgMsg[4096+256]	= "";
#elif DEBUG
	char			szDbgMsg[256]	= "";
#endif

	sprintf(szMsg,   "%s: --- enter ---\n", __FUNCTION__);
	syslog(LOG_USER, szMsg);
	 

	while(1)
	{
		/* Initialize the response data */
		rv = initializeRespData(&stRespData);
		if(rv != SUCCESS)
		{
			sprintf(szMsg,   "%s: Initialization of response data FAILED\n"
																, __FUNCTION__);
			syslog(LOG_USER, szMsg);
			 

			break;
		}

		/* Set some curl library options before sending the data */
		curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &stRespData);
		curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, (void *) req);
		curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE, reqSize);
		curl_easy_setopt(curlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf);

		sprintf(szMsg,   "%s: Posting the data to the server\n", __FUNCTION__);
		syslog(LOG_USER, szMsg);
		 

		/* post the data to the server */
		rv = curl_easy_perform(curlHandle);

		sprintf(szMsg,   "%s: curl_easy_perform done, rv = %d\n", __FUNCTION__, rv);
				syslog(LOG_USER, szMsg);
		 

		if(rv == CURLE_OK)
		{
			*resp = stRespData.szMsg;
			*respSize = stRespData.iWritten;
			
			sprintf(szMsg, "Data Posted Succesfully\n");
				syslog(LOG_USER, szMsg);
			sprintf(szMsg, "%s: Response Len = [%d]\n", __FUNCTION__,
														stRespData.iWritten);
				syslog(LOG_USER, szMsg);
			 
		}
		else
		{
			switch(rv)
			{
			case CURLE_UNSUPPORTED_PROTOCOL: /* 1 */
				/*
				 * The URL you passed to libcurl used a protocol that this libcurl does not support.
				 * The support might be a compile-time option that you didn't use,
				 * it can be a misspelled protocol string or just a protocol libcurl has no code for.
				 */
				sprintf(szMsg,   "%s: protocol not supported by library\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_URL_MALFORMAT: /* 3 */
				/*
				 * The URL was not properly formatted.
				 */
				sprintf(szMsg,   "%s: URL not properly formatted\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_COULDNT_RESOLVE_HOST: /* 6 */
				/*
				 * Couldn't resolve host. The given remote host was not resolved.
				 */
				sprintf(szMsg,   "%s: Couldnt resolve server's address\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_COULDNT_CONNECT: /* 7 */
				/*
				 * Failed to connect() to host or proxy.
				 */
				sprintf(szMsg,   "%s: Failed to connect server\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_HTTP_RETURNED_ERROR: /* 22 */
				/*
				 * This is returned if CURLOPT_FAILONERROR is set TRUE
				 * and the HTTP server returns an error code that is >= 400.
				 */
				sprintf(szMsg,   "%s: Got HTTP error while posting\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_WRITE_ERROR: /* 23 */
				/*
				 * An error occurred when writing received data to a local file,
				 * or an error was returned to libcurl from a write callback.
				 */
				sprintf(szMsg,   "%s: Failed to save the response data\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				 

				rv = FAILURE;
				break;

			case CURLE_OUT_OF_MEMORY: /* 27 */
				/*
				 * A memory allocation request failed.
				 * This is serious badness and things are severely screwed up if this ever occurs.
				 */
				sprintf(szMsg,   "%s: Facing memory shortage \n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				 

				rv = FAILURE;
				break;

			case CURLE_OPERATION_TIMEDOUT: /* 28 */
				/*
				 * Operation timeout.
				 * The specified time-out period was reached according to the conditions.
				 */
				sprintf(szMsg,   "%s: Timeout happened while receiving\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_SSL_CONNECT_ERROR: /* 35 */
				/*
				 * A problem occurred somewhere in the SSL/TLS handshake.
				 *
				 */
				sprintf(szMsg,   "%s: SSL/TLS Handshake FAILED\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_PEER_FAILED_VERIFICATION: /* 51 */
				/*
				 * The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK.
				 */
				sprintf(szMsg, 	"%s: Server's SSL certificate verification FAILED",
							__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_SEND_ERROR: /* 55 */
				/*
				 * Failed sending network data.
				 */
				sprintf(szMsg,   "%s: Failed to post the request data\n", __FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_RECV_ERROR: /* 56 */
				/*
				 * Failure with receiving network data.
				 */
				sprintf(szMsg,   "%s: Failed to receive the response\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_SSL_CACERT: /* 60 */
				/*
				 * Peer certificate cannot be authenticated with known CA certificates.
				 */
				sprintf(szMsg,  
								"%s: Couldnt authenticate peer certificate\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				break;

			case CURLE_SSL_CACERT_BADFILE: /* 77 */
				/*
				 * Problem with reading the SSL CA cert (path? access rights?)
				 */
				sprintf(szMsg,   "%s: Cant find SSL CA sertificate [%s]\n",
												__FUNCTION__, CA_CERT_FILE);
				syslog(LOG_USER, szMsg);
				break;

			default:
				sprintf(szMsg,   "%s: Error [%d] occured while posting\n",
															__FUNCTION__, rv);
				syslog(LOG_USER, szMsg);
				break;
			}
			
			rv = FAILURE;
			
			/* Deallocate the allocated memory */
			if(stRespData.szMsg != NULL)
			{
				free(stRespData.szMsg);
				stRespData.szMsg = NULL;
			}

		
			sprintf(szMsg,   "%s: Lib Curl Error [%s]\n",__FUNCTION__, szCurlErrBuf);
			syslog(LOG_USER, szMsg);
			


		}

		break;
	}

	sprintf(szMsg, "%s: Returning [%d]\n", __FUNCTION__, rv);
	syslog(LOG_USER, szMsg);

	return rv;
}

/*
 * ============================================================================
 * Function Name: saveResponse
 *
 * Description	: This function performs the job of saving the XML response
 * 					coming from the server into a buffer provided as parameter.
 * 					This function is passed to the CURL library, and the
 * 					library would call this function, not us.
 *
 * Input Params	:
 * 					void * ptr -> pointer to the data that needs to be copied
 * 					size_t size-> size of one element of data
 * 					size_t cnt -> count of data members
 * 					void * des -> our buffer where the response is saved
 *
 * Output Params: totLen -> length of data saved
 * ============================================================================
 */
static size_t saveResponse(void * ptr, size_t size, size_t cnt, void * des)
{
	int				rv				= SUCCESS;
	int				iSizeLeft		= 0;
	int				totLen			= 0;
	int				iReqdLen		= 0;
	char *			cTmpPtr			= NULL;
	RESP_DATA_PTYPE	respPtr			= NULL;


	sprintf(szMsg,   "%s: --- enter ---\n", __FUNCTION__);
	syslog(LOG_USER, szMsg);
	 

	while(1)
	{

		respPtr = (RESP_DATA_PTYPE) des;
		totLen = size * cnt;
		iSizeLeft = respPtr->iTotSize - respPtr->iWritten;

		if(totLen > iSizeLeft)
		{
			iReqdLen = (respPtr->iTotSize + totLen - iSizeLeft) + 1/* one extra buffer for NULL at the end for safety purpose*/;

			cTmpPtr = (char *) realloc(respPtr->szMsg, iReqdLen);
			if(cTmpPtr == NULL)
			{
				sprintf(szMsg,   "%s: Reallocation of memory FAILED\n",
																__FUNCTION__);
				syslog(LOG_USER, szMsg);
				 

				rv = FAILURE;
				break;
			}

			memset(cTmpPtr + respPtr->iWritten, 0x00, iReqdLen - respPtr->iWritten);

			respPtr->iTotSize = iReqdLen;
			respPtr->szMsg = cTmpPtr;
		}

		memcpy(respPtr->szMsg + respPtr->iWritten, ptr, totLen);
		respPtr->iWritten += totLen;

		rv = totLen;

		break;
	}

	sprintf(szMsg,   "%s: Returning [%d]\n", __FUNCTION__, rv);
	 
	syslog(LOG_USER, szMsg);

	return rv;
}

/*
 * ============================================================================
 * Function Name: initializeRespData
 *
 * Description	: This function is used for the initial setting of the response
 * 					data structure which would then be used for storing XML
 * 					response retrieved from the server being contacted.
 *
 * Input Params	: RESP_DATA_PTYPE stRespPtr -> pointer to response structure
 *
 * Output Params: SUCCESS / FAILURE
 * ============================================================================
 */
static int initializeRespData(RESP_DATA_PTYPE stRespPtr)
{
	int		rv				= SUCCESS;
	char *	cTmpPtr			= NULL;
	
#ifdef DEBUG
	char	szDbgMsg[128]	= "";
#endif

	sprintf(szMsg,  "%s: --- enter ---\n", __FUNCTION__);
	syslog(LOG_USER, szMsg);

	cTmpPtr = (char *) malloc(4096 * sizeof(char));
	if(cTmpPtr != NULL)
	{
		/* Initialize the memory */
		memset(cTmpPtr, 0x00, 4096 * sizeof(char));
		memset(stRespPtr, 0x00, sizeof(RESP_DATA_STYPE));

		/* Assign the data */
		stRespPtr->iTotSize = 4096;
		stRespPtr->iWritten = 0;
		stRespPtr->szMsg	= cTmpPtr;
	}
	else
	{
		sprintf(szMsg,  "%s: Memory allocation FAILED\n", __FUNCTION__);
	syslog(LOG_USER, szMsg);
		

		rv = FAILURE;
	}

	sprintf(szMsg,  "%s: Returning [%d]\n", __FUNCTION__, rv);
	
	syslog(LOG_USER, szMsg);

	return rv;
}

