/*
* Copyright (c) 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: 
*
*/


#include "cdnsmessagecomposerparser.h"
#include "cdnsmsgbuf.h"
#include "cdnspacket.h"


__FLOG_STMT(_LIT8(KSubsys,"Dns Message Composer Parser");)
__FLOG_STMT(_LIT8(KComponent,"DnsMessage");)



CDnsMessageComposerParser::CDnsMessageComposerParser()
    {
    }


EXPORT_C CDnsMessageComposerParser* CDnsMessageComposerParser::NewL()
    {
    CDnsMessageComposerParser* self= new(ELeave)CDnsMessageComposerParser;
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
    }
 

CDnsMessageComposerParser::~CDnsMessageComposerParser()
    {
    iBuf.Close();
    delete iPacket;
    delete iMsgBuf;
    __FLOG(_L8("-> Composer Parser destroyed"));
    __FLOG_CLOSE;
    }


void CDnsMessageComposerParser::ConstructL()
    {   
	__FLOG_OPEN(KSubsys, KComponent);
	__FLOG(_L8("-> Composer parser created"));
    }
        

/**
 Composes mdns message
 @param aMessage
 @param aType RR type
 @param aUnicast 
 @return iBuf composed dns buffer
 */
EXPORT_C void CDnsMessageComposerParser::CreateMessageL(RBuf8& aMessageBuffer, const CDnsMessage& aMessage )
    {
     __FLOG(_L8("-> Composing Dns Message"));
       
    aMessageBuffer.CreateL(GetLength(aMessage));   
    
    iMsgBuf = CDnsMsgBuf::NewL(aMessageBuffer);

    const TDnsHeader& header = aMessage.Header();  
    iMsgBuf->SetInt16(header.Id());
    iMsgBuf->SetInt16(header.Flags());
      	   
    const RPointerArray<CDnsQuestion>& quesList = aMessage.Queries();
    const RPointerArray<CDnsResourceData>& ansList = aMessage.Answers();
    const RPointerArray<CDnsResourceData>& authList= aMessage.Authorities();
    const RPointerArray<CDnsResourceData>& additionalList = aMessage.Additional();
    
    iMsgBuf->SetInt16(quesList.Count());
    iMsgBuf->SetInt16(ansList.Count());
    iMsgBuf->SetInt16(authList.Count());
    iMsgBuf->SetInt16(additionalList.Count());
    
    TInt i;    
    for(i=0; i<quesList.Count(); i++ )
    	{
    	CDnsQuestion* ques = quesList[i];
        
    	iMsgBuf->SetDomainNameL(ques->Name());    	 
    	iMsgBuf->SetInt16(ques->Type());
    	iMsgBuf->SetInt16(ques->Class());
    	}  
    
    // add answer section
    for(i=0;  i<ansList.Count(); i++ )
    	{
    	AppendResourceRecordsL(ansList[i]);
    	}
    
    // add authority section
    for(i=0;  i<authList.Count(); i++ )
    	{
    	AppendResourceRecordsL(authList[i]);
    	}
    
    // add additional section
    for(i=0;  i<additionalList.Count(); i++ )
    	{
    	AppendResourceRecordsL(additionalList[i]);
    	}   
    aMessageBuffer.SetLength(GetLength(aMessage));
    }


/**
 Parses the raw dns packet
 @param aBuf
 @return dnsMessage Dns Message Structure
 */
