/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * 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 Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

/* $Id$ */

#include "Logger/LoggerMacroses.h"
#include <base/Log.h>
#include <wbxml.h>
#include <wbxml_errors.h>
#include <wbxml_mem.h>

#include "Event.h"
#include "identifiers.h"
#include "hash.h"
#include "lock.h"
#include "Starter.h"
#include "SyncCall.h"
#include "WBXMLUtils.h"

#include "daemon/INotificationCenter.h"
#include "executionqueue/ICommand.h"
#include "executionqueue/IExecutionQueue.h"

#include "serverexchange/commands/AccessConfigCommand.h"
#include "serverexchange/commands/ClientSessionCommand.h"
#include "serverexchange/commands/DevDetailCommand.h"
#include "serverexchange/commands/SEMNetworkEntranceCommand.h"
#include "serverexchange/commands/ServerPollingInfoCommand.h"
#include "serverexchange/firmware/FirmwareManager.h"
#include "serverexchange/BasicCommandsSink.h"
#include "serverexchange/CommandStorage.h"
#include "serverexchange/ConnectionInfo.h"
#include "serverexchange/DRMDConnection.h"
#include "serverexchange/ResponseProcessor.h"
#include "serverexchange/ServerExchangeManager.h"
#include "serverexchange/WIBConnector.h"

#include "serverexchange/wrappers/SAlertCommand.h"
#include "serverexchange/wrappers/SCommandFactory.h"
#include "serverexchange/wrappers/SReplaceCommand.h"
#include "serverexchange/wrappers/SStatusCommand.h"

#include "serverexchange/session/BasicSession.h"
#include "serverexchange/session/HMACSession.h"
#include "serverexchange/session/MD5Session.h"


using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Common;
using namespace NS_DM_Client::NS_Communication;
using namespace NS_DM_Client::NS_SyncMLCommand;


#define SEM_CERTIFICATES_LOCATION "CertificatesLocation"
#define SEM_CONNECTION_DELAY      "ConnectionDelay"

#define SEM_DEFAULT_ACCOUNT_NAME  "DefaultAccountName"
#define SEM_DEFAULT_FWURI         "DefaultFWURI"
#define SEM_DRMD_ACCOUNT_NAME     "DRMDAccountName"

#define SEM_DUMP_MESSAGE_PATH     "DumpMessagesPath"
#define SEM_LOG_TRANSPORT_LAYER   "LogTransportLayer"

#define SEM_MAX_MSG_SIZE          "MaxMsgSize"
#define SEM_MAX_OBJ_SIZE          "MaxObjSize"

#define SEM_NEW_LINES_IN_MESSAGE  "NewLinesInMessage"
#define SEM_OEM_SHARED_KEY        "OEMSharedKey"

#define SEM_POLLING_ATTEMPTS      "PollingAttempts"
#define SEM_POLLING_INTERVAL      "PollingInterval"
#define SEM_POLLING_SUPPORTED     "PollingSupported"

#define SEM_SHORT_TIMEOUT         "ShortTimeout"
#define SEM_SSL_VERIFY_HOST       "SSLVerifyHost"
#define SEM_SSL_VERIFY_SERVER     "SSLVerifyServer"
#define SEM_START_ON_LAN          "StartOnLAN"
#define SEM_USER_AGENT_NAME       "UserAgentName"
#define SEM_WBXML_SESSION         "WBXMLSession"

#define SEM_WIB_RETRIES_COUNT     "WIBRetries"
#define SEM_WIB_RETRY_INTERVAL    "WIBRetryInterval"
#define SEM_WIB_PLAIN_BOOTSTRAP   "WIBPlainBootstrap"
#define SEM_WIB_PLAIN_BOOTSTRAP_CIPHER_HEADER    "WIBPlainBootstrapCipherHeaderPresent"
#define SEM_WIB_SERVICE_DISCOVERY_TARGET    "WIBServiceDiscoveryTarget"
#define SEM_WIB_DNS_SERVER        "WIBDNSServer"


