/* 
 * Copyright (C) 2012 Yee Young Han <websearch@naver.com> (http://blog.naver.com/websearch)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include "SipServer.h"
#include "SipServerSetup.h"
#include "SipServerMap.h"
#include "Log.h"
#include <string.h>
#include <sys/stat.h>
#include "MemoryDebug.h"

CSipServerSetup gclsSetup;

/**
 * @brief XML ¿¡ ÀúÀåµÈ element ¸®½ºÆ®¸¦ ¹®ÀÚ¿­ ¸Ê ÀÚ·á±¸Á¶¿¡ ÀúÀåÇÑ´Ù.
 * @param pclsElement		¸®½ºÆ®¸¦ ÀúÀåÇÑ XML element
 * @param pszTagName		¹®ÀÚ¿­ ¸®½ºÆ® tag ÀÌ¸§
 * @param pszSubTagName ¹®ÀÚ¿­ ¸®½ºÆ®ÀÇ Ç×¸ñ tag ÀÌ¸§
 * @param clsMap				¹®ÀÚ¿­ ¸Ê ÀÚ·á±¸Á¶
 */
void InsertStringMap( CXmlElement * pclsElement, const char * pszTagName, const char * pszSubTagName, CStringMap & clsMap )
{
	CXmlElement * pclsClient;

	pclsClient = pclsElement->SelectElement( pszTagName );
	if( pclsClient )
	{
		XML_ELEMENT_LIST clsList;
		XML_ELEMENT_LIST::iterator	itList;

		if( pclsClient->SelectElementList( pszSubTagName, clsList ) )
		{
			for( itList = clsList.begin(); itList != clsList.end(); ++itList )
			{
				if( itList->IsDataEmpty() ) continue;

				clsMap.Insert( itList->GetData(), "" );
			}
		}
	}
}

/**
 * @ingroup SipLoadBalancer
 * @brief »ý¼ºÀÚ
 */
