#include "FileSender.h"

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <algorithm>	// find 함수 사용 목적


CFileSender::CFileSender(CIPCache * pIpCache, CProcessStatus * pProcess, CSocketControl * pSockCtl )
	: m_bProcessing( false )
	  , m_bThreadStop( false )
	  , m_pIpCache ( pIpCache )
	  , m_pProcessStatus( pProcess )
	  , m_pSocketControl( pSockCtl )
	  , m_fhsAvailableCount(0)
{
	// NEW 2013-03-19 huibong
	// Thread 핸들 멤버 변수 초기화 처리 (Valgrind 체크시 수정 필요사항)

	// CHG 2014-05-26 huibong
	// 본 모듈 특징상 Thread 가 생성되었다가 
	// 1. 지정된 전송 작업을 마친 후 정상적으로 Thread 가 종료됨.
	// 2. 업무 Flow 에 따라 Thread 가 전송 작업 중 모듈 객체 자체가 소멸될 수 있음.
	// 이에 따라 Thread Handle 을 정확하게 제어해야 함.
	// - 잘못 제어할 경우... 다른 Thread Handle 에 명령을 내릴 수 있으므로 주의 (#19102)
	// 하지만... OS 에 따라 pthread_t Type 이 서로 다름
	// - FreeBSD : pthread 구조체에 대한 포인터
	// - Linux : unsigned long integer

#ifdef __FreeBSD__

	m_threadHandle = NULL;

#else						// Linux 인 경우...
	m_threadHandle = 0;

#endif

}

CFileSender::~CFileSender()
{
	// Thread 동작 정지 처리.
#ifdef __FreeBSD__
	if( m_threadHandle != NULL )
		pthread_cancel( m_threadHandle );

#else  // Linux 인 경우...
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );

#endif
	
	m_listTarget.clear();

}

bool CFileSender::Init( const std::string& szFileName, unsigned long long nFileSize, const std::string& szTargetTranId 
		, std::vector<string>& vecTarget, int nHashLevel, bool bUseInternalIp, char command, std::string& szErrorMessage )
{
	// 사용여부 플래그를 확인
	if( IsIdle() == false )
		return false;

	// 전달받은 소스 파일 정보 검증.
	if( CheckFileInfo( szFileName, nFileSize, szErrorMessage ) == false )
		return false;

	// Target FHS 갯수 검사
	if( vecTarget.size() <= 0 )
	{
		szErrorMessage  = "Target FHS count not valid.(";
		szErrorMessage += vecTarget.size();
		szErrorMessage += ")";

		LOG( LERR, "%s", szErrorMessage.c_str() );

		return false;
	}

	// NEW 2012-02-01 huibong
	// 전달받은 Target FHS 정보 중에 현재 FHS ( 원본 ) 가 포함되었는지 검사하는 방어 코드를 추가한다.
	// 실제 Control 프로세스 개발자가 실수로 원본 FHS 을 Target FHS 로 설정하여 전송 작업으로 인한 원본 파일 유실이 발생함.
	char tempBuffer[256];
	memset( tempBuffer, 0x00, sizeof(tempBuffer));
	if( gethostname( tempBuffer, 255 ) != 0 )
	{
		// Current 장비의 hostname 을 가져오지 못할 경우 오류로 처리 및 전송 실패 처리.
		szErrorMessage  = "Source FHS hostname not valid.";
		LOG( LERR, "%s [%d][%s]", szErrorMessage.c_str(), errno, strerror(errno));

		return false;
	}
	else
	{
		// Current host name 이 정상인 경우 Target FHS 상에 해당 정보가 존재하는지 검사한다.
		std::string szCurrentHostName = tempBuffer;
		std::vector<string>::const_iterator it = find( vecTarget.begin(), vecTarget.end(),  szCurrentHostName );

		if( it != vecTarget.end() )  // 실제로 현재 FHS 와 Target FHS 정보가 동일할 경우 오류 처리
		{
			szErrorMessage  = "Source FHS(";
			szErrorMessage += szCurrentHostName ;
			szErrorMessage += ") is included Target FHS(";
			szErrorMessage += *it;
			szErrorMessage += "). request not valid.";

			LOG( LERR, "%s", szErrorMessage.c_str() );
			return false;
		}
	}


	// 정상인 경우 전달받은 정보를 멤버변수에 저장처리후 true 반환.
	m_szSourceFileName	= szFileName;
	m_nSourceFileSize	= nFileSize;
	m_szTargetTranId	= szTargetTranId;
	m_vecTargetFhs		= vecTarget;
	m_nHashLevel		= nHashLevel;
	m_requestType		= command;
	m_bUseInternalIp	= bUseInternalIp;

	// 기타 변수 초기화.
	m_listTarget.clear();
	m_fhsAvailableCount = 0;
	m_szSourceHashValue.clear();
	m_bThreadStop = false;

	return true;
}