static const char * c_LogName = "ServerExchangeManager";

const int c_DefaultMaxMsgSize = 8196;
const int c_DefaultMaxObjSize = 32*1024;


ServerExchangeManager::ServerExchangeManager() :
	m_haveBMProcessed(false),
	m_startOnLAN(false),
	m_startedOnLAN(false),
	m_threadStarted(false),
	m_useShortTimeout(false),

	m_wibCipherHeaderPresent(false),
	m_wibPlainBootstrap(false),
	m_wibRetriesCount(-1),
	m_wibRetryInterval(-1),

	m_connectionDelay(0),

	m_pDefaultConnection(NULL),
	m_pFirmwareManager(NULL),
	m_pProfileHolder(NULL),
	m_pSyncCall(NULL),
	m_pWIBConnector(NULL),
	m_pWIBThread(NULL)
{
	terminate = false;
}


ServerExchangeManager::~ServerExchangeManager()
{
	m_pDefaultConnection = NULL;
    SAFE_DELETE(m_pWIBConnector);
	SAFE_DELETE(m_pWIBThread);

	stopConnections();
	m_connInfoList.clear();

	SAFE_DELETE(m_pFirmwareManager);
}


void ServerExchangeManager::AddCommand(NS_SyncMLCommand::SCommandPtr ptrCommand, const char *connid)
{
	Lock lock(m_csAddCommand);

	GDLDEBUG("add command [serverID='%s'] %d", connid, m_connections.size());
	Connections::iterator iconnection = m_connections.find(connid);
	if (iconnection != m_connections.end() && iconnection->second)
		iconnection->second->AddCommand(ptrCommand, NULL);
	else
	{
		GDLERROR("no available connection '%s' to add command to", connid);
	}
}

void ServerExchangeManager::AddCommands(NS_SyncMLCommand::SCommandsArray &commands, const char *connid)
{
	Lock lock(m_csAddCommand);

	GDLDEBUG("add commands [serverID='%s']", connid);
	Connections::iterator iconnection = m_connections.find(connid);
	if (iconnection != m_connections.end() && iconnection->second)
		iconnection->second->AddCommands(commands, NULL);
	else
	{
		GDLERROR("no available connection '%s' to add commands to", connid);
	}
}


IConnection * ServerExchangeManager::DefaultConnection()
{
	return m_pDefaultConnection;
}


FirmwareManager* ServerExchangeManager::GetFirmwareManager()
{
	if (!m_pFirmwareManager)
	{
		m_pFirmwareManager = new FirmwareManager(m_pProfileHolder);
	}
	return m_pFirmwareManager;
}


