/*
 * <insert one-line description of what the program does>
 * Copyright (c) <2008-2009>, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *

File Name:          TCEngine.c

Description:
    This file implements class TCEngine, DownloadCertFinishEventArgs.
    This file user for GUI get the user data boot tick boot count HD ID etc..
    set DownloadCertFinishEventArgs to tell agent the download finish!

Environment (opt):
    OS: Mandriva, Metasys
    SE: Mandriva, SUSE
 
Notes (opt):
****************************************************************************/

#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "Engine/TCEngine.h"
#include <pthread.h>
#include "GUI/GUI.h"
#include "Provision/Provision.h"

extern int tcEngine_DownloadCertFinish(DownloadCertFinishEventArgs *dcfEventArgs);

extern int g_AutoProvisionSucceed;
// Constructor
int TCEngine_Construct(TCEngine *this)
{
	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	this -> m_objTPMCtrl = NULL;
	this -> m_objTPMCtrl = malloc(sizeof(TPMCtrl));
	this -> m_expirationDate = NULL;
	this -> m_expirationDate = malloc(sizeof(DateTime));
	this -> m_bootCount = 0;
	this -> m_ConnectionStatus = NORMAL_CONNECTION;

	if(this -> m_objTPMCtrl == NULL)
	{
		WriteLog(TCEngine_ExceptionStatus_AllocTPMFailed);
		return TCEngine_ExceptionStatus_AllocTPMFailed;
	}
	else if(this -> m_expirationDate == NULL)
	{
		WriteLog(TCEngine_ExceptionStatus_AllocExpiryDateFailed);
		return TCEngine_ExceptionStatus_AllocExpiryDateFailed;
	}
    else
    {
		TPMCtrl_Construct(this -> m_objTPMCtrl);
        return SUCCESS;
    }
}		

