// 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:
// cresponsehandler.cpp
// 
//
/**
@file
@internalTechnology
*/

//System include 
#include <mdns/ccacheentry.h>

//User include 
#include "cresponsehandler.h"
__FLOG_STMT(_LIT8(KComponent,"MDNSServer");)
/**
Two phase constructor
@param aMessageHandler a reference to messagehandler
*/
CResponseHandler* CResponseHandler::NewL(CMessageHandler& aMessageHandler)
	{
	CResponseHandler* self = new(ELeave) CResponseHandler(aMessageHandler);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
	}

/**
Destructor
*/
CResponseHandler::~CResponseHandler()
	{
	__FLOG(_L8("CResponseHandler::~CResponseHandler - Entry"));
	iNotifyPtrName.Close();
	__FLOG(_L8("CResponseHandler::~CResponseHandler - Exit"));
	__FLOG_CLOSE;
	}

/**
Handles any resopnse packet recieved from the mdns port
@param response packet
@return void
*/
void CResponseHandler::HandleIncomingPacketL(CDnsMessage& aMessage, const TSockAddr& /*aAddr*/)
	{
	__FLOG(_L8("CResponseHandler::HandleIncomingPacketL - Entry"));
	iNotifyPtrName.Close();
	// process the answer section, and add to the host and service caches
	TInt aCount(aMessage.Header().AnswerCount());
	const RPointerArray<CDnsResourceData>& answers = aMessage.Answers();
	for (TInt i = 0; i < aCount; ++i)
		{
		HandlePacketAnswersL(answers[i]);	
		}
	aCount = aMessage.Header().AdditionalRCount();
	const RPointerArray<CDnsResourceData>& additional = aMessage.Additional();
	for (TInt i = 0; i < aCount; ++i)
		{
        if(additional[i]!=NULL)            
		HandlePacketAnswersL(additional[i]);	
		}
    aCount = aMessage.Header().AuthorityNSCount();
    const RPointerArray<CDnsResourceData>& authoritative = aMessage.Authorities();
    for (TInt i = 0; i < aCount; ++i)
        {
        if(authoritative[i]!=NULL)
        HandlePacketAnswersL(authoritative[i]);    
        }
	
	if(iNotifyPtrName.Count() > 0 )
	    {
	    MessageHandler().NotifyNewServiceL(iNotifyPtrName);
	    for(TInt i=0 ; i<iNotifyPtrName.Count() ; i++)
	        {
	        iNotifyPtrName[i].Close();
	        }
	    iNotifyPtrName.Close();
	    }
	__FLOG(_L8("CResponseHandler::HandleIncomingPacketL - Exit"));
	}

/*
Constructor
*/
CResponseHandler::CResponseHandler(CMessageHandler& aMessageHandler):CBaseHandler(aMessageHandler)
	{
	
	}
	
/**
Two phasae constructor
*/
void CResponseHandler::ConstructL()
	{
	__FLOG_OPEN(KMDNSSubsystem, KComponent);
	__FLOG(_L8("CResponseHandler::ConstructL - Entry Exit"));
	}
/*
 * Based on the type of query response recieved from the network.
 * it will route the record to particular interface.
 * @param aResourceData record recieved from the network.
 */
void CResponseHandler::HandlePacketAnswersL(CDnsResourceData* aResourceData)
	{
	__FLOG(_L8("CResponseHandler::HandlePacketAnswersL - Entry"));
	switch(aResourceData->Type())
		{
			case EDnsType_A:
				{
				HandleAddrAnswersL(aResourceData);
				break;	
				}
				
			case EDnsType_PTR:
				{
				HandlePTRAnswersL(aResourceData);
				break;	
				}
				
			case EDnsType_TXT:
				{
				HandleTXTAnswersL(aResourceData);
				break;	
				}
				
			case EDnsType_AAAA:
				{
				HandleAAAAAnswersL(aResourceData);
				//MessageHandler().DnsCache().Insert();
				break;	
				}

			case EDnsType_SRV:
				{
				HandleSRVAnswersL(aResourceData);
				break;	
				}
		}
	__FLOG(_L8("CResponseHandler::HandlePacketAnswersL - Exit"));
	}