bool ServerExchangeManager::Init(const StringMap& settings, const String& loggerInstance, ProfileComponentsHolder& pch)
{
	m_pProfileHolder = &pch;

	Funambol::StringMap *map = const_cast<StringMap*>(&settings);

	const FStringBuffer &certsLocation    = map->get(SEM_CERTIFICATES_LOCATION);
	if (!certsLocation.empty())
		m_settings.CertificatesLocation   = certsLocation;

	const FStringBuffer &connDelay        = map->get(SEM_CONNECTION_DELAY);
	if (!connDelay.empty())
		m_connectionDelay = 1000*atoi(connDelay.c_str());

	const FStringBuffer &sslVerifyServer  = map->get(SEM_SSL_VERIFY_SERVER);
	m_settings.SSLVerifyServer = sslVerifyServer.empty() ? true : atoi(sslVerifyServer.c_str()) != 0;

	const FStringBuffer  &sslVerifyHost   = map->get(SEM_SSL_VERIFY_HOST);
	m_settings.SSLVerifyHost = sslVerifyHost.empty() ? true : atoi(sslVerifyHost.c_str()) != 0;

	const FStringBuffer &propMaxObjSize   = map->get(SEM_MAX_OBJ_SIZE);
	m_settings.MaxObjSize = (propMaxObjSize.empty()) ? 0 : atoi(propMaxObjSize.c_str());
	if (m_settings.MaxObjSize == 0)
		m_settings.MaxObjSize = c_DefaultMaxObjSize;

	const FStringBuffer &propMaxMsgSize   = map->get(SEM_MAX_MSG_SIZE);
	m_settings.MaxMsgSize = (propMaxMsgSize.empty()) ? 0 : atoi(propMaxMsgSize.c_str());
	if (m_settings.MaxMsgSize == 0)
		m_settings.MaxMsgSize = c_DefaultMaxMsgSize;

	const FStringBuffer &defaultFWURI     = map->get(SEM_DEFAULT_FWURI);
	if (!defaultFWURI.empty())
		m_defaultFWURI = defaultFWURI.c_str();

	const FStringBuffer &startOnLAN       = map->get(SEM_START_ON_LAN);
	m_startOnLAN = (!startOnLAN.empty() && startOnLAN.icmp("1")) ? true : false;

	const FStringBuffer & sbDAccName      = map->get(SEM_DEFAULT_ACCOUNT_NAME);
	if (!sbDAccName.empty())
		m_defaultAccountName = sbDAccName.c_str();

	const FStringBuffer & sbDRMDAccName   = map->get(SEM_DRMD_ACCOUNT_NAME);
	if (!sbDRMDAccName.empty())
		m_drmdAccountName = sbDRMDAccName.c_str();

	const FStringBuffer& userAgentName    = map->get(SEM_USER_AGENT_NAME);
	if (!userAgentName.empty())
		m_settings.UserAgentName = userAgentName;

	const FStringBuffer& oemSharedKey     = map->get(SEM_OEM_SHARED_KEY);
	if (!oemSharedKey.empty())
		m_OEMSharedKey = oemSharedKey.c_str();

	const FStringBuffer& newLines         = map->get(SEM_NEW_LINES_IN_MESSAGE);
	if (!newLines.empty())
		m_settings.KeepNewLines = newLines.icmp("1");

	const FStringBuffer &propWBXMLSession = map->get(SEM_WBXML_SESSION);
	const char *cWBXMLSession = propWBXMLSession.c_str();
	bool useWBXML = false;
	if (cWBXMLSession)
	{
		if (!strcmp("1", cWBXMLSession))
		{
			useWBXML = true;
		}
		else if (strcmp("0", cWBXMLSession))
		{
			GDLERROR("Incorrect value of the 'WBXMLSession' property: [%s]; expected are [0,1]", cWBXMLSession);
		}
	}
	m_settings.UseWBXML = useWBXML;


	// read server polling settings
	const FStringBuffer & sbAttempts(map->get(SEM_POLLING_ATTEMPTS));
	const FStringBuffer & sbInterval(map->get(SEM_POLLING_INTERVAL));
	const FStringBuffer & sbSupported(map->get(SEM_POLLING_SUPPORTED));

	m_serverPoller.SetProfile(pch);
	if (!sbAttempts.empty())
		m_serverPoller.URIPollingAttempts = sbAttempts.c_str();
	if (!sbInterval.empty())
		m_serverPoller.URIPollingInterval = sbInterval.c_str();
	if (!sbSupported.empty())
		m_serverPoller.URIPollingSupported = sbSupported.c_str();

	m_useShortTimeout = !map->get("ShortTimeout").empty();

	const FStringBuffer& dumpPath      = map->get(SEM_DUMP_MESSAGE_PATH);
	if (!dumpPath.empty())
		m_settings.MessagesDumpPath = dumpPath;

	const FStringBuffer& transportLog  = map->get(SEM_LOG_TRANSPORT_LAYER);
	if (transportLog == "1")
	{
#if defined(PLATFORM_POSIX)
		Funambol::Log::instance().setLogPath("/tmp");
#elif defined(PLATFORM_WINDOWS)
		Funambol::Log::instance().setLogPath("c:/Windows/Temp");
#endif
		Funambol::Log::instance().setLogName("dmclient_transport.log");
		Funambol::Log::instance().setLevel(Funambol::LOG_LEVEL_DEBUG);
	}

	const FStringBuffer  &wibReties   = map->get(SEM_WIB_RETRIES_COUNT);
	m_wibRetriesCount = wibReties.empty() ? 0 : atoi(wibReties.c_str());

	const FStringBuffer  &wibRetryInterval   = map->get(SEM_WIB_RETRY_INTERVAL);
	m_wibRetryInterval = wibRetryInterval.empty() ? 0 : atoi(wibRetryInterval.c_str());

	const Funambol::StringBuffer& plainBootstrapParam = map->get(SEM_WIB_PLAIN_BOOTSTRAP);
	m_wibPlainBootstrap = (plainBootstrapParam.empty()) ? false : atoi(plainBootstrapParam.c_str()) != 0;

	const Funambol::StringBuffer& plainBootstrapCipherHdrParam = map->get(SEM_WIB_PLAIN_BOOTSTRAP_CIPHER_HEADER);
	m_wibCipherHeaderPresent = (plainBootstrapCipherHdrParam.empty()) ? false : atoi(plainBootstrapCipherHdrParam.c_str()) != 0;

	const Funambol::StringBuffer& wibServiceDiscoveryTarget = map->get(SEM_WIB_SERVICE_DISCOVERY_TARGET);
	m_wibServiceDiscoveryTarget = (wibServiceDiscoveryTarget.empty()) ? "" : wibServiceDiscoveryTarget;

	const Funambol::StringBuffer& wibDnsServer = map->get(SEM_WIB_DNS_SERVER);
	m_wibDnsServer = (wibDnsServer.empty()) ?  "" : wibDnsServer;

	GetFirmwareManager();

	GDLINFO("DefaultAccountName:\t'%s'", m_defaultAccountName.c_str());

	return true;
}


