diff -r 000000000000 -r 72b543305e3a messagingappbase/obexmtms/obexmtm/obexserver/source/obexSendOp_common.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingappbase/obexmtms/obexmtm/obexserver/source/obexSendOp_common.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,703 @@ +// Copyright (c) 2001-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: +// + +//class include +#include + +//System includes +#include +#include //CMsvServerEntry +#include //KUidMsgTypeBt +#include //KUidMsgTypeIr +#include + +#include +#include +#include // CApaApplication::GenerateFileName +#include // RApaLsSession <-- data recognition. + +//user includes + +// Following are named KErrIrObexClientXXXXX because they are using the irobex.dll (which +// should really be called obex.dll now that it supports more than IR) +const TInt KErrIrObexClientFirstPutFailed = -5510; +const TInt KErrIrObexClientSubsequentPutFailed = -5511; +const TInt KMaxBufSizeForRecognition = 1024; + + +EXPORT_C CObexServerSendOperation::CObexServerSendOperation(TUid aMsgTypeUid, CMsvServerEntry& aSendObj, TInt aConnectTimeoutMicroSeconds, TInt aPutTimeoutMicroSeconds, TRequestStatus& aObserverRequestStatus) + :CActive(CActive::EPriorityHigh), + iAsyncInit(EFalse), + iObserverRequestStatus(aObserverRequestStatus), + iMsvSendParent(aSendObj), + iConnectTimeout(aConnectTimeoutMicroSeconds), + iPutTimeout(aPutTimeoutMicroSeconds), + iCancelWithoutCompleting(EFalse), + iProgressPckg(TObexMtmProgress()), + iMtm(aMsgTypeUid) +/** + * Constructor. + * + * @param aMsgTypeUid UID of message type + * @param aSendObj Reference to the object to send. + * @param aConnectTimeoutMicroSeconds Timeout period for Connect operation in microseconds. + * @param aPutTimeoutMicroseconds Timeout period for Put operation in microseconds. + * @param aObserverRequestStatus TRequestStatus of owning active object. + */ + { + } + +EXPORT_C CObexServerSendOperation::CObexServerSendOperation + (TUid aMsgTypeUid, CMsvServerEntry& aSendObj,TInt aConnectTimeoutMicroSeconds, + TInt aPutTimeoutMicroSeconds, TRequestStatus& aObserverRequestStatus, + TBool aLastSendAttempt) + :CActive(CActive::EPriorityHigh), + iAsyncInit(EFalse), + iLastSendAttempt(aLastSendAttempt), + iObserverRequestStatus(aObserverRequestStatus), + iMsvSendParent(aSendObj), + iConnectTimeout(aConnectTimeoutMicroSeconds), + iPutTimeout(aPutTimeoutMicroSeconds), + iCancelWithoutCompleting(EFalse), + iProgressPckg(TObexMtmProgress()), + iMtm(aMsgTypeUid) + +/** + * Constructor. + * + * @param aMsgTypeUid UID of message type + * @param aSendObj Reference to the object to send. + * @param aConnectTimeoutMicroSeconds Timeout period for Connect operation in microseconds. + * @param aPutTimeoutMicroseconds Timeout period for Put operation in microseconds. + * @param aObserverRequestStatus TRequestStatus of owning active object. + * @param aLastSendAttempt TBool flag to check for second send attempt and also for sending headers. EFalse sends full headers, ETrue only sends name and size + */ + { + } + + +EXPORT_C void CObexServerSendOperation::ConstructL(const TDesC* aConnectPassword) +/** + * Second phase constructor. Sets up connection to the FileServer, initialises attachments or filename list then + * starts sending process by initialising. + * + * @param aConnectPassword Pointer to the password to be used for authentication. + * @leave Leaves if insufficient memory. + * @leave Leaves if cannot connect to FileServer. + */ + { + if (aConnectPassword->Length()) + { + // take our own copy of the connection password + HBufC* password=aConnectPassword->AllocL(); + iConnectPassword=password; + } + + BuildSpecificConstructL(); + + + + TRequestStatus* observerStatus = &iObserverRequestStatus; + *observerStatus = KRequestPending; + + + + // If no attachments have been found, try using the Attachment API + // Try Attachment API... + CMsvStore* store = iMsvSendParent.ReadStoreL(); + CleanupStack::PushL(store); + MMsvAttachmentManager& manager = store->AttachmentManagerL(); + iAttachmentEntryCount = manager.AttachmentCount(); + iNextAttachment = iAttachmentEntryCount-1; + CleanupStack::PopAndDestroy(store); + + + iTimeoutTimer = CObexSendOpTimeout::NewL(this); + + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); + + if ( iAttachmentEntryCount > 0 ) + { + // START SENDING + iSendState = TObexMtmProgress::EInitialise; + CompleteSelf(KErrNone); + } + else + { + //Nothing to send + iSendState = TObexMtmProgress::ESendError; + CompleteSelf(KErrNone); + } + } + +EXPORT_C CObexServerSendOperation::~CObexServerSendOperation() +/** + * Destructor. Cancel()s, deletes owned objects and Close()s the connection to the FileServer. + */ + { + Cancel(); + delete iObexClient; + delete iObexObject; + delete iTimeoutTimer; + delete iConnectPassword; + delete iMoveOperation; + delete iMoveEntrySelection; + + + BuildSpecificDestructor(); + } + +void CObexServerSendOperation::TimeOut() +/** + * Called when the current operation times out. Causes the current operation to be cancelled, then reactivates with + * the appropriate error code (KErrIrObexClientNoDevicesFound or KErrIrObexClientPutPeerAborted). + */ + { + switch (iSendState) + { + case TObexMtmProgress::EInitialise: + case TObexMtmProgress::EConnect: + case TObexMtmProgress::EConnectAttemptComplete: + ActivateRunLWithError(KErrIrObexClientNoDevicesFound); + break; + case TObexMtmProgress::ESendObject: // Next RunL: start first send (not started sending yet) + case TObexMtmProgress::ESendNextObject: // Next RunL: start next send (still sending current object) + case TObexMtmProgress::ESendComplete: // Next RunL: nothing left to send (still sending current object) + case TObexMtmProgress::EDisconnected: // Trying to disconnect + ActivateRunLWithError(KErrIrObexClientPutPeerAborted); + break; + default: + Panic(EObexSendOperationUnexpectedTimeout); + } + } + +void CObexServerSendOperation::ActivateRunLWithError(TInt aError) +/** + * Cancels the current operation, then reactivates with the given error code. + * + * @param aError Error code to be passed to CompleteSelf. + */ +{ + ExplicitCancel(); + + // Ensure RunL() is called to handle the error + iSendState=TObexMtmProgress::ESendError; + SetActive(); // Schedule so can CompleteObserverL() using given error code when RunL() is called again + CompleteSelf(aError); +} + +void CObexServerSendOperation::ExplicitCancel() +/** + * Cancel any pending obex operation without completing the observer. + */ + { + // Cancel any pending obex operation without completing the observer (the observer + // owns this messaging operation, it's request status is iObserverRequestStatus) + iCancelWithoutCompleting = ETrue; //this prevents the observer from completing when DoCancel() is called by the active object framework + Cancel(); + iCancelWithoutCompleting = EFalse; + } + +EXPORT_C void CObexServerSendOperation::DoCancel() +/** + * Cancels the current operation, deletes the client and Cancel()s the timeout timer. Only completes the observer + * (by a call to CompleteObserverL) if an external entity (i.e. the owner) has called Cancel(). + * Otherwise the observer is not completed. + */ + { + // Have to delete the Obex client here or it mucks up the active scheduler. + // Abort doesn't cut the mustard. + delete iObexClient; + iObexClient=NULL; + + // Delete the Obex object as we may try to delete the message store entry + // when we complete the observer and this will fail as the iObexObject has the + // associated attachment file open. + delete iObexObject; + iObexObject = NULL; + + iTimeoutTimer->Cancel(); + + if(!iCancelWithoutCompleting) + { + // We will only get here if an external entity has called Cancel() on us, only + // the user (owner) of this active object will do this. In most cases this + // will be the direct result of a user action, e.g. cancelling a dialog + iSendState = TObexMtmProgress::EUserCancelled; + CompleteObserverL(); // complete the observer which owns this messaging op + } + } + +EXPORT_C TBool CObexServerSendOperation::CompletingObserver(TInt aErrorCode) +/** +* This is the default implementation that allows the server mtm to continue normally. +* Tells the derived class that the base class is about to complete the observer. +* This is the first thing called when CompleteObserverL is called. +* Since the behaviour of CompleteObserverL is to clean up the message that it was trying to send, +* this calls gives the derived class an opportunity to either stop this deletion or recover any information +* synchronously from the message. +* If the derived class has no need to use this functionality, the default implementation allows deletion. +* @param aErrorCode The last error code encountered during operation +* @return TBool True delete the message +* @return TBool False DO NOT delete the message +*/ + { + (void)aErrorCode; + + return ETrue; + } + +void CObexServerSendOperation::CompleteObserverL() //CMsvOperation completed. +/** + * Complete the observer, reporting any error via the progress. THIS METHOD MUST BE CALLED ONCE ONLY. + * + */ + { + ProgressL(); + TObexMtmProgress& progress = iProgressPckg(); + + // If the message should be deleted then do it here. +#ifndef MOVE_MESSAGE_TO_SENT + + // this call gives the derived class an opportunity to control this behaviour. + + // note that the error passed into this is the final one which the server mtm wants to + // report to the UI - occasionally, this will be KErrNone when in fact an Obex error + // did occur. To make sure that we tell the derived class exactly what happened, we + // pass the current state of the progress indicator out to it. + + if(CompletingObserver(progress.iError)) + SynchronousEntryDelete(); +#endif + + TRequestStatus* status = &iObserverRequestStatus; + User::RequestComplete(status, KErrNone); + } + +void CObexServerSendOperation::CompleteSelf(TInt aError) +/** + * This causes this active object's request to complete which means + * RunL() will be called again if we are active (immediately if there + * are no higher priority active objects in the active scheduler). + * + * @param aError Error to be passed forward to the next step of the state machine + */ + { + // This causes this active objects request to complete which means + // RunL() will be called again if we are active (immediately if there + // are no higher priority active objects in the active scheduler). + TRequestStatus* status = &iStatus; + User::RequestComplete(status, aError); + } + +EXPORT_C void CObexServerSendOperation::RunL() +/** + * Calls RealRunL(), and traps errors + * + * @leave Leaves with errors from RealRunL() + */ + { + TRAPD(err, RealRunL()); + + if(err) + { + ExplicitCancel(); //don't complete the observer, just cancel sending activity + + // if RealRunL left in ESendError, sending it back would cause an endless loop! + if (iSendState == TObexMtmProgress::ESendError) + { + iStatus = err; + CompleteObserverL(); + + // update iStatus "manually" so that it shows the REAL error (the error that + // made RealRunL leave, not the previous error that made it go to ESendError) + // NOTE: if we left iStatus unchanged here, the observer would get the error + // that caused the send operation to fail. If for any reason this were thought to be + // better, just removing the following line would do. + } + else + { + iSendState = TObexMtmProgress::ESendError; + + // this is so that RunL-RealRunL are called immediately to handle the error + // (the observer will be completed in RealRunL) + + SetActive(); + CompleteSelf(err); + } + + User::Leave(err); // Allow system to display an error. + } + } + +/** + * This is not required to do anything in the base implementation. + */ + +EXPORT_C void CObexServerSendOperation::SecondPhaseObexClientInitL() + { + } + +EXPORT_C void CObexServerSendOperation::PreConnectOperations() + { + } + +EXPORT_C void CObexServerSendOperation::PostConnectOperations() + { + } + +EXPORT_C void CObexServerSendOperation::PreSendOperations() + { + } + +EXPORT_C void CObexServerSendOperation::PostSendOperations() + { + } + +TInt CObexServerSendOperation::SynchronousEntryDelete() +/** + * Delete the outbox entry as operation has 'completed'. + * Will be invisible&InPreparation anyway (MS delete will delete it the next + * time it starts). + */ + { + // Delete the outbox entry as operation has 'completed'. + // Will be invisible&InPreparation anyway (MS delete will delete it the next + // time it starts). + TInt err; + const TMsvEntry& tMsvSentEntry = iMsvSendParent.Entry(); + TInt messageId = tMsvSentEntry.Id(); + err = iMsvSendParent.SetEntry(tMsvSentEntry.Parent()); + if (err == KErrNone) + { + err = iMsvSendParent.DeleteEntry(messageId); + } + return err; + } + +void CObexServerSendOperation::InitialiseAttachmentL(CMsvServerEntry& aParent, TInt aWhichAttachment) +/** + * Load an attachment into the obex sending buffer, and create a new Obex object of name TMsvEntry::iDetails. + * + * @param aParent Reference to CMsvServerEntry to be sent. + * @param aWhichAttachment Zero-based index of attachment to send. + * @leave KErrXXX system wide error codes + */ + { + // Load an attachment into the obex sending buffer + + // use the Attachment API to load the attachment files + CMsvStore* store = aParent.ReadStoreL(); + CleanupStack::PushL(store); + CMsvAttachment* attachment = store->AttachmentManagerL().GetAttachmentInfoL(aWhichAttachment); + CleanupStack::PushL(attachment); + LoadFileIntoObjectL(attachment->FilePath(), attachment->AttachmentName(), attachment->MimeType()); + + // if any additional obex headers have been provided, set them + // in the obex object + TPtrC8 headerData; + if( attachment->GetDesC8Attribute(KUidObexHeaders, headerData) == KErrNone ) + { + // header data exists, try to build an obex header list + CObexHeaderList* headerList = CObexHeaderList::NewLC(); + headerList->ImportFromAttachmentL(*attachment); + headerList->AddHeadersToBaseObjectL(*iObexObject); + CleanupStack::PopAndDestroy(headerList); + } + + CleanupStack::PopAndDestroy(2, store); // attachment, store + } + +void CObexServerSendOperation::LoadFileIntoObjectL(const TDesC& aFileName, const TDesC& aObexName, const TDesC8& aMimeType) + { + // Load the file into our send data buffer. + // Create a new Obex object from the file. + + // Check we actually have a file + TEntry fileEntry; + User::LeaveIfError( FileSession().Entry(aFileName, fileEntry) ); + if(fileEntry.IsDir()) + User::Leave(KErrNotSupported); //Can't send a directory. + + //Store the filename associated with the obex object + iSendFile = aFileName; + + // Get the file length + RFile file; + User::LeaveIfError( file.Open(FileSession(), aFileName, EFileRead | EFileShareReadersOnly) ); + CleanupClose::PushL(file); + TInt fileLength; + User::LeaveIfError( file.Size(fileLength) ); + + // To allow file type recognition, read the file into sendBody + // only need the first KMaxBufSizeForRecognition bytes + HBufC8* sendBody = HBufC8::NewLC(fileLength < KMaxBufSizeForRecognition ? fileLength : KMaxBufSizeForRecognition); + TPtr8 sendBodyPtr = sendBody->Des(); + User::LeaveIfError( file.Read(sendBodyPtr) ); + CleanupStack::Pop(); //sendBodyPtr; + CleanupStack::PopAndDestroy(); //file.Close(); + CleanupStack::PushL(sendBody); //Must be a better way to do this! + + // Create an Obex object from the file. + delete iObexObject; + iObexObject=NULL; + iObexObject = CObexFileObject::NewL(iSendFile); + + // Set the obex object name field + if(aObexName.Length()!=0) + { + iObexObject->SetNameL(aObexName); // Only *set* a header field if have some data, otherwise Obex will assert. + } + + if( aMimeType.Length() > 0 ) + { + iObexObject->SetTypeL(aMimeType); + } + else + { + // Attempt to recognise the datatype (MIME type) of the data in the send buffer + // but only set obex object type field if we are pretty confident of the + // Recogniser's result + RApaLsSession lsSess; + if( lsSess.Connect() == KErrNone) + { + CleanupClosePushL(lsSess); + + TDataRecognitionResult result; + User::LeaveIfError( lsSess.RecognizeData(aObexName, sendBodyPtr, result) ); + if (result.iConfidence>=CApaDataRecognizerType::EProbable) + { + iObexObject->SetTypeL(result.iDataType.Des8()); //only set a header field if have some data, otherwise Obex will assert. + } + CleanupStack::PopAndDestroy(); // lsSess; + } + } + + // Set obex object length field for benefit of remote device's UI + iObexObject->SetLengthL(fileLength); + + // Set obex object time field + TTime modifiedTime; + FileSession().Modified(aFileName, modifiedTime); + iObexObject->SetTimeL(modifiedTime); + + //Cleanup + CleanupStack::PopAndDestroy(); //sendBody + } + + +EXPORT_C void CObexServerSendOperation::GetUserPasswordL(const TDesC& /*aRelm*/) +/** + * Called by the Obex Client when authentication is requested to pass the password back. If the password is invalid, this + * call should succeed but the send operation as a whole will inevitably fail. + * + * @param aRelm ignored, but could be used to indicate which password to use. + * @leave KErrXXX system wide error codes. Shouldn't leave just because the password is invalid. + * @leave KErrIrObexConnectChallRejected, if password is found empty. + */ + { + //A "pusher" may not know in advance that the remote will issue an OBEX authentication challenge when connecting an OBEX session. + // As such if a password is not provided the MTM will return, KErrIrObexConnectChallRejected + if(!iConnectPassword) + { + User::Leave(KErrIrObexConnectChallRejected); + } + + //Simply pass back the default user password + iObexClient->UserPasswordL(*iConnectPassword); + } + + +TBool CObexServerSendOperation::CheckStatusOfLastObject(TInt aStatus, TObexMtmProgress::TSendState aSendState) +/** + * Checks the last object was sent correctly, and tries to action appropriate error feedback if not. Only to be called + * from ESendObject/ESendNextObject or ESendComplete states. + * + * @param aStatus Status of last object + * @return ETrue if message was OK--EFalse if message failed and this function has taken the necessary action + */ + { + if(aStatus != KErrNone) + { + if ((aStatus==KErrInUse) || (aStatus==KErrArgument)) + { + //May indicate a problem with sending the previous object, or an error with a previous connection still + //being active--try and decide which + if (aSendState == TObexMtmProgress::ESendNextObject) + { + // Have managed to send at least one object - receiver might not be + // able to accept multiple objects. + CompleteSelf(KErrIrObexClientSubsequentPutFailed); //would be useful if this err was handled with a "Try sending one at a time" message to the user + } + else + { + // Have connected OK but couldn't send, so receiver might be dealing with + // a previous connection + CompleteSelf(KErrIrObexClientFirstPutFailed); //different to EMultiObjectSendError + } + } + else + { + // May indicate an error with the first or subsequent object + // Pass error code onto the owner of this messaging operation + CompleteSelf(aStatus); // General error. + } + + iSendState=TObexMtmProgress::ESendError; + SetActive(); + return EFalse; + } + else + { + return ETrue; + } + } + +TInt CObexServerSendOperation::PrepareCurrentObjectAndSetStateL() +/** + * Loads the next object to be sent, whether an attachment or a file in the file list. + * + * @return KErrXXX standard error code + * @return KErrNotFound if there were neither attachments nor files in the file list + * @leave KErrXXX system wide error codes + */ + { + // Have attachment(s) to be sent. + InitialiseAttachmentL(iMsvSendParent, iNextAttachment); //Load data into send buffer. + --iNextAttachment; + if(iNextAttachment<0) //iNextAttachment is 0 for a 1 attachment entry. + { + // Send will be complete after the current object has been sent so + // go to next state after the send operation completes + iSendState=TObexMtmProgress::ESendComplete; + } + else + { + // Still have another object to send after current object has been + // sent so return to this state after the send operation completes + iSendState=TObexMtmProgress::ESendNextObject; + } + + //Successfully sent an attachment specified object + return KErrNone; + } + +void CObexServerSendOperation::MoveToSentAndSetActiveL() +/** + * Moves the newly sent message to the global sent items folder, and sets active ready for its completion. + * + * @leave KErrXXX system wide error codes + */ + { + iMoveEntrySelection = new (ELeave) CMsvEntrySelection; + iMoveEntrySelection->AppendL(iMsvSendParent.Entry().Id()); //i.e. just the sent message + iMsvSendParent.SetEntry(iMsvSendParent.Entry().Parent()); //Move to the parent + iMsvSendParent.MoveEntriesL(*iMoveEntrySelection, KMsvSentEntryId, iStatus); + iSendState = TObexMtmProgress::EMovedToSent; + SetActive(); + } + +void CObexServerSendOperation::CleanupAfterMovedToSent() +/** + * Restores after the message has been moved to the inbox, and marks the message as visible. + */ + { + iMsvSendParent.SetEntry(iMoveEntrySelection->At(0)); //Switch back to actual message + TMsvEntry entry = iMsvSendParent.Entry(); + entry.SetVisible(ETrue); // Moved OK. Make the entry visible and flag it as complete. + entry.SetInPreparation(EFalse); + iMsvSendParent.ChangeEntry(entry); //Commit changes + iMsvSendParent.SetEntry(KMsvNullIndexEntryId); //Unlock the entry + } + + +RFs& CObexServerSendOperation::FileSession() +/** + * Returns a reference to the file session (RFs) of the message + * + * @return A reference to the file session of the the message + */ + { + return iMsvSendParent.FileSession(); + } + + +// +// +// CObexSendOpTimeout +// +// + +CObexSendOpTimeout* CObexSendOpTimeout::NewL(CObexServerSendOperation* aSendOperation) +/** + *Canonical NewL function, which also sets the owner operation. + * + *@param aSendOperation Obex send operation which will be "timed out" when the timer expires + */ + { + CObexSendOpTimeout* self=new (ELeave) CObexSendOpTimeout; + CleanupStack::PushL(self); + self->ConstructL(); + self->iSendOperation = aSendOperation; + CleanupStack::Pop(); + return self; + } + +CObexSendOpTimeout::CObexSendOpTimeout() + : CTimer(CActive::EPriorityStandard) + // Construct zero-priority active object +/** + * Constructor. Calls CTimer's constructor with priority EPriorityStandard + */ + {}; + +void CObexSendOpTimeout::ConstructL() +/** + * Second phase constructor. Calls CTimer::ConstructL(), and adds itself to the active scheduler + * + * @leave KErrXXX system wide error codes + */ + { + CTimer::ConstructL(); + CActiveScheduler::Add(this); + } + +void CObexSendOpTimeout::RunL() +/** + * Calls the TimeOut method of the associated CObexServerSendOperation when the timer expires + * + * @leave KErrXXX system wide error codes + */ + { + iSendOperation->TimeOut(); + } + + + + +// +// +// Panic +// +// + +GLDEF_C void Panic(TObexSendOperationPanic aPanic) + { + _LIT(KObexSendOperationPanic,"ObexSendOp"); + User::Panic(KObexSendOperationPanic,aPanic); + } +