// Download cert
// <param name="nCertKind">kind of cert</param>
// <returns>download cert success or fail</returns> 
int TCEngine_DownloadCert(TCEngine *this, int nCertKind)
{
	DownloadCertFinishEventArgs dcfEventArgs;
	BootCert	objBootCert;

	byte		*certData = NULL;
	int			iRet = SUCCESS;

	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}
	if(nCertKind < 1 || nCertKind > 4)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	if (g_AutoProvisionSucceed)
                        this->m_ConnectionStatus = NORMAL_CONNECTION;

	char serveraddr[200];
	memset(serveraddr,0,200);
	ServerInfo_ServerAddress(serveraddr, "GET");
	if (strlen(serveraddr)==0)
	{
		WritestrLog(MODULE_ENGINE, "Server address null, need to start listen broadcast address.");
		TCEngine_StartListen(this);
		return INFOMGMT_ERR_SERVERINFO;//blank server address
	}
	// Download cert
	DownloadCertFinishEventArgs_Construct(&dcfEventArgs);
	if(nCertKind==DOWNLOADKIND_PUSH)
	{
		iRet = ConnMgmt_DownloadSpecialBootCert(&objBootCert); 
	}
	else
	{
		iRet = ConnMgmt_DownloadBootCert(&objBootCert); 
	}
	if(iRet != SUCCESS)
	{		
		if ((iRet < ConnMgmt_ExceptionStatus_Success && iRet >= ConnMgmt_ExceptionStatus_ConnectFailure) 
			//(iRet <= ConnMgmt_SOAP_END && iRet >= ConnMgmt_SOAP_OK) || 
			|| iRet == ConnMgmt_SOAP_TCP_ERROR || iRet == ConnMgmt_SOAP_UDP_ERROR
			|| (iRet <= ConnMgmt_Http5_END && iRet >= ConnMgmt_Http2_START))
			TCEngine_StartListen(this);
		
		return iRet;
	}

	// Save cert kind to eventargs 
	dcfEventArgs.CertKind = objBootCert.CertKind; 
	this->m_ConnectionStatus =NORMAL_CONNECTION;
	
	// Save downlaod status to eventargs
	dcfEventArgs.DownloadStatus = iRet;
	UpdateUIbyStatus();
	switch(dcfEventArgs.CertKind)
	{
		// No Cert
		case NoCert:
			// Set return value
			iRet = TCEngine_ExceptionStatus_NoBootCert;
			WriteLog(iRet);
			break;

		// Get a normal cert
		case NormalCert:
			// Save cert to TPM
			certData = objBootCert.Cert;
//			this -> m_bootCount = -1;
            if(TPMCtrl_SaveProvisionPacket(certData, BOOTCERT_PACK_SIZE) == SUCCESS)
			{
				iRet = SUCCESS;
            }
			else
			{
				iRet = TCEngine_ExceptionStatus_WriteTPMFailed;
				WriteLog(iRet);
			}
			break;

		// Get a speical cert
		case SpecialCert:
			// Save cert to TPM
			certData = objBootCert.Cert;
//			this -> m_bootCount = -1;
			if(TPMCtrl_SaveProvisionPacket(certData, BOOTCERT_PACK_SIZE) == SUCCESS)
			{
	            // Send success to server
				//iRet = ConnMgmt_SendSpecialCertUpdateSuccess();

			}
			else
			{
				iRet = TCEngine_ExceptionStatus_WriteTPMFailed;
				WriteLog(iRet);
			}
			break;

		// Get a shared Secret
		case SharedSecret:
			// Save cert to TPM
			certData = objBootCert.Cert;
            if(TPMCtrl_SaveProvisionPacket(certData, SHAREDSECRET_PACK_SIZE) == SUCCESS)
			{
				// Send success to server
				//iRet = ConnMgmt_SendSharedSecretUpdateSuccess();
			}
            else
            {
                // Set return value
                // Throw write tpm fail exception 
                iRet = TCEngine_ExceptionStatus_WriteTPMFailed;
				WriteLog(iRet);
            }
			break;
		case ServerPublicKey:
		case IntelPublicKey:
			// Save cert to TPM
			certData = objBootCert.Cert;
//			this -> m_bootCount = -1;
            if(TPMCtrl_SaveProvisionPacket(certData, SERVERPUBKEY_HTTPS_PACK_SIZE) == SUCCESS)
			{
				iRet = SUCCESS;
            }
			else
			{
				iRet = TCEngine_ExceptionStatus_WriteTPMFailed;
				WriteLog(iRet);
			}
			break;
	}

	// Solicitation event
	TCEngine_OnDownloadCertFinish(&dcfEventArgs);
	return iRet;
}


int TCEngine_HTTPDownload(TCEngine *this, int nCertKind)
{
	DownloadCertFinishEventArgs dcfEventArgs;
	BootCert	objBootCert;

	byte		*certData = NULL;
	int			iRet = SUCCESS;
	unsigned char ServerPKCert[SERVERPUBKEY_HTTP_PACK_SIZE];
	unsigned char IntelPKCert[SERVERPUBKEY_HTTP_PACK_SIZE];
	Provision provision;
	
	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}
	if (g_AutoProvisionSucceed)
                        this->m_ConnectionStatus = NORMAL_CONNECTION;

	char serveraddr[200];
	memset(serveraddr,0,200);
	ServerInfo_ServerAddress(serveraddr, "GET");
	if (strlen(serveraddr)==0)
	{
	 	WritestrLog(MODULE_ENGINE, "Server address null, need to start listen broadcast address.");
		TCEngine_StartListen(this);
		return INFOMGMT_ERR_SERVERINFO;//blank server address
	}
	
	// Download cert
	DownloadCertFinishEventArgs_Construct(&dcfEventArgs);
	
	iRet = ConnMgmt_HTTPDownload(&objBootCert,nCertKind,  ServerPKCert,IntelPKCert); 

	if(iRet != SUCCESS)
	{
		//if (iRet >=ConnMgmt_ExceptionStatus_ConnectFailure && iRet <=ConnMgmt_ExceptionStatus_ConnectionClosed)  
		
		if ((iRet < ConnMgmt_ExceptionStatus_Success && iRet >= ConnMgmt_ExceptionStatus_ConnectFailure) 
			//(iRet <= ConnMgmt_SOAP_END && iRet >= ConnMgmt_SOAP_OK) || 
			|| iRet == ConnMgmt_SOAP_TCP_ERROR || iRet == ConnMgmt_SOAP_UDP_ERROR
			|| (iRet <= ConnMgmt_Http5_END && iRet >= ConnMgmt_Http2_START))
			TCEngine_StartListen(this);
		return iRet;
	}

	this->m_ConnectionStatus =NORMAL_CONNECTION;
	// Save cert kind to eventargs 
	dcfEventArgs.CertKind = objBootCert.CertKind; 
	UpdateUIbyStatus();
		// Save downlaod status to eventargs
	dcfEventArgs.DownloadStatus = iRet;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//Initialize TPM
	//SaveServerPKToTPM
	//SaveIntelPKToTPM