bool ServerExchangeManager::HasAccountFor(String &serverID)
{
	for (size_t i=0; i<m_connInfoList.size(); ++i)
		if (m_connInfoList[i].get() && serverID == m_connInfoList[i]->GetServerID())
			return true;
	return false;
}


void ServerExchangeManager::HandleNetworkEntrance(int homeNspID, const char* operatorName, bool startWIB)
{
	GDLDEBUG("HandleNetworkEntrance, operator name: '%s'", operatorName);
	Stop();

	Start();
	wait();

	m_haveBMProcessed = false;

	// here we sleep to get the ip & dns assigned
	sleep(m_connectionDelay);

	if (startWIB)
	{
		StartWIB(homeNspID, operatorName);
	}

	startServerPoller();
}


void ServerExchangeManager::NotifyDiagnosticsReady(String &serverID, String &origID)
{
	GDLERROR("notify diagnostics ready to %s server", serverID.c_str());

	IConnection *pDRMDConnection = NULL;
	Connections::iterator iconnection = m_connections.find(serverID);
	if (iconnection != m_connections.end())
	{
		pDRMDConnection = iconnection->second;
	}
	else
	{
		pDRMDConnection = startConnection(serverID);
	}

	if (pDRMDConnection)
	{
		// TODO - pass Diagnostics node uri to alert
		sleep(3000); // wait; TODO - this is a dev hook; remove
		pDRMDConnection->StartSession(SCommandFactory::CreateGenericAlert(NULL,
											DIAGNOSTICS_READY,
											200));
	}
	else
		GDLERROR("  failed to start DRMD connection serverID '%s'", serverID.c_str());
}


void ServerExchangeManager::ProcessBootstrapMessage(const char *bsm, uint length)
{
	if (m_haveBMProcessed)
	{
		GDLDEBUG("bootstrap message already processed; skip request");
		return;
	}

	char *xml = NULL;
	int status = WBXMLUtils::Parse(bsm, length, &xml);
	if (status)
	{
		GDLERROR("error '#%d' while converting wbxml of bootstrap to syncml", status);
		GDLERROR("error text:'%s'", wbxml_errors_string((WBXMLError)status));
	}
	else
	{
		// response processor should process message
		GDLDEBUG("process bootstrap message:\n%s", xml);

		ConnectionInfo info;
		BasicCommandsSink bcs(info);
		ResponseProcessor rp(info, &bcs, m_pProfileHolder);
		ResponseProcessor::ProcessingStatus status = rp.Process(xml, true);

		m_haveBMProcessed = (ResponseProcessor::PS_Successful == status);
		if (m_haveBMProcessed)
			bcs.Wait();

		wbxml_free(xml);

		if (m_haveBMProcessed)
		{
 			Restart();
 			startServerPoller();
		}
	}
}


