--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/obex/obexprotocol/obex/src/obexserver.cpp Tue Feb 02 01:11:40 2010 +0200
@@ -0,0 +1,1588 @@
+// 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 <charconv.h>
+#include <utf.h>
+#include <obex/internal/obexinternalheader.h>
+#include <obextransportinfo.h>
+#include <obex.h>
+#include <obex/internal/obexpacket.h>
+#include <obex/transport/obextransportcontrollerbase.h>
+#include <obex/internal/obextransportconstants.h>
+#include <obex/extensionapis/mobexserverrequestpacketnotify.h>
+#include <obex/internal/mobexserverrequestpacketnotifyregister.h>
+#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<TUint8*> (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<TUint8*> (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<MObexServerRequestPacketNotifyRegister*>(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();
+ }
+