EXPORT_C CDnsMessage* CDnsMessageComposerParser::ParseMessageL(const TDesC8& aBuf)
   { 
   __FLOG(_L8("-> Parsing Dns Raw Buffer"));
   iBuf.CreateL(aBuf);  
   iPacket = CDnsPacket::NewL(iBuf.LeftTPtr(iBuf.Length()));
   
   const TDnsHeader& header = iPacket->GetHeaderL();
  
   CDnsMessage* dnsMessage = CDnsMessage::NewL(header.Id(),header.IsQuery());
   CleanupStack::PushL(dnsMessage);
   
   dnsMessage->SetHeader(header);
   
   TInt i;
   // extract the query section;
   for(i=0; i<header.QueryCount(); i++)
	   {
	   __FLOG(_L8("-> Retrieving Query Section"));
	   RBuf8 domainName;
	   domainName.CreateL(KMaxDNSNameLength);
	   iPacket->GetDomainNameL(domainName);
	   CDnsQuestion* question = CDnsQuestion::NewL();
	   CleanupStack::PushL(question);
	   question->SetNameL(domainName);
	   question->SetType(iPacket->GetInt16L());
	   question->SetClass(iPacket->GetInt16L());	   
	   domainName.Close();
	   dnsMessage->AppendQueryL(question);
	   CleanupStack::Pop(question);
	   }
   
   	// extract the answer section
   for(i=0; i<header.AnswerCount(); i++)
	   {  
	   __FLOG(_L8("-> Retrieving  Answer Section"));
       CDnsResourceData* resourceRecord = GetResourceRecordsL();
       CleanupStack::PushL(resourceRecord);
	   dnsMessage->AppendAnswerL(resourceRecord);
	   CleanupStack::Pop(resourceRecord);
	   }
   
  	// extract the authority section
   for(i=0; i<header.AuthorityNSCount(); i++)
	   {
	   __FLOG(_L8("-> Retrieving  Authority NS Section"));
	   CDnsResourceData* resourceRecord = GetResourceRecordsL();
	   CleanupStack::PushL(resourceRecord);
	   dnsMessage->AppendAuthorityL(resourceRecord);
	   CleanupStack::Pop(resourceRecord);
	   }

	// extract the additional section
	for(i=0; i<header.AdditionalRCount(); i++)
		{
		 __FLOG(_L8("-> Retrieving Additional Record Section"));
	    CDnsResourceData* resourceRecord = GetResourceRecordsL();
	    CleanupStack::PushL(resourceRecord);
		dnsMessage->AppendAdditionalL(resourceRecord);
		CleanupStack::Pop(resourceRecord);
		} 		
	
   CleanupStack::Pop(dnsMessage);
   return dnsMessage;
   }


CDnsResourceData* CDnsMessageComposerParser::GetResourceRecordsL()
	{
	__FLOG(_L8("-> Retrieving Resource Records"));
	RBuf8 name;
	name.CreateL(KMaxDNSNameLength);
	iPacket->GetDomainNameL(name);
	TUint16 type = iPacket->GetInt16L();
	TUint16 classType = iPacket->GetInt16L();
	TUint32 ttl = iPacket->GetInt32L();
	TUint16 rdLength = iPacket->GetInt16L();
	
	CDnsResourceData* resourceData = NULL;
	
	switch(type)
		{
		case EDnsType_A	: // A record
			{
			TInetAddr addr;
			addr.SetAddress(iPacket->GetInt32L());
			
			CRdTypeA* addrRecord = CRdTypeA::NewL();
			CleanupStack::PushL(addrRecord);			
			
			addrRecord->SetNameL(name);
			addrRecord->SetType(type);
			addrRecord->SetClass(classType);
			addrRecord->SetTtl(ttl);
			addrRecord->SetRdLength(rdLength);
			addrRecord->SetAddr(addr);
					
			CleanupStack::Pop(addrRecord);
			resourceData = addrRecord;
			}
		    break;			
	    case EDnsType_PTR: // PTR record
	    	{
			RBuf8 ptrDomainName;
			ptrDomainName.CreateL(KMaxDNSNameLength);
			iPacket->GetRdDomainNameL(ptrDomainName,rdLength);
			
			CRdTypePtr* ptrRecord = CRdTypePtr::NewL();
			CleanupStack::PushL(ptrRecord);
			
			ptrRecord->SetNameL(name);
			ptrRecord->SetType(type);
			ptrRecord->SetClass(classType);
			ptrRecord->SetTtl(ttl);
			ptrRecord->SetRdLength(rdLength);						
			ptrRecord->SetDomainNameL(ptrDomainName);
			
			ptrDomainName.Close();			
			CleanupStack::Pop(ptrRecord);
			resourceData = ptrRecord;
	    	}
		    break;	
		case EDnsType_SRV: // SRC record
			{
			TUint16 priority = iPacket->GetInt16L();
			TUint16 weight = iPacket->GetInt16L();
			TUint16 port = iPacket->GetInt16L();
			RBuf8 srvDomainName;
			srvDomainName.CreateL(KMaxDNSNameLength); 
			TInt length1 = sizeof(priority);
			TInt length2 = sizeof(weight);
			TInt length3 = sizeof(port);
			TInt length =  length1+length2+length3; // 6 = sizeof( prio + weight + port )
			iPacket->GetRdDomainNameL(srvDomainName,rdLength - length); 
			
			CRdTypeSrv* srvRecord = CRdTypeSrv::NewL();
			CleanupStack::PushL(srvRecord);
			
			srvRecord->SetNameL(name);
			srvRecord->SetType(type);
			srvRecord->SetClass(classType);
			srvRecord->SetTtl(ttl);
			srvRecord->SetRdLength(rdLength);
			srvRecord->SetPriority(priority);
			srvRecord->SetWeight(weight);
			srvRecord->SetPort(port);						
			srvRecord->SetTargetL(srvDomainName);
			
			srvDomainName.Close();			
			CleanupStack::Pop(srvRecord);
			resourceData = srvRecord;
			}
		    break;		
		case EDnsType_TXT: // TXT record
			{
			TInt strLength = 0;
			CRdTypeTxt* txtRecord = CRdTypeTxt::NewL();
			CleanupStack::PushL(txtRecord);
			
			txtRecord->SetNameL(name);
			txtRecord->SetType(type);
			txtRecord->SetClass(classType);
			txtRecord->SetTtl(ttl);
			txtRecord->SetRdLength(rdLength);					
			
	    	while(rdLength)
				{
				RBuf8 txtString;
				strLength = iPacket->GetCharL();
				rdLength--;
				txtString.CreateL(strLength);
				iPacket->GetStringL(txtString,strLength);
				rdLength -= strLength;
				txtRecord->AppendTextDataL(txtString);	
				}	    	
	    	CleanupStack::Pop(txtRecord);
	    	resourceData = txtRecord;
			}
		    break;			
		default:
			//User::Leave(KErrCorrupt);
		    break;
	    }
	name.Close();
	return resourceData;
	}