void ServerExchangeManager::RequestFWUpdate(bool userInitiated, const char *fumouri, int data)
{
	if (m_pDefaultConnection)
	{
		m_pDefaultConnection->RequestFWUpdate(userInitiated, fumouri, data);
	}
}


void ServerExchangeManager::RequestSessionStart(const NS_NotificationListener::SessionInfo& sessionInfo)
{
	String serverid = sessionInfo.serverID;
	GDLDEBUG("DMSessionRequest for server '%s'", serverid.c_str());
	Connections::iterator iconnection = m_connections.find(serverid);

	if (iconnection != m_connections.end())
	{
		GDLDEBUG("  found existing connection for server '%s'", serverid.c_str());
		iconnection->second->RequestSessionStart(sessionInfo);
	}
	else
	{
		GDLDEBUG("  start new connection for server '%s'", serverid.c_str());

		IConnection *pConnection = startConnection(serverid);
		if (pConnection)
			pConnection->RequestSessionStart(sessionInfo);
		else
			GDLERROR("  failed to start session for server '%s'", serverid.c_str());
	}
}


void ServerExchangeManager::Restart()
{
	GDLDEBUG("~ restart");
	stopConnections();

	GDLDEBUG("  connections stopped");
	m_connInfoList.clear();
	m_startedOnLAN = false;

	Start();
}


void ServerExchangeManager::Release()
{
}


void ServerExchangeManager::SetConnectionsInfo(ConnInfoList &c)
{
	GDLDEBUG("Set ConnectionsInfo");
	m_connInfoList = c;
	GDLDEBUG("  def account name '%s', start: %d", m_defaultAccountName.c_str(), m_startOnLAN);

	for (size_t i=0; i<m_connInfoList.size(); ++i)
	{
		if (!m_connInfoList[i])
		{
			GDLERROR("passed NULL instead of ConnectionInfo instance");
			continue;
		}

		ConnectionInfo &info = *m_connInfoList[i];
		info.settings = m_settings;
		info.devinf = m_ptrDevInfo;

		GDLDEBUG("  has connection info for '%s'", info.GetServerID());
		if (m_drmdAccountName == info.GetServerID())
		{
			GDLDEBUG("  found DRMD account config '%s', server id '%s'", info.accname.c_str(), info.GetServerID());
			configureDRMDAccount(info);
		}
	}

	if (m_pSyncCall)
		m_pSyncCall->Notify();
}


void ServerExchangeManager::SetDevInfo(Funambol::DevInf &devInf)
{
	GDLDEBUG("Set DevInfo");
	m_ptrDevInfo.reset(devInf.clone());

	if (m_pSyncCall)
		m_pSyncCall->Notify();
}


void ServerExchangeManager::SetServerPollerInfo(bool pollingSupported, int pollingInterval, int pollingAttempts)
{
	if (m_pSyncCall)
		m_pSyncCall->Notify();

	GDLDEBUG("Set ServerPollerInfo");

	m_pollingInfo.pollingSupported = pollingSupported;
	m_pollingInfo.pollingInterval  = pollingInterval;
	m_pollingInfo.pollingAttempts  = pollingAttempts;
}


bool ServerExchangeManager::Start()
{
	GetFirmwareManager();
	FThread::start();
	return true;
}


void ServerExchangeManager::StartClientSession()
{
	GDLDEBUG("start client session");
	if (!m_pDefaultConnection)
		m_pDefaultConnection = startConnection(m_defaultAccountName);

	if (m_pDefaultConnection)
	{
		m_pDefaultConnection->StartSession();
	}
	else
	{
		GDLERROR("default connection is null");
	}
}


