diff -r 866b4af7ffbe -r 4dc88a4ac6f4 obex/obexprotocol/obex/src/obexserver.cpp --- a/obex/obexprotocol/obex/src/obexserver.cpp Tue Sep 28 20:14:08 2010 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1588 +0,0 @@ -// Copyright (c) 1997-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: -// - -/** - @file - @internalComponent -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "logger.h" -#include "obexsetpathdata.h" -#include "OBEXUTIL.H" -#include "authentication.h" -#include "obexnotifyhandlerserver.h" -#include "obexserverstatemachine.h" -#include "obexservernotifysyncwrapper.h" -#include "obexheaderutil.h" -#include "obexserverrequestpacketengine.h" -#include "obexpacketsignaller.h" - -#ifdef __FLOG_ACTIVE -_LIT8(KLogComponent, "OBEX"); -#endif - -/** -Constructor - set initial values -@internalTechnology -*/ -CObexServer::CObexServer() : CObex() - { - iCurrentOperation = EOpIdle; - iEnabled = EFalse; - - //the connectionID is fixed at the moment - ResetConnectionID(); - SetConnectionID(0xc30fa596); - - iTargetChecking = EIfPresent; - } - -void CObexServer::ConstructL(TObexTransportInfo& aObexTransportInfo) - { - CObex::ConstructL(aObexTransportInfo); - iNotifyHandler = new(ELeave)CObexNotifyHandlerServer(*this); - iTransportController->SetOwner(*iNotifyHandler); - - iHeader = CObexHeader::NewL(); - iStateMachine = CObexServerStateMachine::NewL(*this, *iTransportController); - iSyncWrapper = CObexServerNotifySyncWrapper::NewL(*this, *iStateMachine); - iPacketProcessSignaller = CObexPacketSignaller::NewL(); - } - -/** -Destructor. -*/ -CObexServer::~CObexServer() - { - FLOG(_L("CObexServer Destructor\r\n")); - Stop(); - - delete iPacketProcessSignaller; - delete iSyncWrapper; - delete iStateMachine; - delete iHeader; - delete iNotifyHandler; - delete iServerRequestPacketEngine; - } - -void CObexServer::ResetConnectionID() - { - iConnectionID = KConnIDInvalid; - iConnectionIdSet = EFalse; - } - -void CObexServer::SetConnectionID(TUint32 aConnectionID) - { - iConnectionID = aConnectionID; - iConnectionIdSet = ETrue; - } - -TUint32 CObexServer::ConnectionID() - { - return (iConnectionID); - } - -TInt CObexServer::PrepareFinalChallResponse(CObexPacket& aPacket, TConnectState& aNextState) - { - - FLOG(_L("CObexServer::PrepareFinalChallResponse\r\n")); - - aPacket.SetOpcode(ERespSuccess); - - TInt retValue = AddConnectionIDHeader(aPacket); - if (retValue == KErrNone) - { - FLOG(_L("PrepareFinalChallResponse ConnectionID header Added\r\n")); - if (iCallBack) - { - FLOG(_L("PrepareFinalChallResponse Requesting User Password\r\n")); - - //the actual asking of the password happens later in the method OnPacketReceive - //wait for the reply - aNextState = EWaitForUserInput; - retValue = KErrGeneral; //mustn't send yet wait for reply from user - } - else //else can't Auth challenge so drop link - { - FLOG(_L("PrepareFinalChallResponse Can't request User Password for Chall dropping link\r\n")); - - retValue = KErrIrObexConnectChallRejected; - aNextState = EConnTransport; - aPacket.SetOpcode(ERespNotImplemented); - } - } - else - { - aNextState = EDropLink; - } - return (retValue); - } - - -/** A call back from the the service with the password required for use with generating -the challenge response. - -@param aPassword Password - -@leave KErrNotReady if this function is not called from a MObexAuthChallengeHandler::GetUserPasswordL callback. - -@publishedAll -@released -*/ -EXPORT_C void CObexServer::UserPasswordL(const TDesC& aPassword) - { - LOG_LINE - LOG_FUNC - - //now have a password, get a nonce, and get it hashed then reply - if (GetConnectState() == EWaitForUserInput) - { - FLOG(_L("CObexServer::UserPasswordL\r\n")); - PrepareChallResponseL(aPassword); - FLOG(_L("UserPasswordL - PrepareChallResponse Success\r\n")); - - TObexInternalHeader hdr; - hdr.Set(TObexInternalHeader::EAuthResponse, (const_cast (iOutgoingChallResp.Ptr())), iOutgoingChallResp.Size()); - if(iTransportController->SendPacket().InsertData(hdr)) - { - FLOG(_L("UserPasswordL Inserting EAuthResponse Header\r\n")); - - SetConnectState(EConnObex); //all finished - iTransportController->SendPacket().SetFinal(); - iTransportController->Send(); - //inform the client that the connection was succesfull - iOwner->ObexConnectIndication(iRemoteInfo, TPtr8(NULL, 0)); - iStateMachine->ConnectionComplete(); - } - else - { - User::Leave(KErrGeneral); - } - } - else - { - User::Leave(KErrNotReady); - } - } - - -TInt CObexServer::AddConnectionIDHeader(CObexPacket& aPacket) - { - TInt retValue = KErrNone; - //if the Target header was used for the connection - //then reply with ConnectionID and Who headers - if(iTargetReceived) - { - //this solution can only handle one connection at a time therefore - //can safely use the same connection ID repeatedly - //when used the ConnectionID must be first - TObexInternalHeader hdr; - FLOG(_L("CObexServer::AddConnectionIDHeader Inserting EConnectionID Header\r\n")); - - hdr.Set(TObexInternalHeader::EConnectionID, iConnectionID); - - if(aPacket.InsertData(hdr)) - { - // Connection ID header inserted correctly - // Now set a WHO header. - // This logic is a bit backwards due to problems with the 'no target header checking' - // state. Instead of inserting our local Who header, we copy the Target header back. - // This works in the checking states because we drop any connection where the local - // Who is not identical to the Target header received. - // When not checking targets, this may mean that the client gets connected to a server - // which knows nothing about the service, yet thinks it is talking to a strict peer. - // However the server wouldn't understand what was going on anyway, so we're not really - // in a worse state than we would be if we did something more fancy. Ultimately the - // application must drop the connection---probably by deleting the Obex server or - // returning errors to all attempted operations. - - FLOG(_L("CObexServer::AddConnectionIDHeader Inserting EWho Header\r\n")); - - hdr.Set(TObexInternalHeader::EWho, (const_cast (iRemoteInfo.iTargetHeader.Ptr())), - iRemoteInfo.iTargetHeader.Size()); - if(!aPacket.InsertData(hdr)) - { - retValue = KErrGeneral; - } - } - else - { - retValue = KErrGeneral; - } - } - return (retValue); - } - -/** -Allocates and constructs a new OBEX server object. - -The received protocol information object, aObexProtocolInfoPtr, specifies the -transport protocol to use: -For the standard transports the following are used, TObexIrProtocolInfo for -IrDA, TObexBluetoothProtocolInfo for Bluetooth, TObexUsbProtocolInfo for USB. - -@param aObexProtocolInfoPtr Protocol information object describing the -transport to use -@return New OBEX server object - -@publishedAll -@released -*/ -EXPORT_C CObexServer* CObexServer::NewL(TObexProtocolInfo& aObexProtocolInfoPtr) - { - LOG_LINE - LOG_STATIC_FUNC_ENTRY - - TObexProtocolPolicy defaultProtocolPolicy; // no packet sizing policy specified, so use default - TObexTransportInfo* transportInfo = IrOBEXUtil::CreateTransportInfoL(aObexProtocolInfoPtr, defaultProtocolPolicy); - CleanupStack::PushL(transportInfo); - CObexServer* server = CObexServer::NewL(*transportInfo); - CleanupStack::PopAndDestroy(transportInfo); - return server; - } - -/** -Allocates and constructs a new OBEX server object with packet sizing -information. - -The received protocol information object, aObexProtocolInfoPtr, specifies the -transport protocol to use: -For the standard transports the following are used, TObexIrProtocolInfo for -IrDA, TObexBluetoothProtocolInfo for Bluetooth, TObexUsbProtocolInfo for USB. - -The aObexProtocolPolicy parameter specifies the packet sizing policy for this -OBEX object. - -@param aObexProtocolInfoPtr Protocol information object describing the -transport to use -@param aObexProtocolPolicy Protocol policy object specifying the packet sizes -to use -@return New OBEX server object - -@publishedAll -@released -*/ -EXPORT_C CObexServer* CObexServer::NewL(TObexProtocolInfo& aObexProtocolInfoPtr, - TObexProtocolPolicy& aObexProtocolPolicy) - { - LOG_LINE - LOG_STATIC_FUNC_ENTRY - - TObexTransportInfo* transportInfo = IrOBEXUtil::CreateTransportInfoL(aObexProtocolInfoPtr, aObexProtocolPolicy); - CleanupStack::PushL(transportInfo); - CObexServer* server = CObexServer::NewL(*transportInfo); - CleanupStack::PopAndDestroy(transportInfo); - return server; - } - -/** -Allocates and constructs a new OBEX server object with packet sizing -information. - -The received transport information object, aObexTransportInfo, specifies the -transport protocol and packet sizes to use: -For the standard transports the following are used, TObexIrProtocolInfo for -IrDA, TObexBluetoothProtocolInfo for Bluetooth, TObexUsbProtocolInfo for USB. - -@param aObexTransportInfo Transport information object describing the -transport and packet sizes to use -@return New OBEX server object - -@capability WriteDeviceData If the TObexIrV3TransportInfo is passed as the argument - and the associated name is valid. - -@publishedAll -@released -*/ -EXPORT_C CObexServer* CObexServer::NewL(TObexTransportInfo& aObexTransportInfo) - { - LOG_LINE - LOG_STATIC_FUNC_ENTRY - - CObexServer* self = new(ELeave) CObexServer(); - CleanupStack::PushL(self); - self->ConstructL(aObexTransportInfo); - CleanupStack::Pop(self); - return(self); - } - -/** Starts the server, specifying a synchronous notification interface. - -If the server is already started, no state changes occur (i.e. any connections/operations -in progress are not interrupted), but the notifications will be sent to aOwner. -This allows "child" servers to take over ownership of existing connections. - -Details of this function behaviour depend on the transport specified when -constructed: in general a listener socket is created, its port number registered -as appropriate, and an accept queued. - -@param aOwner Server notification interface -@return KErrArgument if parameter is NULL, KErrAlreadyExists if server has already -been started (but notification object will still be updated), otherwise a system wide -error code -@panic OBEX EChangeInterfaceDuringWait when attempting to change the interface at an inappropriate time. - -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::Start(MObexServerNotify* aOwner) - { - LOG_LINE - LOG_FUNC - - if(aOwner == NULL) - { - return(KErrArgument); - } - - // Pass this synchronous interface to the synchronous wrapper - // and pass the synchronous wrapper on to the asynchronous Start() - iSyncWrapper->SetNotifier(aOwner); - return Start(iSyncWrapper); - } - - -/** Starts the server, specifying an asynchronous notification interface. - -If the server is already started, no state changes occur (i.e. any connections/operations -in progress are not interrupted), but the notifications will be sent to aOwner. -This allows "child" servers to take over ownership of existing connections. - -Details of this function behaviour depend on the transport specified when -constructed: in general a listener socket is created, its port number registered -as appropriate, and an accept queued. - -@param aOwner Server notification interface -@return KErrArgument if parameter is NULL, KErrAlreadyExists if server has already -been started (but notification object will still be updated), otherwise a system wide -error code -@panic OBEX EChangeInterfaceDuringWait when attempting to change the interface at an inappropriate time. - -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::Start(MObexServerNotifyAsync* aOwner) - { - if(aOwner == NULL) - { - return(KErrArgument); - } - - iOwner = aOwner; - - iStateMachine->Start(*iOwner); - if(iEnabled) - { - return(KErrAlreadyExists); - } - iEnabled = ETrue; - return(AcceptConnection()); - } - - -/** Disconnects any transfer in progress and disables further connections. - -@publishedAll -@released -*/ -EXPORT_C void CObexServer::Stop() - {// Cancel and Disable accepts, and bring and transport down. - LOG_LINE - LOG_FUNC - - if(!iEnabled) - { - return; - } - iEnabled = EFalse; - ControlledTransportDown(); - - // just check that iTransportController is still valid here (that is what we - // aspect to be) - __ASSERT_DEBUG(iTransportController, IrOBEXUtil::Fault(ETransportControllerNotCreated)); - - iTransportController->CancelAccept(); - iStateMachine->Stop(); - iOwner = NULL; - iSyncWrapper->SetNotifier(NULL); - } - -TInt CObexServer::AcceptConnection() - { - if(iEnabled && iOwner) - { - iCurrentOperation = EOpIdle; - TRAPD(err, iTransportController->AcceptConnectionL()); - if(err != KErrNone) - { - iEnabled = EFalse; - } - return(err); - } - else - { - return(KErrNone); - } - } - - - -/** Sets a password required to access the server. - -When a password is set, a client must specify it to access the server. - -@param aPassword Password - -@publishedAll -@released -*/ -EXPORT_C void CObexServer::SetChallengeL(const TDesC& aPassword) - { - LOG_LINE - LOG_FUNC - - delete iChallPassword; - iChallPassword = NULL; - iChallPassword = HBufC8::NewL(aPassword.Length()); - TPtr8 ptr = iChallPassword->Des(); - CnvUtfConverter::ConvertFromUnicodeToUtf8(ptr, aPassword); - iChallenge = ETrue; - } - -/** Resets the password. - -After this call, a client does not need to provide a password to access the -server. - -@publishedAll -@released -*/ -EXPORT_C void CObexServer::ResetChallenge() - { - LOG_LINE - LOG_FUNC - - delete iChallPassword; - iChallPassword = NULL; - iChallenge = EFalse; - } - -/** -Specifies target header checking behaviour. - -Supports three behaviours---never check, always check, and check only if a target -header has been sent. The default behaviour is to only check when a target header -has been sent. - -No checking allows a form of multiplexing to be used, where one server object may -respond to multiple target headers. The behaviour desired by the client can be -determined by examining the target header specified in the Connect. - -@param aChecking The desired level of target header checking. -@publishedAll -@released -*/ -EXPORT_C void CObexServer::SetTargetChecking(TTargetChecking aChecking) - { - LOG_LINE - LOG_FUNC - - iTargetChecking = aChecking; - } - - -/** -Prepare next packet for the connection attempt -ConnectionID and Who headers are Mandatory if the Target header was used in the connection from -@param aPacket Packet to fill -@internalComponent -*/ -TInt CObexServer::PrepareConnectPacket(CObexPacket& aPacket) - { - FLOG(_L("CObexServer::PrepareConnectPacket\r\n")); - TInt retValue = KErrNone; - TConnectState nextState = GetConnectState(); - - if(!iTransportController->InsertLocalConnectInfo(aPacket, iLocalInfo.iVersion, iLocalInfo.iFlags)) - { - FLOG(_L("PrepareConnectPacket Local data insertion FAILED\r\n")); - return(KErrGeneral); - } - FLOG(_L("PrepareConnectPacket Local data inserted\r\n")); - - if(GetConnectState() == ESimpleConnRequest) //no Auth requested by the Client - { - FLOG(_L("PrepareConnectPacket GetConnectState() == ESimpleConnRequest\r\n")); - //if the Server must challenge - if(iChallenge) - { - FLOG(_L("PrepareConnectPacket Challenge Required\r\n")); - - aPacket.SetOpcode(ERespUnauthorized); - retValue = GenerateChallenge(aPacket); - if ( retValue == KErrNone ) - { - FLOG(_L("PrepareConnectPacket Challenge generated\r\n")); - nextState = ESimpleConnChallIssued; - } - else - { - FLOG(_L("PrepareConnectPacket Challenge generation FAILED\r\n")); - nextState = EConnTransport; - aPacket.SetOpcode(ERespInternalError); - } - } - else //don't require Authentication - { - FLOG(_L("PrepareConnectPacket No Challenge Required\r\n")); - - aPacket.SetOpcode(ERespSuccess); - //if the Target header was used for the connection - //if so then reply with ConnectionID and Who headers - if ((retValue = AddConnectionIDHeader(aPacket)) == KErrNone) - { - FLOG(_L("PrepareConnectPacket ConnectionID Inserted\r\n")); - nextState = EConnObex; - } - else - { - nextState = EDropLink; - FLOG(_L("PrepareConnectPacket ConnectionID Insertion FAILED\r\n")); - } - } - } //end if(GetConnectState() == ESimpleConnRequest) - else if (GetConnectState() == EChallConnRequested) - { - FLOG(_L("PrepareConnectPacket GetConnectState() == EChallConnRequested\r\n")); - - //if the Server must challenge - if(iChallenge) - { - FLOG(_L("PrepareConnectPacket Challenge required\r\n")); - aPacket.SetOpcode(ERespUnauthorized); - retValue = GenerateChallenge(aPacket); - if ( retValue == KErrNone ) - { - FLOG(_L("PrepareConnectPacket Challenge Generated\r\n")); - nextState = EChallConnChallIssued; //chall answered with another chall - } - else - { - FLOG(_L("PrepareConnectPacket Challenge Generation FAILED\r\n")); - nextState = EConnTransport; - aPacket.SetOpcode(ERespInternalError); - } - } - else //don't require Authentication - { //the response would already have been verified in ParseConnectPacket() - //get password from user, prepare a response to the challenge - FLOG(_L("PrepareConnectPacket Challenge Not Required\r\n")); - retValue = PrepareFinalChallResponse(aPacket, nextState); - } - } //end else if GetConnectState() == EChallConnRequested - else if (GetConnectState() == EFinalChallRxed) - { // - retValue = PrepareFinalChallResponse(aPacket, nextState); - } - else if ( GetConnectState() == EFinalResponseReceived ) - { //the response had to be OK otherwise would never have gotten this far - aPacket.SetOpcode(ERespSuccess); - //if the Target header was used for the connection - //if so then reply with ConnectionID and Who headers - if ((retValue = AddConnectionIDHeader(aPacket)) == KErrNone) - { - nextState = EConnObex; - FLOG(_L("PrepareConnectPacket ConnectionID header Added\r\n")); - } - else - { - nextState = EDropLink; - FLOG(_L("PrepareConnectPacket ConnectionID header Addition FAILED\r\n")); - } - } - else //it's all gone wrong - { - FLOG(_L("PrepareConnectPacket complete failure, bad state\r\n")); - - //break connection, inform user - nextState = EConnTransport; - aPacket.SetOpcode(ERespInternalError); - retValue = KErrGeneral; - } - //if the Server is now connected inform the client - if ( nextState == EConnObex) - iOwner->ObexConnectIndication(iRemoteInfo, TPtr8(NULL, 0)); - - SetConnectState(nextState); - return(retValue); - } -/** -Prepare next packet for an invalid connection attempt (i.e. the ParseConnectPacket failed). -A fail response (to a connect request) includes the version, flags, and packet size information. - -@param aPacket Packet to fill -@internalComponent -*/ -TInt CObexServer::PrepareErroredConnectPacket(CObexPacket& aPacket) - { - FLOG(_L("CObexServer::PrepareErroredConnectPacket\r\n")); - - if ( !iTransportController->InsertLocalConnectInfo(aPacket, iLocalInfo.iVersion, iLocalInfo.iFlags)) - { - FLOG(_L("PrepareConnectPacket Local data insertion FAILED\r\n")); - return(KErrGeneral); - } - return KErrNone; - } - -void CObexServer::SignalReadActivity() - { - iPacketProcessSignaller->Signal(EObexReadActivityDetected); - } - -void CObexServer::CheckTarget(TConnectState& aNextState, TInt& aRetVal) - { - FLOG(_L("Local Who:\r\n")); - LOGHEXDESC(iLocalInfo.iWho); - FLOG(_L("Target:\r\n")); - LOGHEXDESC(iRemoteInfo.iTargetHeader); - - // Workaround for bug with PocketPC 2002---if target header is sixteen bytes of zeros, connect anyway. - _LIT8(KZeroTarget, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); - - // Allow connection iff: - // PocketPC attempting to connect to inbox (Sixteen bytes of zeros), when no LocalWho - // or Target header matches LocalWho (includes Inbox connections) - - if (!( // Negate as block below discards connection - ((iLocalInfo.iWho == KNullDesC8) && (iRemoteInfo.iTargetHeader == KZeroTarget)) - || ( iLocalInfo.iWho == iRemoteInfo.iTargetHeader) - )) - { - FLOG(_L("ParseConnectPacket ETarget header doesn't match local iWho, dropping link\r\n")); - aNextState = EConnTransport; - aRetVal = ERespNotFound; - } - } - - -//if the Target header is sent then it must match the local iWho field -TInt CObexServer::ParseConnectPacket(CObexPacket& aPacket) - { - TConnectState nextState = GetConnectState(); //must change otherwise it's all wrong - TBool challReceivedOK = EFalse; //authentication challenge received from client - //after server has issued its own authentication challenge. - TBool respReceivedOK = EFalse; //authentication received from client - //after server has issued its own authentication challenge. - FLOG(_L("CObexServer::ParseConnectPacket\r\n")); - - if(!iTransportController->ExtractRemoteConnectInfo(aPacket, iRemoteInfo.iVersion, iRemoteInfo.iFlags)) - { - FLOG(_L("ParseConnectPacket remote connect info extraction FAILED\r\n")); - return KErrGeneral; - } - FLOG(_L("ParseConnectPacket remote connect info extracted\r\n")); - - TObexInternalHeader hdr; - iTargetReceived = EFalse; //if target received then must reply with ConnectionID - - //if the present state is EConnTransport then no headers are actually - //required, a simple connect is sufficient - if(GetConnectState() == EConnTransport) - { - nextState = ESimpleConnRequest; - } - - TInt retVal = KErrNone; - TBool authAttempted = EFalse; - - while (aPacket.ExtractData(hdr) && (nextState != EDropLink) && (nextState != EConnTransport)) - { - switch(hdr.HI()) - { - case TObexInternalHeader::ETarget: - { - FLOG(_L("ParseConnectPacket extracting ETarget header\r\n")); - iTargetReceived = ETrue; - //copy the target header into iRemoteInfo for the user - iRemoteInfo.iTargetHeader.Copy(hdr.HVByteSeq(), hdr.HVSize() > iRemoteInfo.iTargetHeader.MaxSize() ? iRemoteInfo.iTargetHeader.MaxSize() : hdr.HVSize()); - - if (iTargetChecking == EIfPresent) - { - FLOG(_L("EIfPresent target header checking...")); - CheckTarget(nextState, retVal); - } - } - break; - case TObexInternalHeader::EAuthChallenge: - { - FLOG(_L("ParseConnectPacket EAuthChallenge Header received processing\r\n")); - authAttempted = ETrue; - TRAPD(err, ProcessChallengeL(hdr)); - if (!err) - { - FLOG(_L("ParseConnectPacket Processing Chall SUCCESS\r\n")); - if (GetConnectState() == EConnTransport) - { - nextState = EChallConnRequested; - } - else if ((GetConnectState() == ESimpleConnChallIssued) || (GetConnectState() == EChallConnChallIssued)) - { - challReceivedOK = ETrue; //the response must be verified first - nextState = EFinalChallRxed; - } - else - { - nextState = EConnTransport; - retVal = ERespInternalError; - } - } - else - { - FLOG(_L("ParseConnectPacket Processing Chall FAILED\r\n")); - - nextState = EConnTransport; - retVal = ERespInternalError; - } - } - break; - case TObexInternalHeader::EAuthResponse: - { - if (iChallenge) - //else there is no challenge password to check against! - { - FLOG(_L("ParseConnectPacket EAuthResponse Header received processing\r\n")); - authAttempted = ETrue; - TRAPD(err, ProcessChallResponseL(hdr)); - if (err == KErrNone) - { - FLOG(_L("ParseConnectPacket Processing Chall Response SUCCESS\r\n")); - if (GetConnectState() == ESimpleConnChallIssued) - { - respReceivedOK =ETrue; - if (challReceivedOK) //was a new challenge issued by the Client? - { - nextState = EFinalChallRxed; //must respond to chall - } - else - { - nextState = EFinalResponseReceived; //everything is OK send Success - } - } - else if (GetConnectState() == EChallConnChallIssued) - { - respReceivedOK =ETrue; - if (challReceivedOK) //was a new challenge issued by the Client? - { - nextState = EFinalChallRxed; //must respond to chall - } - else - { - //If we do not later in the packet see a challenge, - //(in which case this 'nextState' value will be overwritten), - //the client will have come back WITHOUT re-issuing - //either his original challenge or a new one. - //Treat as if client had never issued a challenge. - //This sequence has been observed in FOMA phones. - nextState = EFinalResponseReceived; - } - } - else - { - nextState = EConnTransport; - retVal = ERespInternalError; - } - } - else if (err == KErrAccessDenied) - { - nextState = EConnTransport; - retVal = ERespUnauthorized; - FLOG(_L("ParseConnectPacket Processing Chall Response FAILED with Access Denied\r\n")); - } - else - { - nextState = EConnTransport; - retVal = ERespInternalError; - FLOG(_L("ParseConnectPacket Processing Chall Response FAILED\r\n")); - } - } - else - { - // if no challenge was issued, then receiving a challenge response means the peer is badly - // behaved. For this case we simply ignore the header, anything else would be too drastic. - FLOG(_L("ParseConnectPacket Chall Response received when no Chall issued\r\n")); - } - } - break; - default: - break; - } - } - - if (((GetConnectState() == ESimpleConnChallIssued) || (GetConnectState() == EChallConnChallIssued)) && !respReceivedOK) - // Client's connect packet should have contained an authentication response. - // Treat as if we had rejected an authentication response. - { - nextState = EConnTransport; - retVal = ERespUnauthorized; - } - - if (iTargetChecking == EAlways) - { - FLOG(_L("EAlways target header checking...")); - CheckTarget(nextState, retVal); - } - - if (!authAttempted && (GetConnectState() == ESimpleConnChallIssued)) - nextState = ESimpleConnRequest; - - SetConnectState(nextState); - - return retVal; - } - - -/** -Check, if required, the object connection ID. - -@return ETrue if it was not necessary to receive the ConnectionID or - if it was necessary and was correctly received. Otherwise - EFalse. -@internalComponent -*/ -TBool CObexServer::CheckObjectForConnectionId(CObexBaseObject& aObject) - { - TBool retValue = ETrue; - - if( iTargetReceived ) - { - retValue = EFalse; - if (aObject.iValidHeaders & KObexHdrConnectionID ) - { - TUint32 connID = aObject.ConnectionID(); - if (iConnectionIdSet && (iConnectionID == connID)) - { - retValue = ETrue; - } - } - } - return (retValue); - } - -/** -Check, if required, that the packet connection ID matches that of the Server's current connection -@return ETrue if the connection ID matches or if Target was not used in the original connection -@return EFalse if Target was not used in the original connection and the connection ID was not found -@internalComponent -*/ -TBool CObexServer::CheckPacketForConnectionId(CObexPacket& aPacket) - { - // Connection ID check is compulsory if Target was used in the original connection - if (!iTargetReceived) - { - return ETrue; - } - - // Search for ConnectionID - // ConnectionID should be the first header, but we check all of them just in case - TObexInternalHeader header; - while (aPacket.ExtractData(header)) - { - if (header.HI() == TObexInternalHeader::EConnectionID) - { - TUint32 newConnectionID = (header.HVByteSeq()[0] << 24) + (header.HVByteSeq()[1] << 16) - + (header.HVByteSeq()[2] << 8) + (header.HVByteSeq()[3]); - - if (ConnectionID() == newConnectionID) - { - return ETrue; - } - } - } - - // Target was used in original connection and could not find Connection ID - return EFalse; - } - -void CObexServer::OnPacketReceive(CObexPacket& aPacket) - { - FLOG(_L("CObexServer::OnPacketReceive\r\n")); - MObexServerRequestPacketNotify* packetNotify = NULL; - if (iServerRequestPacketEngine) - { - packetNotify = iServerRequestPacketEngine->RequestPacketNotify(); - } - // If a packet notify has been registered then we should tell it - // about each request packet we receive. - if (packetNotify) - { - TObexResponse response; - TBool normalOperation = ETrue; - - // Rebuild full OBEX opcode to pass to observer - TObexOpcode opcode = aPacket.Opcode() | (aPacket.IsFinal() ? KObexPacketFinalBit : 0); - - switch (aPacket.Opcode()) - { - case CObex::EOpConnect: - { - TObexConnectInfo connectInfo; - if (!iStateMachine->Transport().ExtractRemoteConnectInfo(aPacket, connectInfo.iVersion, connectInfo.iFlags)) - { - // If the packet cannot be parsed correctly at this stage it is very malformed - // and so we should abort processing it for the server app, and defer handling - // the error to the state machine. - break; - } - - TObexInternalHeader header; - // Call ExtractData() until it returns 0 bytes read - then we know the extract - // point will have been reset so the CObexPacket can be parsed again in the - // future. For this reason do not attempt to optimise this loop. - while (aPacket.ExtractData(header)) - { - switch (header.HI()) - { - case TObexInternalHeader::ETarget: - // Only take the first Target header found - if (connectInfo.iTargetHeader.Length() == 0) - { - TInt size = header.HVSize() > connectInfo.iTargetHeader.MaxSize() ? connectInfo.iTargetHeader.MaxSize() : header.HVSize(); - connectInfo.iTargetHeader.Copy(header.HVByteSeq(), size); - } - break; - case TObexInternalHeader::EWho: - // Return the first Who header in packet - if (connectInfo.iWho.Length() == 0) - { - TInt size = header.HVSize() > connectInfo.iWho.MaxSize() ? connectInfo.iWho.MaxSize() : header.HVSize(); - connectInfo.iWho.Copy(header.HVByteSeq(), size); - } - break; - default: - break; - } - } - - normalOperation = packetNotify->RequestPacket(opcode, connectInfo, response); - } - break; - case CObex::EOpSetPath: - { - TObexSetPathData data; - - if (!aPacket.ExtractData(data)) - { - // If the packet cannot be parsed correctly at this stage it is very malformed - // and so we should abort processing it for the server app, and defer handling - // the error to the state machine. - break; - } - CObex::TSetPathInfo info(data); - - TObexInternalHeader header; - // Call ExtractData() until it returns 0 bytes read - then we know the extract - // point will have been reset so the CObexPacket can be parsed again in the - // future. For this reason do not attempt to optimise this loop. - while(aPacket.ExtractData(header)) - { - // Take the first name header we find. - if(!info.iNamePresent && header.HI() == TObexInternalHeader::EName && header.GetHVText(info.iName) == KErrNone) - { - info.iNamePresent = ETrue; - } - } - - normalOperation = packetNotify->RequestPacket(opcode, info, response); - } - break; - case CObex::EOpDisconnect: - case CObex::EOpAbortNoFBit: - case CObex::EOpPut: - case CObex::EOpGet: - default: - normalOperation = packetNotify->RequestPacket(opcode, response); - break; - } - - if (!normalOperation) // Abandon processing of request - { - CheckServerAppResponseCode(aPacket.Opcode(), response); // a success response code => panic - iStateMachine->OverrideRequestHandling(response); - return; - } - } - - // Normal processing - iStateMachine->OnPacketReceive(aPacket); - } - -/** -This function is to ensure that a response a server application provides the Obex Server -to respond to the Obex Client with when it has overriden the default handling of a request -packet does not represent a success. - -The rationale for this is to attempt to keep the Obex peers synchronised. As the packet has -been dropped, the client should not be lead to believe it has been received successfully. - -Therefore, this function asserts that the application does not send a success response for -the request packet received. -*/ -void CObexServer::CheckServerAppResponseCode(TObexOpcode aOpcode, TObexResponse aResponse) - { - TBool valid = ETrue; - switch (aOpcode) - { - case CObex::EOpConnect: - if (aResponse == ERespSuccess) - { - valid = EFalse; - } - break; - case CObex::EOpPut: - case CObex::EOpGet: - if (aResponse == ERespSuccess || aResponse == ERespContinue) - { - valid = EFalse; - } - break; - case CObex::EOpSetPath: - if (aResponse == ERespSuccess) - { - valid = EFalse; - } - break; - case CObex::EOpDisconnect: - case CObex::EOpAbortNoFBit: - // We allow any response to a abort/disconnect request, - // as only success codes are allowed. - default: - break; - } - __ASSERT_ALWAYS(valid, IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp)); - } - -void CObexServer::OnError(TInt aError) - { - FTRACE(FPrint(_L("OnError aError: %d iCurrentOperation: %d, iConnectState: %d"), aError, iCurrentOperation, iConnectState)); - - if (aError == KErrDisconnected) - { - // Note: It is not clear that iCurrentOperation is ever equal - // to EOpDisconnect but the check has been retained just in case - if ((iCurrentOperation != EOpDisconnect) && (iConnectState > EConnTransport)) - { - //extended error for IrObex,("peer device aborted data transmission/obex sending") - iOwner->ErrorIndication(KErrIrObexServerPutPeerAborted); - } - } - else - { - iOwner->ErrorIndication(aError); - } - // The state machine needs to know about the error regardless of whether ErrorIndication() is called - iStateMachine->Error(); - } - -void CObexServer::OnTransportUp() - { - iTargetReceived = EFalse; - - // For servers on the device using USB, there is a possibility that - // this function can be called, even though the server is stopped, - // as OBEX does not control the transport, the USB host does. - // Hence the need to check if there is an active iOwner. - if (iOwner) - { - iOwner->TransportUpIndication(); - } - iStateMachine->TransportUp(); // state machine needs to know about the event regardless of Server state - } - -/** -Tell the MObexServerNotifyAsync observer the transport is down and listen -for another connection. -*/ -void CObexServer::OnTransportDown() - {// Cancel Anything waiting. Restart the accepter - - // For servers on the device using USB, there is a possibility that - // this function can be called, even though the server is stopped, - // as OBEX does not control the transport, the USB host does - // Hence the need to check if there is an active iOwner. - if (iOwner) - { - iOwner->TransportDownIndication(); - } - iStateMachine->TransportDown(); // state machine needs to know about the event regardless of Server state - TInt err = AcceptConnection(); - if(err != KErrNone) - Error(err); - } - -/** Signals an event has ocurred. - -@released -@internalComponent -@param aEvent The event that has occurred. (TObexPacketProcessEvent) -*/ -void CObexServer::SignalPacketProcessEvent(TInt aEvent) - { - LOG_FUNC - - // This is how we signal the completed send of an ACK to a disconnect - // command. Tell the state machine so it can finish the disconnection - // sequence. - if(aEvent & EObexWriteCompletedFinal) - { - iStateMachine->WriteComplete(); - } - - // Server will have definitely finished with the read packet so queue the next read - if(aEvent & EObexWriteCompleted) - { - iTransportController->Receive(); - } - - if(aEvent & EObexReadActivityDetected) - { - iStateMachine->ReadActivityDetected(); - } - } - -// CObexServer -/** Tests if the server is started, and is available to accept connections. - -@return ETrue if the server is started, EFalse otherwise - -@publishedAll -@released -*/ -EXPORT_C TBool CObexServer::IsStarted() - { - LOG_LINE - LOG_FUNC - - return iEnabled; - } - -/** -Returns the operation currently being performed by the remote client, or -EOpIdle if between operations. Note that there is no implication of whether -the server is currently connected; EOpIdle will be returned regardless of -connection state, if no operation is currently being performed. Use -CObex::IsConnected () to find connection staus. - -@return Operation currently being performed by the remote client - -@publishedAll -@released -*/ -EXPORT_C CObex::TOperation CObexServer::CurrentOperation() const - { - LOG_LINE - LOG_FUNC - - return iCurrentOperation; - } - -/** -Setter function to allow other classes in the DLL to set the Server's current operation. -Used by the Server state machine. -@see CObexServerStateMachine -@param aOperation The operation currently being performed by the remote client -@internalComponent -*/ -void CObexServer::SetCurrentOperation(const CObex::TOperation aOperation) - { - iCurrentOperation = aOperation; - } - -/** -Specify the set of headers to return to remote Obex client in final -Put response packet. The total length of the headers when encoded -should not exceed the maximum Obex packet payload size. - -This function may be called at any point during a Put operation. -Repeated calls to this replace rather than add to the header set -for inclusion in the final Put response packet. - -It may be called with a NULL pointer, which means that no headers -will be sent with the Put Final Response. - -Even if this function returns with an error (even KErrNotReady) a -best-effort attempt will be made to send as many headers as will fit -in the final Put response packet. - -@param aHeaderSet A set of headers to be encoded in the final Put -response packet. Ownership of the header set always passes to -CObexServer. - -@return KErrNone if the operation completes successfully. - KErrNotReady if the current operation is not a Put. - KErrArgument if the length of the headers when encoded - exceeds the maximum Obex packet payload size. - -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::SetPutFinalResponseHeaders(CObexHeaderSet* aHeaderSet) - { - LOG_LINE - LOG_FUNC - - // First, let the state machine take ownership of the headerset - iStateMachine->SetPutFinalResponseHeaderSet(aHeaderSet); - - TInt err=KErrNone; - - // Easy check first - are we currently engaged in a Put? - if(iCurrentOperation != EOpPut) - { - err=KErrNotReady; - } - - if(!err && aHeaderSet) - { - // Next, the not so easy check. Will all the headers, when encoded, - // fit inside a send packet? - // First, how much space do we have to play with? - TInt available = iTransportController->SendPacket().DataLimit() - KObexPacketHeaderSize; - - // Next, what is the combined encoded size of all the headers? - TInt required = 0; - aHeaderSet->First(); - while(aHeaderSet->This(iHeader) == KErrNone) - { - required+=iHeader->EncodedSize(); - (void)aHeaderSet->Next(); - } - - if(required>available) - { - err=KErrArgument; - } - } - - return err; - } - - -/** -Complete an asynchronous callback, supplying a CObexBaseObject derived object. -Passing in NULL results in an Obex level error being sent to the client -- the -semantics are that either a PUT request has been rejected or a GET request has -not found a suitable object to return. - -@panic Obex ENoNotificationToComplete Raised if the server does not have a request -outstanding. -@param aObject The object passed back from application -@return result of state changes -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::RequestIndicationCallback(CObexBaseObject* aObject) - { - LOG_LINE - LOG_FUNC - return iStateMachine->RequestNotificationCompleted(aObject); - } - -/** -Complete an asynchronous callback, supplying a obex response code. Applications -should use this function when rejecting Get/Put RequestIndication in order to -specify the response code. - -@panic Obex ENoNotificationToComplete Raised if the server does not have a request -outstanding. -@panic Obex EInvalidResponseCodeFromServerApp raised if TObexResponse aResponseCode is outside range -[1,255] or it is one of the successful response (e.g. ERespSuccess, ERespContinue) -@param TObexResponse aResponseCode Application's response to the indication as an Obex response Code. The Final bit is ignored. -@return result of state changes -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::RequestIndicationCallbackWithError(TObexResponse aResponseCode) - { - LOG_LINE - LOG_FUNC - return iStateMachine->RequestNotificationCompleted(aResponseCode); - } - -/** -Complete an asynchronous callback, supplying a obex response code. Applications -should use this function when rejecting Get/Put RequestIndication in order to -specify the error code. - -@panic Obex ENoNotificationToComplete Raised if the server does not have a request -outstanding. -@panic Obex EInvalidResponseCodeFromServerApp raised if TObexResponse aResponseCode non-negtive. Note: KErrNone is -not acceptable because this function is only used when there is an error. -@param aErrorCode Application's response to the indication as an Obex response Code. -@return result of state changes -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::RequestIndicationCallbackWithError(TInt aErrorCode) - { - LOG_LINE - LOG_FUNC - __ASSERT_ALWAYS(aErrorCode <= 0, IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp)); - return iStateMachine->RequestNotificationCompleted(IrOBEXUtil::ObexResponse(aErrorCode, ERespSuccess)); - } - - -/** -Complete an asynchronous callback, supplying a obex response code. This function is -used for asychronously handling PutComplete, GetComplete and SetPath Indication. - -@panic Obex ENoNotificationToComplete Raised if the server does not have a request -outstanding. -@panic Obex EInvalidResponseCodeFromServerApp raised if TObexResponse aResponseCode is outside range -[1,255] or it is ERespContinue (which would confuse the client) -@param TObexResponse aResponseCode Application's response to the indication as an Obex response Code. The Final bit is ignored. -@return result of state changes -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::RequestCompleteIndicationCallback(TObexResponse aResponseCode) - { - LOG_LINE - LOG_FUNC - return iStateMachine->RequestCompleteNotificationCompleted(aResponseCode); - } - -/** -Complete an asynchronous callback, supplying a obex response code. This function is -used for asychronously handling PutComplete, GetComplete and SetPath Indication. - -@panic Obex ENoNotificationToComplete Raised if the server does not have a request -outstanding. -@panic Obex EInvalidResponseCodeFromServerApp raised if aErrorCode is positive, i.e. -invalid Symbian error code -@param TObexResponse aResponseCode Application's response to the indication as a Symbian error code -@return result of state changes -@publishedAll -@released -*/ -EXPORT_C TInt CObexServer::RequestCompleteIndicationCallback(TInt aErrorCode) - { - LOG_LINE - LOG_FUNC - __ASSERT_ALWAYS(aErrorCode <= 0, IrOBEXUtil::Panic(EInvalidResponseCodeFromServerApp)); - return iStateMachine->RequestCompleteNotificationCompleted(IrOBEXUtil::ObexResponse(aErrorCode, ERespSuccess)); - } - -/** -Provides the pre-parsed contents of the most recently received request packet. - -@param aHeaderSet A reference to a pointer that will be modified to NULL if no headers -are contained in the request packet, or to point to a new headerset containing -representations of the headers within the packet. Ownership of the headerset -(when aHeaderSet is not NULL) is passed to the caller. -@return KErrNone if successful, otherwise a system wide error code. - -@publishedPartner -@released -*/ -EXPORT_C TInt CObexServer::PacketHeaders(CObexHeaderSet*& aHeaderSet) - { - FLOG(_L8("CObexServer::PacketHeaders")); - - return DoPacketHeaders(aHeaderSet, NULL); - } - - -/** -Provides the selectively pre-parsed contents of the most recently received request packet. - -@param aHeaderSet A reference to a pointer that will be modified to NULL if no interesting -header are contained in the request packet, or to point to a new headerset containing -representations of the headers within the packet that are of interest. Ownership of the -headerset (when aHeaderSet is not NULL) is passed to the caller. -@param aHeaderCheck A reference to an MObexHeaderCheck derived class that encapsulates -whether or not a particular header or headers should be included in the returned header -set (i.e. whether the headers are "interesting"). - -@return KErrNone if successful, otherwise a system wide error code. - -@publishedPartner -@released -*/ -EXPORT_C TInt CObexServer::PacketHeaders(CObexHeaderSet*& aHeaderSet, MObexHeaderCheck& aHeaderCheck) - { - FLOG(_L8("CObexServer::PacketHeaders (selective)")); - - return DoPacketHeaders(aHeaderSet, &aHeaderCheck); - } - -/** Sets a read activity observer. - -This replaces any previous observer. The observer will receive a callback -when the first read arrives for a put or get request. - -This does not transfer ownership. - -@publishedPartner -@released -@param aObserver The observer to receive packet process events. This may - be NULL. -*/ -EXPORT_C void CObexServer::SetReadActivityObserver(MObexReadActivityObserver* aObserver) - { - iPacketProcessSignaller->SetReadActivityObserver(aObserver); - } -/** -Contains the functionality for the PacketHeader interface functions in a refactored way. - -@param aHeaderSet A reference to a pointer that will be modified to NULL if no interesting -header are contained in the request packet, or to point to a new headerset containing -representations of the headers within the packet that are of interest. Ownership of the -headerset (when aHeaderSet is not NULL) is passed to the caller. -@param aHeaderCheck A pointer to an MObexHeaderCheck derived class that encapsulates -whether or not a particular header or headers should be included in the returned header -set (i.e. whether the headers are "interesting"). If the pointer is NULL then that is taken -to mean that all headers should be added to the aHeaderSet. - -@return KErrNone if successful, otherwise a system wide error code. -*/ -TInt CObexServer::DoPacketHeaders(CObexHeaderSet*& aHeaderSet, MObexHeaderCheck* aHeaderCheck) - { - FLOG(_L8("CObexServer::DoPacketHeaders")); - - TRAPD(err, aHeaderSet = CObexHeaderSet::NewL()); - if (err != KErrNone) - { - aHeaderSet = NULL; - return err; - } - - CObexPacket& packet = iTransportController->ReceivePacket(); - - // for "non-standard" size requests ensure we correctly start - // extracting headers where they start. - switch (packet.Opcode()) - { - case CObex::EOpConnect: - { - TObexConnectInfo connectInfo; - if (!iStateMachine->Transport().ExtractRemoteConnectInfo(packet, connectInfo.iVersion, connectInfo.iFlags)) - { - err = KErrUnderflow; - } - } - break; - case CObex::EOpSetPath: - { - TObexSetPathData data; - if (!packet.ExtractData(data)) - { - err = KErrUnderflow; - } - } - break; - default: - break; - } - - TBool interestingHeaders = EFalse; - TObexInternalHeader header; - // Call ExtractData() until it returns 0 bytes read - then we know the extract - // point will have been reset so the CObexPacket can be parsed again in the - // future. For this reason do not attempt to optimise this loop. - while(packet.ExtractData(header)) - { - // if there was an error previously we want to just keep going through the - // loop to reset the CObexPacket extraction point. - if (err == KErrNone && (!aHeaderCheck || aHeaderCheck->Interested(header.HI()))) - { - err = IrOBEXHeaderUtil::ParseHeader(header, *aHeaderSet); - if (!interestingHeaders && err == KErrNone) - { - interestingHeaders = ETrue; - } - } - } - - if (err != KErrNone || !interestingHeaders) - { - delete aHeaderSet; - aHeaderSet = NULL; - } - return err; - } - -/** -Provides additional interfaces for CObexServer. - -@param aUid The UID of the interface that is required. -@return A pointer to an instance implementing the interface represented by aUid. - -@leave KErrNotSupported if the given UID does not represent an interface CObexServer can provide. -@leave KErrInUse if an instance of MObexServerRequestPacketNotifyRegister has already been provided - by an earlier call to ExtensionInterfaceL, and it has not been released. - -@internalTechnology -*/ -EXPORT_C TAny* CObexServer::ExtensionInterfaceL(TUid aUid) - { - // MObexServerRequestPacketNotifyRegister interface - if (aUid == KObexServerRequestPacketNotifyRegisterInterface) - { - // We only return an instance if there are no other packet access extensions - // hooked into the CObexServer instance (indicated by the existance of an engine). - if (iServerRequestPacketEngine) - { - User::Leave(KErrInUse); - } - iServerRequestPacketEngine = CObexServerRequestPacketEngine::NewL(*this); - return static_cast(iServerRequestPacketEngine); - } - // if we don't know the interface UID then we don't support it. - User::Leave(KErrNotSupported); - return NULL; // to silence the compiler. - } - -/** -Returns a pointer to the TObexTransportInfo being used by the OBEX transport -layer. THE USER MUST NOT MODIFY THE OBJECT POINTED TO. -This is useful primarily when using OBEX over RFCOMM and the user has -specified 'KRfcommPassiveAutoBind' as the port. KRfcommPassiveAutoBind makes -RFCOMM itself find a free port. The user needs to know which port is really -being used by RFCOMM in order to correctly populate the SDP record. -May be called meaningfully after CObexServer::Start has returned KErrNone. -@return Pointer to the transport layer's transport info. -@publishedAll -@released -*/ -EXPORT_C const TObexTransportInfo* CObexServer::TransportInfo() const - { - LOG_LINE - LOG_FUNC - - __ASSERT_DEBUG(iTransportController, IrOBEXUtil::Fault(ETransportControllerNotCreated)); - return iTransportController->TransportInfo(); - } -