// 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:
//
#include "ccontrolchannel.h"
#include <es_sock.h>
#include <e32const.h>
#include <upnpconstants.h>
CControlChannelHandler::CControlChannelHandler ( RSocket &aSocket, TBool aChannelType )
:CActive ( EPriorityNormal ), iSocket ( aSocket ), iChannelType ( aChannelType )
{
CActiveScheduler::Add(this);
}
TInt CControlChannelHandler::RunError ( TInt aError )
{
User::RequestComplete ( iChannelStatus, aError );
return KErrNone;
}
// -------------------------------------------------------------
CControlChannelReader* CControlChannelReader::NewL ( RSocket& aSocket, TBool aChannelType )
{
return new ( ELeave ) CControlChannelReader ( aSocket, aChannelType );
}
CControlChannelReader::CControlChannelReader ( RSocket& aSocket, TBool aChannelType )
: CControlChannelHandler ( aSocket, aChannelType )
{
}
CControlChannelReader::~CControlChannelReader ()
{
Cancel ();
}
void CControlChannelReader::Read ( TControlMessage& aMessage, TRequestStatus& aStatus )
{
if ( iChannelType == EFalse )
{
__ASSERT_DEBUG ( !aMessage.IsDirty (), User::Panic ( KUPnPControlChannel, KErrInUse ) );
}
iMessage = &aMessage;
iChannelStatus = &aStatus;
*iChannelStatus = KRequestPending;
iMessage->MessagePtr().SetLength ( 0 );
iSocket.RecvOneOrMore ( iMessage->MessagePtr(), 0, iStatus, iBytesReceived );
SetActive ();
}
void CControlChannelReader::RunL ()
{
User::LeaveIfError ( iStatus.Int() );
TPtrC8 buffer ( iMessage->MessagePtr() );
TInt lenToSkip = 0;
if ( iDataLength == 0 ) // ie; first message
{
// We should have more than 4 bytes ( ie; the identifier, which is of TUint,
// size. here, sizeof (TUint) )
__ASSERT_DEBUG ( buffer.Length () >= sizeof(TUint), User::Invariant() );
// Extract the first 4 bytes. we will have our identifier with it.
TLex8 parser ( buffer );
TUint id = 0;
User::LeaveIfError ( parser.Val ( id, EDecimal ) );
iMessage->SetId( id );
lenToSkip = sizeof (TUint);
// Check how we are going to end with the message. checking the TControlMessage::MaxLength
// or by explicitly specifying it.
// Get the Maxlength and set it to the iMessage
TInt maxLen = 0;
User::LeaveIfError ( iSocket.GetOpt ( KCHMaxRecvLength, KCHOptionLevel, maxLen ) );
iMessage->SetMaxLength ( maxLen );
}
TPtrC8 bufferToCopy ( buffer.Ptr() + lenToSkip, buffer.Length() - lenToSkip );
iMessage->MessagePtr().Copy ( bufferToCopy );
iDataLength += bufferToCopy.Length ();
TInt maxLen = iMessage->MaxLength ();
if ( maxLen == KErrUnknown )
{
// We have to issue a GetOpt to see whether we complete with the message.
TBool last = EFalse;
User::LeaveIfError ( iSocket.GetOpt ( KCHLastMessage, KCHOptionLevel, last ) );
if ( last )
iMessage->SetLast ();
}
else
{
if ( iDataLength == maxLen )
{
// We completed with the message. Set the message end flag as ETrue
// and reset the iRecvLength
iDataLength = 0;
iMessage->SetLast ();
}
}
if ( iMessage->IsLast () && iChannelType == EFalse )
{
// client usage is done with the current control message
// We have to set a dirty bit as well for not reusing this message again
iMessage->SetDirty ();
}
User::RequestComplete ( iChannelStatus, KErrNone );
}
void CControlChannelReader::DoCancel ()
{
iSocket.CancelRecv ();
User::RequestComplete ( iChannelStatus, KErrCancel );
}
// ---------------------------------------------------------------
CControlChannelWriter* CControlChannelWriter::NewL ( RSocket& aSocket, TBool aChannelType )
{
return new ( ELeave ) CControlChannelWriter ( aSocket, aChannelType );
}
CControlChannelWriter::CControlChannelWriter ( RSocket& aSocket, TBool aChannelType )
: CControlChannelHandler ( aSocket, aChannelType)
{
}
CControlChannelWriter::~CControlChannelWriter ()
{
Cancel ();
}
void CControlChannelWriter::Write ( TControlMessage& aMessage, TRequestStatus& aStatus )
{
if ( iChannelType )
{
__ASSERT_DEBUG ( !aMessage.IsDirty (), User::Panic ( KUPnPControlChannel, KErrInUse ) );
}
iMessage = &aMessage;
iChannelStatus = &aStatus;
*iChannelStatus = KRequestPending;
if ( iMessage->Id () == 0 )
{
// Message ID is not set. Typically when we do the first send after connect
// ie; Client side RControlChannel operation.
iMessage->SetId ( iSocket.SubSessionHandle () );
}
if ( iMessage->MaxLength() != KErrUnknown )
{
TCHMessageOption option ( iMessage->Id (), iMessage->MaxLength () );
TPckg < TCHMessageOption > optionBuf ( option );
// Set the total size if set in the TControlMessage
iSocket.SetOpt ( KCHMaxRecvLength, KCHOptionLevel, optionBuf );
}
if ( iMessage->IsLast () && iChannelType )
{
// server usage is done with the current control message
// We have to set a dirty bit as well for not reusing this message again
iMessage->SetDirty ();
}
iSocket.Send ( iMessage->MessageDes(), iMessage->Id(), iStatus );
SetActive ();
}
void CControlChannelWriter::RunL ()
{
User::LeaveIfError ( iStatus.Int() );
if ( iMessage->MaxLength() == KErrUnknown && iMessage->IsLast () )
{
TCHMessageOption option ( iMessage->Id (), iMessage->IsLast () );
TPckg < TCHMessageOption > optionBuf ( option );
// SetOpt to know whether we are sending the last message
User::LeaveIfError ( iSocket.SetOpt( KCHLastMessage, KCHOptionLevel, optionBuf ) );
}
User::RequestComplete ( iChannelStatus, KErrNone );
}
void CControlChannelWriter::DoCancel ()
{
iSocket.CancelSend ();
User::RequestComplete ( iChannelStatus, KErrCancel );
}
// ----------------------------------------------------------------
CControlChannel* CControlChannel::NewL ( RSocketServ& aSocketServ, RSubConnection& aSubConnection, TUint aAddrFamily, TBool aChannelType, TUint aProtocol, const TConnectionDetails& aConnInfo )
{
CControlChannel* self = new (ELeave) CControlChannel ( );
CleanupStack::PushL ( self );
self->ConstructL ( aSocketServ, aSubConnection, aAddrFamily, aChannelType, aProtocol, aConnInfo );
CleanupStack::Pop (); // self
return self;
}
CControlChannel::~CControlChannel ()
{
delete iReader;
delete iWriter;
iSocket.Close ();
}
void CControlChannel::Recv ( TControlMessage& aMessage, TRequestStatus& aStatus )
{
iReader->Read ( aMessage, aStatus );
}
void CControlChannel::Send ( TControlMessage& aMessage, TRequestStatus& aStatus )
{
iWriter->Write ( aMessage, aStatus );
}
CControlChannel::CControlChannel ( )
{
}
void CControlChannel::ConstructL ( RSocketServ& aSocketServ, RSubConnection& aSubConnection, TUint aAddrFamily, TBool aChannelType, TUint aProtocol, const TConnectionDetails& aConnInfo )
{
User::LeaveIfError ( iSocket.Open( aSocketServ, aAddrFamily, KSockStream, aProtocol, aSubConnection ) );
if ( aConnInfo.iAddr )
{
TRequestStatus connectStatus;
iSocket.Connect ( *(aConnInfo.iAddr), connectStatus );
User::WaitForRequest ( connectStatus ); // This is Ok to do... as in the esock server ( actually flow )will just store the
// connect address internally and not attempting to do an actual connect. As good as
// setopt but we need the socket to know that this is a connected socket.
User::LeaveIfError ( connectStatus.Int() );
}
if ( aConnInfo.iUri.Length() != 0 )
{
// Do a setopt to inform the esock about our URI
User::LeaveIfError ( iSocket.SetOpt ( KCHAbsoluteUri, KCHOptionLevel, aConnInfo.iUri ) );
}
iReader = CControlChannelReader::NewL ( iSocket, aChannelType );
iWriter = CControlChannelWriter::NewL ( iSocket, aChannelType );
}