void ServerExchangeManager::StartWIB(HomeNSPID nspID, const char* operatorName)
{
	// !!TODO: add lock for m_pWIBConnector

    SAFE_DELETE(m_pWIBConnector);
    m_pWIBConnector = new WIBConnector(*m_pProfileHolder,
		m_wibPlainBootstrap, (m_wibPlainBootstrap) ? m_wibCipherHeaderPresent : true,
		m_wibServiceDiscoveryTarget, m_wibDnsServer,
		m_wibRetriesCount, m_wibRetryInterval);
    if (m_pWIBConnector)
    {
        SAFE_DELETE(m_pWIBThread);
        m_pWIBThread = new Starter(*m_pWIBConnector);
        if (m_pWIBThread)
        {
			GDLDEBUG("WIB start");
            m_pWIBThread->start();
        }
    }
}


bool ServerExchangeManager::Stop()
{
	GDLDEBUG("Received Stop ...");
	m_serverPoller.Stop();
	if (m_threadStarted)
	{
		terminate = true;
		if (m_pSyncCall)
			m_pSyncCall->Notify();
		SAFE_DELETE(m_pSyncCall);

		GDLDEBUG("wait SEM to stop ...");
		wait();

		terminate = false;
	}

	stopConnections();
	m_connInfoList.clear();
	SAFE_DELETE(m_pFirmwareManager);
	GDLDEBUG("terminated");

	return true;
}


void ServerExchangeManager::Wait()
{
	Lock lock(m_csWaitComplete);
}


void ServerExchangeManager::run()
{
	GDLDEBUG("<~~ start SEM");
	m_threadStarted = true;
	Lock lock(m_csWaitComplete);

	do {
		m_pSyncCall = new SyncCall(new DevDetailCommand(m_pProfileHolder), m_pProfileHolder);
		m_pSyncCall->Call();
		SAFE_DELETE(m_pSyncCall);
		if (terminate) break;

		m_pSyncCall = new SyncCall(new ServerPollingInfoCommand(m_pProfileHolder,
																m_serverPoller.URIPollingSupported,
																m_serverPoller.URIPollingInterval,
																m_serverPoller.URIPollingAttempts),
								   m_pProfileHolder);
		m_pSyncCall->Call();
		SAFE_DELETE(m_pSyncCall);
		if (terminate) break;

		m_pSyncCall = new SyncCall(new DMAccountCommand(m_pProfileHolder), m_pProfileHolder);
		m_pSyncCall->Call();
		SAFE_DELETE(m_pSyncCall);
		if (terminate) break;

		if (m_startOnLAN && !m_startedOnLAN)
		{
			ICommand *pNetworEntryCmd = new SEMNetworkEntranceCommand(*m_pProfileHolder, 0, NULL, false);
			m_pProfileHolder->GetExecutionQueue()->Add(*pNetworEntryCmd);
			m_startedOnLAN = true;
		}
	} while(false);

	m_threadStarted = false;
	GDLDEBUG("~~ SEM finished>");
	GDLDEBUG("");
}