/// @brief 파일 전송 관련 작업을 정지시킨다.
void CFileSender::Stop()
{
	if( IsIdle() == true )
		return;

	m_bThreadStop = true;
	return;
}

bool CFileSender::Start()
{
	int nRet = ::pthread_create( &m_threadHandle, 0, CFileSender::SenderThread, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "thread create failed.[%d][%s]", errorNum, strerror(errorNum) );

		// 파일 Replication 수행을 위한 Thread 생성 실패시
		// Control daemon 으로 오류 패킷 전송 처리.
		m_pSocketControl->SendErrorMessageToControl( "file trnasfer thread create failed.", m_requestType );

		return false;
	}

	return true;
}

/// @brief FileSender Class 의 Thread Function, 파일의 전송을 담당함.
void * CFileSender::SenderThread( void * arg )
{
	CFileSender * pObject = reinterpret_cast<CFileSender *>(arg);    
	pthread_detach( pthread_self() );

	// 파일 Sending 작업을 위한 함수 호출.
	pObject->SetThreadStart();
	pObject->Execute();
	pObject->SetThreadEnd();

	// CHG 2014-05-26 huibong
	// Thread Handle 관련 변수 초기화 기능 추가
#ifdef __FreeBSD__
	pObject->m_threadHandle = NULL;

#else	// Linux 인 경우...
	pObject->m_threadHandle = 0;

#endif
	
	// CHG 2013-03-19 huibong
	// detach 된 Thread 에 대해 pthread_exit() 호출시 
	// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 상황이 발생.
	// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
	//pthread_exit( NULL );
	return NULL;
}

/// @brief 실제 파일의 전송을 처리하기 위한 함수.
void CFileSender::Execute()
{
	m_pProcessStatus->AddSendCount();

	// 1. Target FHS 정보 초기화.
	InitFhsInfo();

	// 2. Target FHS 로 접속 처리.
	if( ConnectTargetFhs() == false || m_bThreadStop == true )
	{
		// 접속을 시도했는데 유효한 Connection 이 1개도 없는 경우
		m_pProcessStatus->SubtractSendCount();
		m_listTarget.clear();
		return;
	}

	// 3. 파일의 전송 처리.
	if( SendFileToFhs() == false || m_bThreadStop == true )
	{
		m_pProcessStatus->SubtractSendCount();
		m_listTarget.clear();
		return;
	}

	// BUG 2010-11-30 huibong
	// if 구문 안의 OR 조건이 '||' 아닌 '|' 로 처리되어 이에 대한 수정처리
	
	// 4. Hash 파일 검사 여부에 따른 처리.
	if( CheckHashValue() == false || m_bThreadStop == true )
	{
		m_pProcessStatus->SubtractSendCount();
		m_listTarget.clear();
		return;
	}
	
	// 5. 해당 처리 내역을 Control 로 전송처리
	if( m_bThreadStop == false )
		SendTransferResultToControl();
	
	// 6. 종료 처리
	m_listTarget.clear();

	m_pProcessStatus->SubtractSendCount();	
}

/// @brief 대상 FHS 정보를 설정하고 접속을 설정함.
bool CFileSender::InitFhsInfo()
{
	// FHS 정보 저장소 Clear 처리.
	m_listTarget.clear();

	// 전달받은 FHS 정보를 Target FHS 에 저장처리.
	std::vector<string>::const_iterator it;
	for( it = m_vecTargetFhs.begin(); it != m_vecTargetFhs.end(); it++)
	{
		CTargetFhsInfo target( *it );
		m_listTarget.push_back( target );
	}

	return true;
}