void CDnsMessageComposerParser::AppendResourceRecordsL( CDnsResourceData* aResourceRecord )
	{
	__FLOG(_L8("-> Adding Resource Records to buffer"));
	
	iMsgBuf->SetDomainNameL(aResourceRecord->Name()); 
	iMsgBuf->SetInt16(aResourceRecord->Type());
	iMsgBuf->SetInt16(aResourceRecord->Class());
	iMsgBuf->SetInt32(aResourceRecord->Ttl());
		
	switch( aResourceRecord->Type())
		{
		case EDnsType_A: // A record 
			{
			CRdTypeA* addrRecord = static_cast<CRdTypeA*>(aResourceRecord);	
			const TInetAddr& addr = addrRecord->Address();
			iMsgBuf->SetInt16(4);
			iMsgBuf->SetInt32(addr.Address());
			}
		    break;	
		case EDnsType_PTR: // PTR record	
			{
			CRdTypePtr* ptrRecord = static_cast<CRdTypePtr*>(aResourceRecord);
			iMsgBuf->SetPtrRdLengthL(ptrRecord->DomainName());		
			iMsgBuf->SetDomainNameL(ptrRecord->DomainName());
			}
		    break;	
		case EDnsType_SRV: // SRV record
			{
			CRdTypeSrv* srvRecord = static_cast<CRdTypeSrv*>(aResourceRecord);
			iMsgBuf->SetSrvRdLengthL(srvRecord->Target());
			iMsgBuf->SetInt16(srvRecord->Priority());
			iMsgBuf->SetInt16( srvRecord->Weight());
			iMsgBuf->SetInt16( srvRecord->Port());
			iMsgBuf->SetDomainNameL(srvRecord->Target());
			}
		    break;
		case EDnsType_TXT: // TXT record
			{
			CRdTypeTxt* txtRecord = static_cast<CRdTypeTxt*>(aResourceRecord);
			const RArray<RBuf8>& txtList = txtRecord->Text();
			iMsgBuf->SetTxtRdLength(txtList);
			for(TInt j=0; j<txtList.Count(); j++)
				{
				RBuf8 txt;
				txt.CreateL(txtList[j]); 	
				iMsgBuf->SetText(txt);
				txt.Close();
				}
			}
			break;  	     
	    }	
	}


//Returns the size of the dns message 
TUint16 CDnsMessageComposerParser::GetLength(const CDnsMessage& aMessage)const
    {
    return (aMessage.Size());
    }