/**
This handles any address content in the answer section .
If the cache already contains a address entry with the same name .This will notify the server to change the name.
If the cache doesnt contain the entry it will just add this entry in to it.
@param aResourceData Contains the answers and nspackets recieved from the mdns port
@param aEntry Reference to entry to which record should be added so as to add the same to cache later.
@return void 
*/	
void CResponseHandler::HandleAddrAnswersL(CDnsResourceData* aResourceData)
	{
	__FLOG(_L8("CResponseHandler::HandleAddrAnswersL - Entry"));
	CRdTypeA* addrRecord = static_cast<CRdTypeA*>(aResourceData);
	CleanupStack::PushL(addrRecord);
	TBufC8<100> domainName(addrRecord->Name());
	RPointerArray<CCacheEntry> aCacheArray;
	TBool isUpdated = EFalse;
	TRAPD(error,MessageHandler().DnsCache().FindServiceL(aCacheArray,domainName,EDnsType_A));
	if(error == KErrNone && 0 != aCacheArray.Count())
		{
		const CRdTypeA* addrRecordCEntry = (aCacheArray[0])->AddressRecord();
		if(addrRecordCEntry != NULL && addrRecordCEntry->Name().Compare(domainName) == 0)
			{
			if(aCacheArray[0]->IsAuthoritative())
				{
				if(addrRecordCEntry->Address() != addrRecord->Address() && addrRecord->IsFlushBitSet())
					{
					MessageHandler().DnsCache().UpdateCacheL(*addrRecord,EFalse,0);
					isUpdated = ETrue;
					}
				}
			else
				{
				isUpdated = ETrue;
				MessageHandler().DnsCache().UpdateCacheL(*addrRecord,EFalse,0);
				}
							
			}
			
		}
		
	if (! isUpdated&& 0 != aResourceData->Ttl())
		{
		// we didn't find it, add a new entry.
		MessageHandler().DnsCache().UpdateCacheL(*addrRecord,EFalse,0);
		}
	aCacheArray.ResetAndDestroy();	
	aCacheArray.Close();	
	CleanupStack::Pop();//addrRecord
	__FLOG(_L8("CResponseHandler::HandleAddrAnswersL - Exit"));
	}

/*
 * Handles any PTR records in the answer section.
 * 1.Updates the cache if not present .
 * 2.Notitfies the client about the new service availability if registered for it.
 * @param aResourceData ptr record recieved from the network. 
 */
 
void CResponseHandler::HandlePTRAnswersL(CDnsResourceData* aResourceData)
	{
	__FLOG(_L8("CResponseHandler::HandlePTRAnswersL - Entry"));
	CRdTypePtr* ptrEntry = static_cast<CRdTypePtr*>(aResourceData);
	CleanupStack::PushL(ptrEntry);
	TBufC8<100> name(ptrEntry->Name());
	RPointerArray<CCacheEntry> aCacheArray;
	MessageHandler().DnsCache().FindServiceL(aCacheArray,name,EDnsType_PTR);
	TBool isUpdated = EFalse;
	for (TInt i =0 ; i< aCacheArray.Count(); i++)
		{
		if(NULL != aCacheArray[i])
			{
			const CRdTypePtr* ptrRecord = aCacheArray[i]->PtrRecord();
			if(ptrRecord->DomainName().CompareF(ptrEntry->DomainName()) == 0)
				{
				MessageHandler().DnsCache().UpdateCacheL(*ptrEntry,EFalse,0);
				isUpdated = ETrue;
				if(aResourceData->Ttl() == 0)
				    {
				    if(ptrEntry->DomainName().Find(ptrEntry->Name()) != KErrNotFound)
                        {
                        RBuf8 temp;
                        temp.CreateL(ptrEntry->DomainName());
                        if(iNotifyPtrName.Find(temp) == KErrNotFound)
                            {
                            iNotifyPtrName.AppendL(temp);
                            }
                        else
                            {
                            temp.Close();
                            }
                        }
				    CRdTypeSrv* srvRecord = static_cast <CRdTypeSrv*>( aCacheArray[i]->ServiceRecord());
				    if(srvRecord != NULL)
				        {
				        srvRecord->SetTtl(0);
				        MessageHandler().DnsCache().UpdateCacheL(*srvRecord,EFalse,0);
				        }
				    CRdTypeTxt* txtRecord = static_cast <CRdTypeTxt*> (aCacheArray[i]->TxtRecord());
                    if(txtRecord != NULL)
                        {
                        txtRecord->SetTtl(0);
                        MessageHandler().DnsCache().UpdateCacheL(*txtRecord,EFalse,0);
                        }
				    }
				
				break;	
				}
			}
		}
	if(!isUpdated && 0 != aResourceData->Ttl())
		{
		MessageHandler().DnsCache().UpdateCacheL(*ptrEntry,EFalse,0);	
		//Notify the  client about new service
		if(ptrEntry->DomainName().Find(ptrEntry->Name()) != KErrNotFound)
		    {
		    RBuf8 temp;
		    temp.CreateL(ptrEntry->DomainName());
		    if(iNotifyPtrName.Find(temp) == KErrNotFound)
                {
                iNotifyPtrName.AppendL(temp);
                }
            else
                {
                temp.Close();
                }
		    }
        }
	aCacheArray.ResetAndDestroy();	
	aCacheArray.Close();
	CleanupStack::Pop(ptrEntry);
	__FLOG(_L8("CResponseHandler::HandlePTRAnswersL - Exit"));
	}

/*
 * Handles any TXT records in the answer section.
 * 1.Updates the cache if not present .
 * @param aResourceData ptr record recieved from the network. 
 */