/// @brief 대상 FHS 로 접속 처리를 수행.
bool CFileSender::ConnectTargetFhs()
{
	// Target FHS 정보를 이용하여 접속 처리.
	std::list< CTargetFhsInfo >::iterator it;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		it->MakeConnection( m_bUseInternalIp, m_pIpCache );
		it->PrintToConnectionInfo();
	}

	// 각 Target FHS 로 접속된 유효한 Connection 이 1개로도 있는지 검사한다.
	// 만약 유효한 Connection 이 1 개라도 존재하지 않는 경우 Control Daemon 에게 오류메시지를 전달하고
	// 작업을 종료처리한다.
	
	m_fhsAvailableCount = 0;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->IsAvailable() == true )
			m_fhsAvailableCount++;	
	}

	if( m_fhsAvailableCount == 0 )
	{
		// 유효한 Connection 을 가진 FHS 가 존재하지 않는 경우 
		// Control 로 오류 메시지 전송처리.
		// 재시도에 대한 처리를 위해 
		// 오류로 처리하지 않고 성공으로 처리하여 Control 에서 재시도 처리가 이루어지도록 한다.
		SendTransferResultToControl();
		return false;
	}
	
	return true;
}

/// @brief 각 FHS 로 파일 전송을 시작한다.
bool CFileSender::SendFileToFhs()
{
	// 1. 원본 파일에 대한 Open 처리.
	std::string szErrorMessage;
	int sourceFd = open( m_szSourceFileName.c_str(), O_RDONLY );
	if( sourceFd == -1 )
	{
		// 해당 파일을 Open 할 수 없는 경우.
		// control 로 오류 메시지 전송 
		szErrorMessage = "Source file open fail.(" + m_szSourceFileName + ")";		
		m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );

		return false;
	}

	// 2. 각 FHS 로 파일 전송 요청 메시지 전송.
	// ** 주의 : Control-Transfer 간 Protocol Command 가 
	//           Transfer-Transfer 간 Command 와 다르므로 변환처리하여 전송해야 한다.
	char transferCommand;
	switch( m_requestType )
	{
		case CONTROL_FILE_REPLICATION:
			transferCommand = TRANSFER_FILE_REPLICATION;
			break;

		case CONTROL_FILE_CACHE:
			transferCommand = TRANSFER_FILE_CACHE;
			break;

		default:
			LOG( LERR, "Command convert error [%02x]", m_requestType );

			transferCommand = m_requestType;
			break;
	}

	std::list< CTargetFhsInfo >::iterator it;
	m_fhsAvailableCount = 0;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->ReadyToFileTransfer( m_szSourceFileName, m_nSourceFileSize, m_szTargetTranId, transferCommand ) == false )
		{
			LOG( LERR, "FHS Error [%s][%s]", (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
		}

		// 유효 목록 FHS count 갱신
		if( it->IsAvailable() == true )
			m_fhsAvailableCount++;
	}

	// 3. 전송 가능한 FHS 정보 확인.
	if( m_fhsAvailableCount == 0 )
	{
		// 유효한 FHS 가 없는 경우
		// 오류메시지로 전송해야 하지만 그럴 경우 Control 에서 Retry 를 수행하지 않으므로
		// 정상적인 동작을 수행시켜 차후에 Retry 가 수행될 수 있도록 수정한다.
		
		close( sourceFd );

		LOG( LERR, "Available target fhs not exist [%s][%llu]", m_szSourceFileName.c_str(), m_nSourceFileSize );

		SendTransferResultToControl();
		return false;
	}

	// 4. 각 FHS 로 파일 전송 시작.
	// - 우선은 Buffer 를 이용한 전송처리.
	// TODO 차후에는 Hash Level, 유효 FHS 수를 확인하여 noCopySend (sendfile) 방법으로 전송처리.
	//if( m_nHashLevel != HASH_LEVEL_FULL || m_fhsAvailableCount == 1 )

	// 0 Byte 파일이 아닌 경우에만 전송 처리.
	if( m_nSourceFileSize > 0 )
	{
		// 2017-12-26 BUG huibong (#31633)
		// read() 호출 관련 반환값 저장을 위한 nRead 가 size_t(unsigned) 형일 경우 
		// read() 호출시 오류로 인한 상황 인지를 못하게 됨. 
		// 특히 disk 장애시 -1 을 반환하고 errno 에 5(EIO) 값이 저장됨.
		//size_t nRead = 0;
		ssize_t nRead = 0;

		size_t nWrite = 0;

		unsigned char fileBuffer[FILE_DATA_BUFFER_SIZE];
		memset( fileBuffer, 0x00, sizeof(fileBuffer));
		unsigned long long nTotalSendSize = 0;

		CFileHash * pFileHash = NULL;
		if( m_nHashLevel == HASH_LEVEL_FULL)
		{
			pFileHash = new CFileHash;
			pFileHash->ClearBufferHash();
		}

		// EOF를 만날 때 까지 buffer 크기 만큼 읽어 전송 처리 => 이전에 파일 사이즈 검사는 했음.
		while( (nRead = read( sourceFd, fileBuffer, FILE_DATA_BUFFER_SIZE )) > 0 )
		{
			nWrite = (size_t)nRead;

			for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
			{
				it->SendBufferToFile( (void *)fileBuffer, nWrite );
			}

			// File 에 대한 Full Hash 검사시 버퍼의 내용을 Hash 객체에 저장 처리.
			if( pFileHash != NULL )
			{
				pFileHash->UpdateBufferHash( fileBuffer, nRead);
			}

			nTotalSendSize += nWrite;

			// Thread 정지 처리인 경우 
			if( m_bThreadStop == true )
				break;
		}

		// 2017-12-26 BUG huibong (#31633)
		// disk 장애 발생시 read() 함수는 -1을 반환하고...  errno 에 5(EIO) 값이 저장됨. 
		// 이와 관련된 기능 추가
		if( nRead < 0 )
		{
			int errorNum = errno;
			close( sourceFd );

			// Hash 객체 삭제 처리 
			if( pFileHash != NULL )
			{
				delete pFileHash;
				pFileHash = NULL;
			}

			// std::string 에서 unsinged long long 값을 문자열로 처리시 문제가 발생하여
			// C style로 처리함.
			char tempBuffer[1024];
			snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "Transfer error. source file[%s][%llu] read[%llu] error(%d:%s)."
					  , m_szSourceFileName.c_str(), m_nSourceFileSize, nTotalSendSize
					  , errorNum, strerror( errorNum ) );

			szErrorMessage = tempBuffer;

			// NEW 2012-02-02 huibong
			// Content read error 상황을  Control 프로세스에 오류 상황으로 Control 프로세스에 보고 처리한다.
			m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );

			LOG( LERR, "%s", szErrorMessage.c_str() );

			return false;
		}

		// Hash 생성시 해당 처리 결과 값을 멤버변수에 저장처리.
		if( pFileHash != NULL )
		{
			pFileHash->GetBufferHash( m_szSourceHashValue );
			delete pFileHash;
			pFileHash = NULL;
		}

		// Thread 정지 처리인 경우 
		if( m_bThreadStop == true )
		{
			close( sourceFd );
			LOG( LWAR, "File[%s] Transfer stop By thread stop flag", m_szSourceFileName.c_str() );

			// CHG 2012-02-02 huibong
			// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
			// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
			// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우 
			// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.
			
			return false;
		}

		
		// 만약 전달받은 파일 크기와 실제 전송한 파일의 크기가 다른 경우 
		// 오류 처리 하고 Socket 종료한다. => 이러면 전송된 파일은 자동으로 unlink 처리됨.
		if( m_nSourceFileSize != nTotalSendSize )
		{
			close( sourceFd );

			// std::string 에서 unsinged long long 값을 문자열로 처리시 문제가 발생하여
			// C style로 처리함.
			char tempBuffer[1024];
			snprintf( tempBuffer, sizeof(tempBuffer)-1, "Transfer completed. But source file[%s] size changed. (file: %llu, transfer: %llu)"
					, m_szSourceFileName.c_str(), m_nSourceFileSize, nTotalSendSize );

			szErrorMessage = tempBuffer;

			// NEW 2012-02-02 huibong
			// Content 전송 완료 후 원본 파일이 변경된 상황이므로.. Control 프로세스에서 재시도를 못하도록 
			// 오류 상황으로 Control 프로세스에 보고 처리한다.
			m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );

			LOG( LERR, "%s", szErrorMessage.c_str());

			return false;
		}

		_LOG( LINF, "File[%s] Transfer Complete[%llu / %llu]", m_szSourceFileName.c_str(), nTotalSendSize, m_nSourceFileSize );
	}
	else
	{
		_LOG( LINF, "File[%s] Transfer Complete. 0 byte file", m_szSourceFileName.c_str());
	}

	// 5. 파일 전송 완료 후 File Descriptor Close
	close( sourceFd );
	
	// 6. 전송 결과 응답 대기 처리.
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->GetFileTransferResult() == false )
		{
			LOG( LERR, "Get transfer result failed [%s][%s]", (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
		}
	}

	// 7. 전송 완료 처리가 완료된 유효한 FHS 가 존재하는지 확인.
	m_fhsAvailableCount = 0;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->IsAvailable() == true )
			m_fhsAvailableCount++;	
	}

	// 8. 전송 가능한 FHS 정보 확인.
	if( m_fhsAvailableCount == 0 )
	{
		// 유효한 FHS 가 없는 경우
		// 오류메시지로 전송해야 하지만 그럴 경우 Control 에서 Retry 를 수행하지 않으므로
		// 정상적인 동작을 수행시켜 차후에 Retry 가 수행될 수 있도록 수정한다.
		LOG( LERR, "Available target fhs not exist by send complete [%s][%llu]", m_szSourceFileName.c_str(), m_nSourceFileSize );

		SendTransferResultToControl();
		return false;
	}

	// 9. 전송 완료 처리 후 원본 소스 파일이 변경되었는지 다시 검증
	if( CheckFileInfo( m_szSourceFileName, m_nSourceFileSize, szErrorMessage ) == false )
	{
		// 전송 완료 후 소스 파일 검증 처리시 오류가 발생한 경우.
		// 이때는 원본 파일이 변경된 경우이므로 Control 에서 재시도를 하지 못하도록 오류로 처리한다.
		LOG( LERR, "File[%s] transfer completed. But source file size changed.[%s]", m_szSourceFileName.c_str(), szErrorMessage.c_str());

		m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );

		return false;
	}

	return true;
}

