// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// cmdnsprobemanager.cpp
// 
//
/**
@file
@internalTechnology
*/
//User include 
#include "e32math.h" 
#include "cmdnsprobemanager.h"
//System include
#include <mdns/ccacheentry.h>
__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
/*
 * Two phase constructor
 * @param aCache reference to cache
 * @param aMessageHandler reference to message handler.
 * @param aAutoresolveEnabled True if name conflict to be handled.
 */
CMDNSProbeManager* CMDNSProbeManager::NewL(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
	{
	CMDNSProbeManager* self = CMDNSProbeManager::NewLC(aCache,aMessageHandler,aAutoResolveEnabled);
	CleanupStack::Pop(self);
	return self;
	}

static void CleanUpCache(TAny* aAny)
    {
    RPointerArray <CCacheEntry> * records = static_cast < RPointerArray <CCacheEntry>* > (aAny);
    records->ResetAndDestroy();
    records->Close();
    }
/*
 * Two phase constructor
 * @param aCache reference to cache
 * @param aMessageHandler reference to message handler.
 * @param aAutoresolveEnabled True if name conflict to be handled.
 */
CMDNSProbeManager* CMDNSProbeManager::NewLC(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
	{
	CMDNSProbeManager* self = new (ELeave)CMDNSProbeManager(aCache,aMessageHandler,aAutoResolveEnabled);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
	}

/*
 * Constructor
 */
CMDNSProbeManager::CMDNSProbeManager(MDNSCacheMgr& aCache,CMessageHandler& aMessageHandler,TBool aAutoResolveEnabled)
:CTimer(CActive::EPriorityStandard),iCache(aCache),iMessageHandler(aMessageHandler),iAutoResolveEnabled(aAutoResolveEnabled)
	{
	CActiveScheduler::Add(this);
	}

/*
 * Two phase Constructor
 */
void CMDNSProbeManager::ConstructL()
	{
	__FLOG_OPEN(KMDNSSubsystem, KComponent);
	CTimer::ConstructL();
	iCurrentProbeState = EIdle;
	}
/*
 * Destructor
 */
CMDNSProbeManager::~CMDNSProbeManager()
	{
	__FLOG(_L8("CMDNSProbeManager::~CMDNSProbeManager - Entry"));
	iName.Close();
	iTarget.Close();
	iProbeName.Close();
	iProbeType.Close();
	__FLOG(_L8("CMDNSProbeManager::~CMDNSProbeManager - Exit"));
	__FLOG_CLOSE;
	}

/*
 * State machine which probe send announcement and handle conflicts 
 * if autoresolve enabled.
 */
void CMDNSProbeManager::RunL()
	{	
	__FLOG(_L8("CMDNSProbeManager::RunL - Entry"));
	TTime currentTime;

	if(iCurrentProbeState <= EProbeComplete)
		{
		if(DefensiveResponseL())
			{		
			iCurrentProbeState = EStart;				
			if(iAutoResolveEnabled)
				{
				GenerateNonConflictingName();	
				}		
			//	else { }//  to intimate server of failure
			if(iProbeCounter >= KMaxFailures)
				{
				//If Probe Failure rate is greater then 15 per second, wait for 
				//5 seconds before probing again
				iProbeCounter = 0;
				After(5*1000*1000);				
				}
			}		
		}

	switch(iCurrentProbeState)
		{		
		case EIdle:
		break;//Do Nothing
	
		case EStart:
			{
			currentTime.UniversalTime();
			TInt64 randseed = currentTime.Int64();					
	
			//Random time between 0 & 250 ms
			TInt delay = Math::Rand(randseed) % 250;
			iCurrentProbeState = EProbeFirstUnicast;
			//Convert to microsecond
	
			After(delay*1000);			
			}				
			break;
	
		case EProbeFirstUnicast:
			{
			SendProbeL(ETrue);	
			iCurrentProbeState = EProbeSecondUnicast;							
			}
			break;
	
		case EProbeSecondUnicast:
			{				
			SendProbeL(ETrue);	
			iCurrentProbeState = EProbeMulticast;										
			}
			break;		
	
		case EProbeMulticast:
			{
			SendProbeL(EFalse);	
			iCurrentProbeState = EProbeComplete;			
			}
			break;
	
		case EProbeComplete:
			{
			iCurrentProbeState = EFirstAnnouncement;
			//Probe succeeded ,insert in cache 
			InsertInCache();
			iMessageHandler.Server().SetHostNameL(iName);
			After(KProbeDelay*1000);
			//Some Delay ?? Required or defensive response HAS TO come within 750ms?
			}
			break;
	
		case EFirstAnnouncement:
			{
			SendAnnouncementL();
			iCurrentProbeState = ESecondAnnouncement;
			//After(1000);// A Millisecond delay: Required??	
			}
			break;
	
		case ESecondAnnouncement:
			{
			SendAnnouncementL();
			iCurrentProbeState = EIdle;
			}
			break;	
		}
	__FLOG(_L8("CMDNSProbeManager::RunL - Exit"));
	}

/*
 * Handles any leave from RunL
 */
TInt CMDNSProbeManager::RunError(TInt aError)
	{
	__FLOG(_L8("CMDNSProbeManager::RunError - Entry Exit"));
	return aError;
	}

/*
 * Probe host
 * @param aUnicast True if unicast or else multicast.
 */
void CMDNSProbeManager::SendProbeL(TBool aUnicast)
	{
	__FLOG(_L8("CMDNSProbeManager::SendProbeL - Entry"));
	//Construct DNS Message
	CDnsMessage* message = CDnsMessage::NewL(0,ETrue);
	CleanupStack::PushL(message);

	//Form the Query/Question part of the message
	CDnsQuestion* question = CDnsQuestion::NewL();
	CleanupStack::PushL(question);
	question->SetNameL(iName);
	question->SetClass(EDnsClass_IN);
	question->SetType(EDnsQType_Any);
	if(aUnicast)
		{
		question->SetUnicast(ETrue);
		}

	//Append the Query to the Message
	message->AppendQueryL(question);


	//Form the Record to be filled in the Authoritative Field of the Query
	switch(iType)
	{
	case EDnsType_A:
		{
		CRdTypeA* addRec = FormAddressRecordL();			
		//Append to the Authoritative Section
		message->AppendAuthorityL(addRec);
		}
		break;

	case EDnsType_SRV:
		{							
		CRdTypeSrv* srvRec = FormServiceRecordL();
		//Append to the Authoritative Section
		message->AppendAuthorityL(srvRec);	
		}

		break;			
	}		
	//Send the query
	iMessageHandler.SendQueryL(message,*this);

	CleanupStack::Pop();//question
	CleanupStack::Pop();//message
	__FLOG(_L8("CMDNSProbeManager::SendProbeL - Exit"));
	}


/*
 * Send an announcement if probing is successfull.
 */
void CMDNSProbeManager::SendAnnouncementL()
	{
	__FLOG(_L8("CMDNSProbeManager::SendAnnouncementL - Entry"));
	RPointerArray<CCacheEntry> entries;
	

	//An API in Cache Interface that returns a list of all Authoritative records
	iCache.AuthoritativeEntriesL(entries);
		
	for(TInt index =0; index<entries.Count();index++)
		{
		CCacheEntry* entry = entries[index];
		//Send Announcements for all the Services we have published, and entered in Cache			
		CDnsMessage* message = CDnsMessage::NewL(0,EFalse);
		CleanupStack::PushL(message);			
		
	    TDnsHeader header(message->Header());
	    header.SetAuthoritative(ETrue);
	    message->SetHeader(header);
		
		if(entry->AddressRecord())
		    message->AppendAnswerL(entry->AddressRecord()->CloneL());
		if(entry->ServiceRecord())
		    message->AppendAnswerL(entry->ServiceRecord()->CloneL());
		if(entry->PtrRecord())
		    message->AppendAnswerL(entry->PtrRecord()->CloneL());
		if(entry->TxtRecord())
		    message->AppendAnswerL(entry->TxtRecord()->CloneL());

		//should've been a new API sendresponse
		
		iMessageHandler.SendQueryL(message,*this);
		 
		CleanupStack::Pop();//message
		entries.ResetAndDestroy();
		entries.Close();
		}
	__FLOG(_L8("CMDNSProbeManager::SendAnnouncementL - Exit"));
	}

/*
 * Returns ture hostname exists in the cache.
 * True means there is already a record in the network with the same name.
 */
TBool CMDNSProbeManager::DefensiveResponseL()
	{
	__FLOG(_L8("CMDNSProbeManager::DefensiveResponseL - Entry"));
	TBool probeFailed(EFalse);		
	RPointerArray <CCacheEntry> entries;
	CleanupStack::PushL(TCleanupItem(TCleanupOperation(&CleanUpCache),&entries));
		
	_LIT8(KDot,".");
	TBuf8 <KMaxLength> buffer;	
	
	buffer.Copy(iName);
	buffer.Append(KDot);
	
	iCache.FindServiceL(entries,buffer,EDnsQType_Any);
	
	if(entries.Count()>0)
		{
		probeFailed = ETrue;
		}
	
    CleanupStack::PopAndDestroy();
    entries.ResetAndDestroy();
    entries.Close();	
    __FLOG(_L8("CMDNSProbeManager::DefensiveResponseL - Exit"));
	return probeFailed;
	}

TInt CMDNSProbeManager::StartNameProbeL(const TDesC8& aHostName,TInetAddr aAddr)
	{
	__FLOG(_L8("CMDNSProbeManager::StartNameProbeL - Entry"));
	iName.Close();
	iName.CreateL(aHostName);
    iProbeName.Close();
    iProbeName.CreateL(KMaxLength);
    iProbeName.Append(iName);
	iAddr = aAddr;
	iCurrentProbeState =  EStart;
	iType = EDnsType_A; 
    iMessageHandler.SetStateHostProbing(ETrue);
	Schedule();
	__FLOG(_L8("CMDNSProbeManager::StartNameProbeL - Exit"));
	return KErrNone;
	}

/*
 * Start probing the service.
 */
TInt CMDNSProbeManager::StartServiceProbeL(const TDesC8& aServiceName,const TDesC8& aTargetMachine,TUint16 aPort)
	{
	__FLOG(_L8("CMDNSProbeManager::StartServiceProbeL - Entry"));
	iName.Close();
	iName.CreateL(aServiceName);
	
	iTarget.Close();
	iTarget.CreateL(aTargetMachine);
	
	iPort = aPort;
	iType = EDnsType_SRV;

	iCurrentProbeState =  EStart;

	Schedule();
	__FLOG(_L8("CMDNSProbeManager::StartServiceProbeL - Exit"));
	return KErrNone;
	}

	
/*
 * Callback method
 * Will be notified on successfully sending a packet to the network .
 * State will be EIdle when an address is successully announced twice.
 */
void CMDNSProbeManager::OnPacketSendL(TInt /*aError*/)
	{
	__FLOG(_L8("CMDNSProbeManager::OnPacketSendL - Entry"));
	if( iCurrentProbeState != EIdle)
	    {
	    After(KProbeDelay*1000);//Give more delay than this
	    return;
	    }
	iMessageHandler.SetStateHostProbing(EFalse);
	__FLOG(_L8("CMDNSProbeManager::OnPacketSendL - Exit"));
	}


/*
 * Create an address record
 * @return returns an address record created.
 */
CRdTypeA* CMDNSProbeManager::FormAddressRecordL()
	{
	__FLOG(_L8("CMDNSProbeManager::FormAddressRecordL - Entry"));
	CRdTypeA* addRec = CRdTypeA::NewL();
	CleanupStack::PushL(addRec);

	TInt dataLength(4);

	addRec->SetNameL(iName);	
	addRec->SetType(EDnsType_A);
	addRec->SetClass(EDnsClass_IN);
	addRec->SetFlushBit(EFalse);
	addRec->SetAddr(iAddr);
	addRec->SetTtl(120);

	CleanupStack::Pop();//addRec
	__FLOG(_L8("CMDNSProbeManager::FormAddressRecordL - Exit"));
	return addRec;
	}

/*
 * Create a service record
 * @return retrun the newly created service record.
 */
CRdTypeSrv* CMDNSProbeManager::FormServiceRecordL()
	{
	__FLOG(_L8("CMDNSProbeManager::FormServiceRecordL - Entry"));
	CRdTypeSrv* srvRec = CRdTypeSrv::NewL();
	CleanupStack::PushL(srvRec);

	srvRec->SetClass(EDnsClass_IN);
	srvRec->SetFlushBit(EFalse);
	srvRec->SetNameL(iName);
	srvRec->SetPort(iPort);	
	srvRec->SetPriority(/*Default Priority*/0 );
	srvRec->SetTargetL(iTarget);
	srvRec->SetTtl(120*60);
	srvRec->SetType(EDnsType_SRV);
	srvRec->SetWeight(/*Default Weight*/ 0);


	CleanupStack::Pop();//srvRec
	__FLOG(_L8("CMDNSProbeManager::FormServiceRecordL - Exit"));
	return srvRec;
	}
	
void CMDNSProbeManager::InsertInCache()
	{
	__FLOG(_L8("CMDNSProbeManager::InsertInCache - Entry"));
	TBuf8<KMaxLength> name;
	_LIT8(KDot,".");

	CDnsResourceData* data = NULL;
	
	switch(iType)
		{		
			case EDnsType_A:
			{	
			data = FormAddressRecordL();
			}
			break;
			
			case EDnsType_SRV:
			{
			data = FormServiceRecordL();
			}
			break;
												
		}		
	
	//Append  a "." at the end of the name
	name.Copy(iName);
	name.Append(KDot);	
	data->SetNameL(name);
		
	iCache.UpdateCacheL(*data,ETrue,0/*session Id*/);
	
	delete data;
	__FLOG(_L8("CMDNSProbeManager::InsertInCache - Exit"));
	}




/*
 * If autoresolve enable a non conflict name will be generated using this.
 * @return returns the probecount.
 */
TInt CMDNSProbeManager::GenerateNonConflictingName()
	{		
/*
	__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName - Entry"));
	_LIT8(KDot,".");
	
	if(iProbeCounter)
		{
		//From 2nd time, chop the last 3 characters
		iProbeName.SetLength(iProbeName.Length() - 3);
		}
		
	++iProbeCounter;
	
	TBuf8 <KMaxLength> newName;
	newName.Append(iProbeName);
	
	_LIT8(KOpenBrace,"(");
	newName.Append(KOpenBrace);		
	
	newName.AppendNum(iProbeCounter);
	
	_LIT8(KCloseBrace,")");
	newName.Append(KCloseBrace);
	
	iProbeName.Close();	
	iProbeName.Create(KMaxNameLength);
	iProbeName.Append(newName);
		
	iName.Close();	
	iName.Create(KMaxLength);
	iName.Append(iProbeName);

	iName.Append(KDot);
	iName.Append(iProbeType);
	
	__FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName - Exit"));
	return iProbeCounter;			
*/	
	//-------------------------------------------------------
    __FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName- Entry"));
    _LIT8(KDot,".");
        
    ++iProbeCounter;
    
    TBuf8 <KMaxLength> newName;
   
    iName.Close();  
    iName.Create(KMaxLength);
    iName.Append(iProbeName);

    TInt pos = iName.Locate('.');
    _LIT8(KOpenBrace,"(");
    newName.Append(KOpenBrace);
    newName.AppendNum(iProbeCounter);
    _LIT8(KCloseBrace,")");
    newName.Append(KCloseBrace);
    iName.Insert(pos,newName); 
    //ChangeDomainL(iName);
   
    __FLOG(_L8("CMDNSProbeManager::GenerateNonConflictingName- Exit"));
    return iProbeCounter;
	
	
	
	
	
	}
		
/*
 * Self complete the request.
 */
	
void CMDNSProbeManager::Schedule()
	{
	__FLOG(_L8("CMDNSProbeManager::Schedule - Entry"));
	TRequestStatus* status(&iStatus);
	*status = KRequestPending;
	SetActive();
	User::RequestComplete(status, KErrNone);
	__FLOG(_L8("CMDNSProbeManager::Schedule - Exit"));
	}