void CResponseHandler::HandleTXTAnswersL(CDnsResourceData* aResourceData)
	{
	__FLOG(_L8("CResponseHandler::HandleTXTAnswersL - Entry"));
	CRdTypeTxt* txtEntry = static_cast<CRdTypeTxt*>(aResourceData);
	CleanupStack::PushL(txtEntry);
	RPointerArray<CCacheEntry> aCacheArray;
	TBool isUpdated = EFalse;
	TRAPD(error,MessageHandler().DnsCache().FindServiceL(aCacheArray,txtEntry->Name(),EDnsType_TXT));
	if(error == KErrNone && 0 != aCacheArray.Count() && NULL != aCacheArray[0]->TxtRecord())
		{
		const CRdTypeTxt* txtRecord = aCacheArray[0]->TxtRecord();
		if(NULL != txtRecord && txtRecord->Name().CompareF(txtEntry->Name()) == 0)
			{
			if(aCacheArray[0]->IsAuthoritative())
				{
				isUpdated = ETrue;
				MessageHandler().DnsCache().UpdateCacheL(*txtEntry,ETrue,0);	
				}
			else 
				{
				isUpdated = ETrue;
				MessageHandler().DnsCache().UpdateCacheL(*txtEntry,EFalse,0);		
				}
					
			}
		}
	if(!isUpdated && aResourceData->Ttl() != 0 )
		{
		MessageHandler().DnsCache().UpdateCacheL(*txtEntry,EFalse,0);
		}
	RBuf8 temp;
    temp.CreateL(txtEntry->Name());
    if(iNotifyPtrName.Find(temp) == KErrNotFound)
        {
        iNotifyPtrName.AppendL(temp);
        }
    else
        {
        temp.Close();
        }
	aCacheArray.ResetAndDestroy();	
	aCacheArray.Close();
	CleanupStack::Pop();//txtEntry*/
	__FLOG(_L8("CResponseHandler::HandleTXTAnswersL - Exit"));
	}
	
void CResponseHandler::HandleAAAAAnswersL(CDnsResourceData* /*aResourceData*/)
	{
		
	}

/*
 * Handles any SRV records in the answer section.
 * 1.Updates the cache if not present .
 * @param aResourceData ptr record recieved from the network. 
 */
void CResponseHandler::HandleSRVAnswersL(CDnsResourceData* aResourceData)
	{
	__FLOG(_L8("CResponseHandler::HandleSRVAnswersL - Entry"));
	CRdTypeSrv* srvEntry = static_cast<CRdTypeSrv*>(aResourceData);
	CleanupStack::PushL(srvEntry);
	RPointerArray<CCacheEntry> cacheArray;
	TBool isUpdated = EFalse;
	TRAPD(error,MessageHandler().DnsCache().FindServiceL(cacheArray,srvEntry->Name(),EDnsType_SRV));
	if(KErrNone == error && cacheArray.Count() != 0 && NULL != cacheArray[0]->ServiceRecord())
		{
	const CRdTypeSrv* srvRecord = cacheArray[0]->ServiceRecord();
	/*if(aResourceData->Ttl() == 0)
        {
        if(srvEntry->Name().Find(srvEntry->Target()) != KErrNotFound)
            {
            RBuf8 temp;
            temp.CreateL(srvEntry->Name());
            if(iNotifyPtrName.Find(temp) == KErrNotFound)
                {
                iNotifyPtrName.AppendL(temp);
                }
            else
                {
                temp.Close();
                }
            }
        CRdTypeSrv* srvRecord = static_cast <CRdTypeSrv*>( cacheArray[i]->ServiceRecord());
        if(srvRecord != NULL)
            {
            srvRecord->SetTtl(0);
            MessageHandler().DnsCache().UpdateCacheL(*srvRecord,EFalse,0);
            }
        CRdTypeTxt* txtRecord = static_cast <CRdTypeTxt*> (aCacheArray[i]->TxtRecord());
        if(txtRecord != NULL)
            {
            txtRecord->SetTtl(0);
            MessageHandler().DnsCache().UpdateCacheL(*txtRecord,EFalse,0);
            }
        }*/
		
		if(srvRecord->Name().CompareF(srvEntry->Name()) == 0 && srvEntry->IsFlushBitSet())
			{
			if(cacheArray[0]->IsAuthoritative())
				{
				isUpdated = ETrue;
				MessageHandler().DnsCache().UpdateCacheL(*srvEntry,ETrue,0);
				}
			else 
				{
				isUpdated = ETrue;
				MessageHandler().DnsCache().UpdateCacheL(*srvEntry,EFalse,0);	
				}
			}
			
		}
	RBuf8 temp;
    temp.CreateL(srvEntry->Name());
    if(iNotifyPtrName.Find(temp) == KErrNotFound)
        {
        iNotifyPtrName.AppendL(temp);
        }
    else
        {
        temp.Close();
        }
	if(!isUpdated && srvEntry->Ttl() != 0)
		{
		MessageHandler().DnsCache().UpdateCacheL(*srvEntry,EFalse,0);
		}
	cacheArray.ResetAndDestroy();
	cacheArray.Close();
	CleanupStack::Pop();//srvEntry	
	__FLOG(_L8("CResponseHandler::HandleSRVAnswersL - Exit"));
	}	
	