/// @brief 파일에 대한 Hash 값을 생성하여 각 FHS 로 전송된 File 의 Hash 검사를 요청한다.
bool CFileSender::CheckHashValue()
{
	// Hash 검사를 수행하지 않는 경우 그냥 통과. ^^
	if( m_nHashLevel != HASH_LEVEL_PARTITIAL && m_nHashLevel != HASH_LEVEL_FULL )
		return true;

	// 0 Byte 파일인 경우 Hash 검사 그냥 통과.
	if( m_nSourceFileSize == 0 )
		return true;

	std::string szErrorMessage;
	// 자 Hash 검사를 수행하기 위해 Source 파일에 대한 Hash 를 생성한다.
	if( m_nHashLevel == HASH_LEVEL_PARTITIAL )
	{
		// 부분 Hash 검사인 경우.
		// Hash 처리를 위한 객체를 생성한 후 Hash 검사를 수행
		CFileHash fileHash;
		if( fileHash.GetFileHash( m_szSourceFileName, m_nHashLevel, m_szSourceHashValue) == false )
		{
			szErrorMessage = "source file hash create fail [" + m_szSourceFileName + "] [" + fileHash.GetLastErrorMessage() + "]";
			m_pSocketControl->SendErrorMessageToControl( szErrorMessage, m_requestType );

			LOG( LERR, "%s", szErrorMessage.c_str());

			return false;
		}
	}
	// Full Hash 인 경우 Socket 으로 데이터 전송시 Source Hash 값이 생성됨.

	// Thread 정지 처리인 경우 
	if( m_bThreadStop == true )
	{	
		LOG( LERR, "File[%s] Hash check stop By thread stop flag", m_szSourceFileName.c_str() );

		// CHG 2012-02-02 huibong
		// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
		// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
		// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우 
		// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.

		return false;
	}

	// 정상적으로 Hash 생성이 완료된 경우 각 FHS 로 Hash 검사 요청 처리.
	std::list< CTargetFhsInfo >::iterator it;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->SendFileHashCheckRequest( m_nHashLevel, m_nSourceFileSize, m_szSourceHashValue) == false )
		{
			LOG( LERR, "Get hash check result failed [%d][%s][%s]", m_nHashLevel, (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );
		}
	}

	// 각 FHS 로 부터 처리된 Hash 검사 결과 정보 수신 처리.
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( it->GetFileHashCheckResult() == false )
		{
			LOG( LERR, "Get hash check result failed [%d][%s][%s]", m_nHashLevel, (it->GetHostName()).c_str(), (it->GetErrorMessage()).c_str() );

		}

		// Thread 정지 처리인 경우 
		if( m_bThreadStop == true )
		{	
			LOG( LERR, "File[%s] Hash result stop By thread stop flag", m_szSourceFileName.c_str() );

			// CHG 2012-02-02 huibong
			// File Send Thread 가 중지되는 경우는 현재 Control 과 통신 연결이 끊어진 경우 밖에 없으므로
			// Control 프로세스로 결과를 전송하는 기능은 탑재하지 않는다..
			// 만약 Control 가 통신 연결 해제 상황 외에 Thread 가 정지하는 기능을 추가할 경우 
			// Control 로 수행 결과를 전송하는 함수를 호출처리해야 한다.

			return false;
		}
	}

	return true;
}