CSipServerSetup::CSipServerSetup() : m_iUdpPort(5060), m_iUdpThreadCount(10)
	, m_iTcpPort(5060), m_iTcpThreadCount(10), m_iTcpRecvTimeout(300)
	, m_iTlsPort(5061), m_iTlsAcceptTimeout(10)
	, m_iLogLevel(0), m_iLogMaxSize(20000000)
	, m_iMonitorPort(6000), m_iFileSize(0)
{
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼Ò¸êÀÚ
 */
CSipServerSetup::~CSipServerSetup()
{
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ ÆÄÀÏÀ» ÀÐ¾î¼­ ¸â¹ö º¯¼ö¿¡ ÀúÀåÇÑ´Ù.
 * @param pszFileName ¼³Á¤ ÆÄÀÏ full path
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::Read( const char * pszFileName )
{
	CXmlElement clsXml, * pclsElement;

	if( clsXml.ParseFile( pszFileName ) == false ) return false;

	// SIP ¼³Á¤
	pclsElement = clsXml.SelectElement( "Sip" );
	if( pclsElement == NULL ) 
	{
		CLog::Print( LOG_ERROR, "file(%s) does not contain Sip element", pszFileName );
		return false;
	}

	pclsElement->SelectElementData( "LocalIp", m_strLocalIp );
	pclsElement->SelectElementData( "UdpPort", m_iUdpPort );
	pclsElement->SelectElementData( "UdpThreadCount", m_iUdpThreadCount );
	pclsElement->SelectElementData( "TcpPort", m_iTcpPort );
	pclsElement->SelectElementData( "TcpThreadCount", m_iTcpThreadCount );
	pclsElement->SelectElementData( "TcpRecvTimeout", m_iTcpRecvTimeout );
	pclsElement->SelectElementData( "TlsPort", m_iTlsPort );
	pclsElement->SelectElementData( "TlsAcceptTimeout", m_iTlsAcceptTimeout );
	pclsElement->SelectElementData( "CertFile", m_strCertFile );

	if( ReadSipServer( clsXml ) == false ) return false;

	// ·Î±×
	pclsElement = clsXml.SelectElement( "Log" );
	if( pclsElement == NULL ) 
	{
		CLog::Print( LOG_ERROR, "file(%s) does not contain Log element", pszFileName );
		return false;
	}

	pclsElement->SelectElementData( "Folder", m_strLogFolder );

	// ¸ð´ÏÅÍ¸µ
	pclsElement = clsXml.SelectElement( "Monitor" );
	if( pclsElement ) 
	{
		pclsElement->SelectElementData( "Port", m_iMonitorPort );
	}

	Read( clsXml );

	m_strFileName = pszFileName;
	SetFileSizeTime();

	return true;
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ ÆÄÀÏ¿¡¼­ ½Ç½Ã°£À¸·Î ¼öÁ¤ °¡´ÉÇÑ Ç×¸ñÀ» ÀÐ´Â´Ù.
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::Read( )
{
	CXmlElement clsXml;

	if( clsXml.ParseFile( m_strFileName.c_str() ) == false ) return false;
	if( ReadSipServer( clsXml ) == false ) return false;
	if( Read( clsXml ) == false ) return false;

	SetFileSizeTime();

	return true;
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ ÆÄÀÏ¿¡¼­ SIP ¼­¹ö ¸®½ºÆ®¸¦ Á¦¿ÜÇÑ ½Ç½Ã°£À¸·Î ¼öÁ¤ °¡´ÉÇÑ Ç×¸ñÀ» ÀÐ´Â´Ù.
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::Read( CXmlElement & clsXml )
{
	CXmlElement * pclsElement;

	// ·Î±×
	pclsElement = clsXml.SelectElement( "Log" );
	if( pclsElement ) 
	{
		m_iLogLevel = 0;

		CXmlElement * pclsClient = pclsElement->SelectElement( "Level" );
		if( pclsClient )
		{
			bool bTemp;

			pclsClient->SelectAttribute( "Debug", bTemp );
			if( bTemp ) m_iLogLevel |= LOG_DEBUG;

			pclsClient->SelectAttribute( "Info", bTemp );
			if( bTemp ) m_iLogLevel |= LOG_INFO;

			pclsClient->SelectAttribute( "Network", bTemp );
			if( bTemp ) m_iLogLevel |= LOG_NETWORK;

			pclsClient->SelectAttribute( "Sql", bTemp );
			if( bTemp ) m_iLogLevel |= LOG_SQL;
		}

		pclsElement->SelectElementData( "MaxSize", m_iLogMaxSize );

		CLog::SetLevel( m_iLogLevel );
		CLog::SetMaxLogSize( m_iLogMaxSize );
	}

	// ¸ð´ÏÅÍ¸µ
	m_clsMonitorIpMap.DeleteAll();

	pclsElement = clsXml.SelectElement( "Monitor" );
	if( pclsElement ) 
	{
		InsertStringMap( pclsElement, "ClientIpList", "ClientIp", m_clsMonitorIpMap );
	}

	// º¸¾È
	m_clsDenySipUserAgentMap.DeleteAll();

	pclsElement = clsXml.SelectElement( "Security" );
	if( pclsElement )
	{
		InsertStringMap( pclsElement, "DenySipUserAgentList", "SipUserAgent", m_clsDenySipUserAgentMap );
	}

	return true;
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ ÆÄÀÏ¿¡¼­ SipServer Á¤º¸¸¦ ÀÐ¾î¼­ SipServerMap ¿¡ ÀúÀåÇÑ´Ù.
 * @param clsXml ¼³Á¤ÆÄÀÏÀ» ÀÐÀº XML °´Ã¼
 * @returns ¼º°øÇÏ¸é true ¸¦ ¸®ÅÏÇÏ°í ½ÇÆÐÇÏ¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::ReadSipServer( CXmlElement & clsXml )
{
	CXmlElement * pclsElement;

	pclsElement = clsXml.SelectElement( "SipServerList" );
	if( pclsElement == NULL ) 
	{
		CLog::Print( LOG_ERROR, "does not contain SipServerList element" );
		return false;
	}

	gclsSipServerMap.SetDeleteAll();

	XML_ELEMENT_LIST clsList;
	XML_ELEMENT_LIST::iterator	itList;

	if( pclsElement->SelectElementList( "SipServer", clsList ) )
	{
		for( itList = clsList.begin(); itList != clsList.end(); ++itList )
		{
			const char * pszIp = itList->SelectAttribute( "Ip" );
			if( pszIp == NULL || strlen(pszIp) == 0 ) continue;

			int iPort = 5060;
			itList->SelectAttribute( "Port", iPort );

			bool bUse = true;
			itList->SelectAttribute( "Use", bUse );

			if( gclsSipServerMap.Insert( pszIp, iPort, bUse ) == false )
			{
				CLog::Print( LOG_ERROR, "sipserver(%s:%d) is not inserted to sipservermap" );
			}
		}
	}

	gclsSipServerMap.DeleteIfSet();

	return true;
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¸ð´ÏÅÍ¸µ Å¬¶óÀÌ¾ðÆ® IP ÁÖ¼ÒÀÎ°¡?
 * @param pszIp		Å¬¶óÀÌ¾ðÆ® IP ÁÖ¼Ò
 * @returns ¸ð´ÏÅÍ¸µ Å¬¶óÀÌ¾ðÆ® IP ÁÖ¼ÒÀÌ¸é true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::IsMonitorIp( const char * pszIp )
{
	return m_clsMonitorIpMap.Select( pszIp );
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ÆÄÀÏÀÌ ¼öÁ¤µÇ¾ú´ÂÁö È®ÀÎÇÑ´Ù.
 * @returns ¼³Á¤ÆÄÀÏÀÌ ¼öÁ¤µÇ¾úÀ¸¸é true ¸¦ ¸®ÅÏÇÏ°í ±×·¸Áö ¾ÊÀ¸¸é false ¸¦ ¸®ÅÏÇÑ´Ù.
 */
bool CSipServerSetup::IsChange()
{
	struct stat	clsStat;

	if( stat( m_strFileName.c_str(), &clsStat ) == 0 )
	{
		if( m_iFileSize != clsStat.st_size || m_iFileTime != clsStat.st_mtime ) return true;
	}

	return false;
}

/**
 * @ingroup SipLoadBalancer
 * @brief ¼³Á¤ÆÄÀÏÀÇ ÀúÀå ½Ã°£À» ÀúÀåÇÑ´Ù.
 */
void CSipServerSetup::SetFileSizeTime( )
{
	struct stat	clsStat;

	if( stat( m_strFileName.c_str(), &clsStat ) == 0 )
	{
		m_iFileSize = clsStat.st_size;
		m_iFileTime = clsStat.st_mtime;
	}
}