void ServerExchangeManager::configureDRMDAccount(ConnectionInfo &ci)
{
	// set DRMD specific values
	const char *serverID = ci.GetServerID();
	size_t devidsize = 256;
	char deviceID[256] = {0};
	FStringBuffer serialNumber;
	if (m_pProfileHolder->GetDeviceAdapter()->GetDeviceID(deviceID, devidsize))
	{
		if (deviceID[0])
		{
			serialNumber.append(deviceID);
			serialNumber.upperCase();
		}
		else
		{
			GDLERROR("Received empty DeviceID");
			return;
		}
	}
	else
	{
		GDLERROR("Failed to receive DeviceID from the DeviceAdapter");
		return;
	}

	// client name
	ci.acconfig.setUsername(deviceID);
	GDLDEBUG("drmd username %s", deviceID);

	// client password
	const char * clientpass = NS_Common::calculateF(serialNumber.c_str(), serverID, m_OEMSharedKey.c_str());
	if (clientpass)
	{
		GDLDEBUG("drmd clientpass %s", clientpass);
		ci.acconfig.setPassword(clientpass);
		SAFE_DELETE_ARR(clientpass);
	}
	else
	{
		GDLERROR("Received empty clientpass on '%s' '%s' '%s'", serialNumber.c_str(), serverID, m_OEMSharedKey.c_str());
		return;
	}

	// server password
	const char * serverpass = NS_Common::calculateF(serverID, serialNumber.c_str(), m_OEMSharedKey.c_str());
	if (serverpass)
	{
		GDLDEBUG("drmd serverpass %s", serverpass);
		ci.acconfig.setServerPWD(serverpass);
		SAFE_DELETE_ARR(serverpass);
	}
	else
	{
		GDLERROR("Received empty serverpass on '%s' '%s' '%s'", serverID, serialNumber.c_str(), m_OEMSharedKey.c_str());
		return;
	}

	ci.settings.UseWBXML = false;
	GDLDEBUG("DefaultAccountName:[cdr]\t'%s'", m_defaultAccountName.c_str());
}


bool ServerExchangeManager::isDRMDServer(String &serverID)
{
	return (m_drmdAccountName == serverID);
}


void ServerExchangeManager::startServerPoller()
{
	GDLDEBUG("startServerPoller");
	m_serverPoller.Stop();

	if (m_pollingInfo.pollingInterval == -1)
	{
		m_serverPoller.SetPollingInterval(1);//m_useShortTimeout ? 1 : 60);
		m_serverPoller.SetPollingAttempts(1);
		m_serverPoller.Start();
	}
	else if (m_pollingInfo.pollingInterval)
	{
		m_serverPoller.SetPollingInterval(m_useShortTimeout ? m_pollingInfo.pollingInterval : 60*m_pollingInfo.pollingInterval);
		if (m_pollingInfo.pollingAttempts > 0)
			m_serverPoller.SetPollingAttempts(m_pollingInfo.pollingAttempts+1);
		else
			m_serverPoller.SetPollingAttempts(m_pollingInfo.pollingAttempts);

		if (m_pollingInfo.pollingSupported && m_pollingInfo.pollingAttempts != 0)
		{
			m_serverPoller.Start();
		}
	}
}


IConnection * ServerExchangeManager::startConnection(String &serverid)
{
	GDLDEBUG("start connection for server '%s'", serverid.c_str());
	IConnection *pConnection = NULL;
	for (size_t i=0; i<m_connInfoList.size(); ++i)
	{
		if (!m_connInfoList[i])
		{
			GDLERROR("stored NULL instead of ConnectionInfo instance");
			continue;
		}

		ConnectionInfo &info = *m_connInfoList[i];
		GDLDEBUG("\there is server '%s'", info.GetServerID());
		if (serverid == info.GetServerID())
		{
			GDLDEBUG("found AccConfig for server '%s'", serverid.c_str());

			NS_Common::Event event;
			if (isDRMDServer(serverid))
				pConnection = new DRMDConnection(info, m_OEMSharedKey);
			else
				pConnection = new Connection(info);

			m_connections[info.GetServerID()] = pConnection;
			pConnection->SetPCH(*m_pProfileHolder);
 			if (m_defaultAccountName == serverid)
 				m_pDefaultConnection = pConnection;

			GDLDEBUG("[start connection ...");
			pConnection->Start(&event);
			event.wait();
			GDLDEBUG("connection started]");
			break;
		}
	}
	return pConnection;
}


void ServerExchangeManager::stopConnections()
{
	GDLDEBUG("stop all connections");
	Connections::iterator iconnection = m_connections.begin();
	for (; iconnection != m_connections.end(); ++iconnection)
	{
		IConnection *pConnection = iconnection->second;
		pConnection->Stop();
		pConnection->Wait();
		delete pConnection;
	}
	m_connections.clear();
	m_pDefaultConnection = NULL;
}