/// @brief 파일 Transfer 처리 결과를 Control Daemon 으로 전송처리.
bool CFileSender::SendTransferResultToControl()
{
	std::map< std::string, std::string> mapSuccessList;
	std::map< std::string, std::string> mapFailList;

	std::string szHostName;
	std::string szFileFullName;
	std::string szErrorMessage;

	// 루프를 돌면서 메시지를 생성
	std::list< CTargetFhsInfo >::iterator it;
	for( it = m_listTarget.begin(); it != m_listTarget.end(); it++)
	{
		if( (it->GetResult( szHostName, szFileFullName, szErrorMessage)) == true )
		{
			mapSuccessList[ szHostName ] = szFileFullName;
			LOG( LDBG, "SEND::SUCCESS [%s]->[%s][%s]", m_szSourceFileName.c_str(), szHostName.c_str(), szFileFullName.c_str());
		}
		else
		{
			mapFailList[ szHostName ] = szErrorMessage;
			LOG( LERR, "SEND::FAIL [%s]->[%s][%s]", m_szSourceFileName.c_str(), szHostName.c_str(), szErrorMessage.c_str());
		}
	}

	return m_pSocketControl->SendFileReplicationResult( m_requestType, mapSuccessList, mapFailList);
}

/// @brief 전달받은 파일 및 크기가 실제 현재 경로에 존재, 크기가 정확한지 검사하기 위한 함수.
bool CFileSender::CheckFileInfo( const std::string& szFileName, unsigned long long nFileSize, std::string& szErrorMessage )
{
	// 1. 전달받은 파일이 존재하는지 검사.
	if( access(szFileName.c_str(), R_OK) == -1 )
	{
		szErrorMessage = "source file access fail.[" + szFileName + "]";
		LOG( LERR, "%s", szErrorMessage.c_str() );

		return false;
	}

	// 2. 파일 크기 검사.
	FILE * pFile = fopen( szFileName.c_str(), "r+" );
	if( pFile == NULL )
	{
		szErrorMessage = "source file open fail.[" + szFileName + "]";
		LOG( LERR, "%s", szErrorMessage.c_str() );

		return false;
	}
	else
	{
		fseeko( pFile, 0, SEEK_END );
		off_t totalSize = ftello( pFile );
		rewind( pFile );
		fclose( pFile );	// 열려진 descriptor close

		// ADD 2010-12-01 huibong off_t type 확인
		// /usr/include/sys/_types.h 에 다음과 같이 정의됨. (64Bit, 32Bit 장비 )
		// typedef __int64_t   __off_t;    /* file offset */
		// 또한 printf 포맷 형식 중 %jd => intmax_t 형식이 존재 (/usr/include/machine/stdint.h)
		// intmax_t 은 /usr/include/machine/_types.h 에 다음과 같이 정의됨 (64Bit,32Bit 동일)
		// typedef __int64_t __intmax_t;
		// 이에 따라 snprintf 사용시 off_t totalSize 출력형식을 기존 %lld -> %jd 로 변경처리
		
		if( (unsigned long long)totalSize != nFileSize )
		{
			// std::string 에 숫자를 append 처리시 오류가 자주 발생하여 C Style 로 변환하여 처리함.
			char tempBuffer[1024];
			snprintf( tempBuffer, sizeof(tempBuffer)-1
					, "source file size not match.[ request: %llu, local: %jd ]"
					, nFileSize, totalSize );

			szErrorMessage = tempBuffer;
			LOG( LERR, "%s", szErrorMessage.c_str() );

			return false;
		}
	}

	// 모두 정상인 경우.
	return true;
}