/*	1)	Initialize TPM data (BT = 10, ED = ¡®2099/12/31¡¯)
2)	Grab Ethernet Card MAC Address and generate HWID and write it into TPM NVM
3)	Download Intel CMPC Public Key and write it into TPM (via SOAP/HTTP)
a)	Direct write into the TPM NVM Index-2 space;
4)	Download School Server Public Key and write it into TPM (via SOAP/HTTP)
a)	Direct write into the TPM NVM Index-2 space;
5)	Download shared secret from the server
6)	Create TPM owner and set its default password (hard-coded)
7)	Set Disable TPM Owner Clear Flag
8)	Download Server certificate and Root CA certificate and import them into CMPC
9)	Prompt for OS reboot
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			// Save cert to TPM
	switch(dcfEventArgs.CertKind)
	{
		// No Cert
		case NoCert:
			// Set return value
			iRet = TCEngine_ExceptionStatus_NoBootCert;
			WriteLog(iRet);
			break;

		// Get a normal cert
		case AutoProvisionPackage:
          	iRet = Provision_Construct(&provision);
			if (iRet) {WriteLog(iRet);		return iRet;}
			
			iRet = Provision_TPMAutoProvision(&provision, IntelPKCert,ServerPKCert);
			if (iRet) {WriteLog(iRet);		return iRet;}		
			
			iRet = Provision_Destruct();
			if (iRet) {WriteLog(iRet);		return iRet;}	
			break;
		case CAPackage:
			break;
	}		

	// Solicitation event
	TCEngine_OnDownloadCertFinish(&dcfEventArgs);

	return iRet;
}

// Get Student Id
int TCEngine_GetStudentId(string strStudentID)
{
	// Check if input parameter is availabe
	if(strStudentID == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	DeviceInfo_StudentID(strStudentID, CMD_GET);
	return SUCCESS;
}

// Get expiration information
// The information will first get from the variable in variable,
// it get information from config file if the variable is empty,
// it get information from TPM if config file is empty 
// <param name="dateExpirationDate">(out)expiration date</param>
// <param name="nBootCount">(out)boot count</param>
int TCEngine_GetExpirationInfo(TCEngine *this, DateTime *dateExpirationDate, int *pBootCount)
{		
	struct tm	*pTmNow = NULL;
	time_t		timeNow;
	DateTime	dateTime;
	OtherInfo	otherInfo; 

	char        strBuf[20];
	string		dateInfo = NULL;
	string		lastDate = NULL;
	string		lastTimes = NULL;
	int         iRet = SUCCESS;

	// Check if input parameter is availabe
	if(this == NULL || dateExpirationDate == NULL || pBootCount == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	OtherInfo_Construct(&otherInfo);
/*	if(this -> m_bootCount != 0)
	{
		// Get from presave
		memcpy(&dateTime, this -> m_expirationDate, sizeof(DateTime));
		*pBootCount = this -> m_bootCount;
	}
	else
*/
		// Get from config
		lastDate = otherInfo.LastDate;
		lastTimes = otherInfo.LastTimes;  
		if(strcmp(lastDate, "") == 0 || strcmp(lastTimes, "") == 0)
		{
			// Get from TPM
 			iRet = TPMCtrl_ExpiryDate(this -> m_objTPMCtrl, &dateTime);
			if(iRet != SUCCESS)
			{
				time(&timeNow);
				pTmNow = localtime(&timeNow);
				dateTime.Year = pTmNow -> tm_year;
				dateTime.Month = pTmNow -> tm_mon;
				dateTime.Day = pTmNow -> tm_mday;
			}
			iRet = TPMCtrl_BootCount(this -> m_objTPMCtrl, pBootCount);
            if(iRet != SUCCESS)
			{
                *pBootCount = 0;
			}
		}
		else
		{
			// parse expire date form config
			dateInfo = lastDate;
			strcpy(strBuf, dateInfo + DATE_YEAR_OFFESET);
			strBuf[4] = 0;
			dateTime.Year = atoi(strBuf);
			strcpy(strBuf, dateInfo + DATE_MONTH_OFFESET);
			strBuf[2] = 0;
			dateTime.Month = atoi(strBuf);
			strcpy(strBuf, dateInfo + DATE_DAY_OFFESET);
			strBuf[2] = 0;
			dateTime.Day = atoi(strBuf);			
			// parse boot count form config
			*pBootCount = -1;			
			if((*pBootCount = atoi(lastTimes)) != -1); 
			else
			{
                // Get from TPM
				iRet = TPMCtrl_ExpiryDate(this -> m_objTPMCtrl, &dateTime);
                if(iRet != SUCCESS)
				{
					strcpy(strBuf, dateInfo + DATE_YEAR_OFFESET);
					strBuf[4] = 0;
					dateTime.Year = atoi(strBuf);
					strcpy(strBuf, dateInfo + DATE_MONTH_OFFESET);
					strBuf[2] = 0;
					dateTime.Month = atoi(strBuf);
					strcpy(strBuf, dateInfo + DATE_DAY_OFFESET);
					strBuf[2] = 0;
					dateTime.Day = atoi(strBuf);
				}
                iRet = TPMCtrl_BootCount(this -> m_objTPMCtrl, pBootCount);
                if(iRet != SUCCESS)
                {
                    *pBootCount = 0;
                }
			}
		}
		memcpy(this -> m_expirationDate, &dateTime, sizeof(dateTime));
		this -> m_bootCount = *pBootCount;


	// availability in the expiration date
	dateExpirationDate -> Year = dateTime.Year;
	dateExpirationDate -> Month = dateTime.Month;
	dateExpirationDate -> Day = dateTime.Day;
	dateExpirationDate -> Hour = 23;
	dateExpirationDate -> Minute = 59;
	dateExpirationDate -> Second = 59;
	return iRet;
}

// Get device current information
int TCEngine_GetCurrentSate(TCEngine *this)
{
	struct tm	*pTmNow = NULL;
	struct tm	tmExpiry;
	time_t		timeNow, timeExpiry;
	DateTime	dateExpirationDate;
	PromptInfo	promptInfo;

	int			nBootCount = 0;
	int			timeSpanDays = 0;
	int			iRet = STATUS_NORMAL;

// start add by Roc Zhang 200709017
	int			bEnable  = 0;
	int			bActived = 0;
// end add

	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}
// start add by Roc Zhang 200709017
	if ( NO_CONNECTION  == this->m_ConnectionStatus )
	{
		iRet |= STATUS_NO_CONNECTION; 
	}

	OtherInfo_Enable( &bEnable, "GET");
	if ( !bEnable )
	{
		iRet |= STATUS_DISABLE;  
	}

	OtherInfo_Actived( &bActived, "GET");
	if ( !bActived )
	{
		iRet |= STATUS_NOT_APPROVED;   
	}
// end add

	PromptInfo_Construct(&promptInfo);
	TCEngine_GetExpirationInfo(this, &dateExpirationDate, &nBootCount);

	// Cert permanent
	if(dateExpirationDate.Year >= PERMANENT_YEAR
		&& nBootCount > PERMANENT_BOOTCOUNT)
	{
		iRet |= STATUS_PERMANENT;
	}
	else
	{
		// Expiration year should not exceed to 2038 year, or the mktime will be overflow.
		if(dateExpirationDate.Year >= 2038)
			if (nBootCount < PERMANENT_BOOTCOUNT && nBootCount > atoi(promptInfo.PromptTimes))
			{
			//	iRet = STATUS_NORMAL;
				return iRet;
			}		
			else
			{
				iRet |= STATUS_WARNING;
				return iRet;
			}

		time(&timeNow);
		pTmNow = localtime(&timeNow);
		timeNow = mktime(pTmNow);
		tmExpiry.tm_sec = tmExpiry.tm_min = 59;
		tmExpiry.tm_hour = 23;
        tmExpiry.tm_mday = dateExpirationDate.Day;
        tmExpiry.tm_mon = dateExpirationDate.Month - 1;
        tmExpiry.tm_year = dateExpirationDate.Year - 1900;
		tmExpiry.tm_wday = tmExpiry.tm_yday = 0;
        tmExpiry.tm_isdst = 0;
		timeExpiry = mktime(&tmExpiry);
		timeSpanDays = (int)difftime(timeExpiry, timeNow) / SECONDS_FOR_ONE_DAY;

		if(timeSpanDays <= 0 || nBootCount <= 1)
		{// Urgency
			iRet |= STATUS_WARNING;
		}
		else if(atoi(promptInfo.PromptDays) > timeSpanDays 
			|| atoi(promptInfo.PromptTimes) >= nBootCount)
		{// Warning
			iRet |= STATUS_WARNING;
		}
	}
	return iRet;
}

// respond download cert finish event
// <param name="e">event parameter</param>
int TCEngine_OnDownloadCertFinish(DownloadCertFinishEventArgs *pArgs)
{	
	// Check if input parameter is availabe
	if(pArgs == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	return tcEngine_DownloadCertFinish(pArgs);
}

// Constructor
int DownloadCertFinishEventArgs_Construct(DownloadCertFinishEventArgs *pArgs)
{
	// Check if input parameter is availabe
	if(pArgs == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	pArgs -> DownloadStatus = SUCCESS;
	pArgs -> CertKind = NoCert; 
	return SUCCESS;
}

// Constructor A
int TCEngineException_Construct_A(TCEngineException *this)
{
	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

	this -> Status = TCEngine_ExceptionStatus_UnknownError;
	return SUCCESS;
}

// Constructor B
int TCEngineException_Construct_B(TCEngineException *this, int status)
{	
	// Check if input parameter is availabe
	if(this == NULL)
	{
		return TCEngine_ExceptionStatus_ParamError;
	}

    this -> Status = status;
	return SUCCESS;
}

int TCEngine_StartListen(TCEngine* this)
{

	//	static int m_AgentWindowPoped=0; //after auto discovery, if there is no server, pop up the agent window first time.
		//static int Listened=0;
		this->m_ConnectionStatus = NO_CONNECTION;//no connection with server
		if (Listened ==1) return 0;
	
		UpdateUIbyStatus();
//		int iRet= ConnMgmt_StartListen();
		pthread_t MyThread;
		int ret = pthread_create(&MyThread, NULL, ConnMgmt_StartListen, NULL);
		if (ret) 
		{
			WritestrLog(MODULE_ENGINE, "fork StartListen error.");
			return 1;
		}
		//ret = pthread_join(MyThread, (void **)NULL);
		ret = pthread_detach(MyThread);
		if (ret) 
		{

			WritestrLog(MODULE_ENGINE, "thread StartListen detach error.");
			return 1;
		}
		//Listened = 1;
		//if (iRet == 0)//discover succeed
		if (g_AutoProvisionSucceed)
			this->m_ConnectionStatus = NORMAL_CONNECTION;
		/*else
		{
			if (!m_AgentWindowPoped)
			{
				m_AgentWindowPoped = 1;
				GUI_ShowAgentWindow();
			}		
		}*/
		return 0;//modify:zhangxian Date :2009-02-04

}
