diff -r 000000000000 -r 8e480a14352b messagingfw/sendas/server/src/csendasmessage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/messagingfw/sendas/server/src/csendasmessage.cpp Mon Jan 18 20:36:02 2010 +0200 @@ -0,0 +1,1126 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "csendasmessage.h" + +#include +#include +#include +#include +#include +#include + +#include "csendassender.h" +#include "csendassession.h" +#include "csendasattachment.h" +#include "csendasmtmmanager.h" +#include "sendasserverdefs.h" +#include "csendasactivecontainer.h" +#include "csendaseditwatcher.h" +#include "tsendasserverpanic.h" + +// max index for incoming RMessage parameters +const TInt KSendAsMaxMessageIndex = 3; + +CSendAsMessage* CSendAsMessage::NewL(CSendAsSession& aSession) + { + CSendAsMessage* self = new(ELeave) CSendAsMessage(aSession); + return self; + } + +CSendAsMessage::~CSendAsMessage() + { + delete iSender; + delete iClientMtm; + delete iAttachment; + } + +CSendAsMessage::CSendAsMessage(CSendAsSession& aSession) +: iSession(aSession) + { + // the data members of iProgress are defined such that when they + // are zero initialised (like being embedded in a C-Class) this is + // the same as the default progress structure. + } + +/** Dispatch the subsession message to the correct handler. + +The return value indicates to the owner session if we are doing an +asynchronous operation. ETrue is async: EFalse is sync. +Errors can only be returned by leaving. This method should be encapsulated +in a trap harness to prevent the server AO from leaving during a ServiceL. + +@param aMesasge +The IPC message. + +@return +A value of true if the request is asynchronous, otherwise a value of false +indicating that the request is synchronous. +*/ +TBool CSendAsMessage::DoSubSessionServiceL(const RMessage2& aMessage) + { + TBool async = EFalse; + + switch( aMessage.Function() ) + { + case ESAMCreateForAccount: + { + CreateMessageForAccountL(aMessage); + } break; + case ESAMCreateByType: + { + CreateMessageByTypeL(aMessage); + } break; + case ESAMGetProgress: + { + ProgressL(aMessage); + } break; + case ESAMDelete: + { + DeleteMessageL(aMessage); + } break; + case ESAMSetBodyFirst: + { + SetBodyTextL(aMessage, ETrue); + } break; + case ESAMSetBodyNext: + { + SetBodyTextL(aMessage, EFalse); + } break; + case ESAMSetSubject: + { + SetSubjectL(aMessage); + } break; + case ESAMSetBioType: + { + SetBioTypeL(aMessage); + } break; + case ESAMAddRecipient: + { + AddRecipientL(aMessage); + } break; + case ESAMAddRecipientWithAlias: + { + AddRecipientWithAliasL(aMessage); + } break; + case ESAMTransferAttachmentFile: + { + TransferAttachmentFileL(aMessage); + } break; + case ESAMAddAttachment: + { + AddAttachmentL(aMessage); + async = ETrue; + } break; + case ESAMAddAttachmentWithType: + { + AddAttachmentWithMimeTypeL(aMessage); + async = ETrue; + } break; + case ESAMAddLinkedAttachment: + { + AddLinkedAttachmentL(aMessage); + async = ETrue; + } break; + case ESAMAddLinkedAttachmentWithType: + { + AddLinkedAttachmentWithMimeTypeL(aMessage); + async = ETrue; + } break; + case ESAMCreateAttachment: + { + // Message will complete upon transfering ownership of file to client. + CreateAttachmentL(aMessage); + async = ETrue; + } break; + case ESAMCreateAttachmentWithType: + { + // Message will complete upon transfering ownership of file to client. + CreateAttachmentWithMimeTypeL(aMessage); + async = ETrue; + } break; + case ESAMCancel: + { + Cancel(); + } break; + case ESAMLaunchEditor: + { + LaunchEditorL(aMessage); + } break; + case ESAMSendMessageConfirmed: + { + async = ETrue; + // drop through to next case... + } + case ESAMSendMessageConfirmedBackground: + { + SendMessageL(aMessage, ETrue, !async); + } break; + case ESAMSendMessage: + { + async = ETrue; + // drop through to next case... + } + case ESAMSendMessageBackground: + { + SendMessageL(aMessage, EFalse, !async); + } break; + case ESAMSaveMessage: + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage)); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + DoSaveMessageL(); + } break; + case ESAMSetCharacterSet: + { + // For setting the charset encoding value for the message. + SetMessageCharacterSetL(aMessage); + }break; + + case ESAMCharSetInfoForAttachment: + { + // For setting the charset encoding value for the message. + SetCharsetInfoForAttachment(aMessage); + }break; + default: + PanicClientL(aMessage, ESendAsClientPanicBadRequest); + break; + } + return async; + } + +/** Cancels any asynchronous activity being done by the message. +*/ +void CSendAsMessage::CancelMessage() + { + // cancelling is a safe thing to do, like closing. + switch( iState ) + { + case EAddingOrCreatingAttachment: + { + __ASSERT_DEBUG( iAttachment != NULL, User::Invariant() ); + + // cancel and then delete the attachment object. + iAttachment->Cancel(); + + delete iAttachment; + iAttachment = NULL; + } break; + case ESendingMessage: + { + __ASSERT_DEBUG( iSender != NULL, User::Invariant() ); + + // cancel and then delete the sender object. + iSender->Cancel(); + + delete iSender; + iSender = NULL; + } break; + case EIdle: + case EMessageCreated: + case ESendingMessageComplete: + case EMessageDeleted: + case EPendingClose: + default: + // do nothing as cancel is a safe thing... + break; + } + } + +/** Allocate a heap buffer to hold a descriptor parameter and copy it in. + +This method leaves if the desciptor is not valid. This method pushes the buffer +onto the cleanup stack and leaves it there when it returns. + +@param aMessage +The IPC message. + +@param aIndex +The index of the parameter to read from the IPC message. + +@return +A pointer to an array populated with the descriptor data. A copy of the pointer +is left on the cleanup stack. +*/ +HBufC* CSendAsMessage::GetDesParamLC(const RMessage2& aMessage, TInt aIndex) + { + __ASSERT_DEBUG((aIndex >=0 && aIndex <=KSendAsMaxMessageIndex), PanicClientL(aMessage, ESendAsClientPanicBadRequestArgument)); + + TInt len = User::LeaveIfError(aMessage.GetDesLength(aIndex)); + HBufC* buffer = HBufC::NewLC(len); + TPtr ptr(buffer->Des()); + aMessage.Read(aIndex, ptr); + return buffer; + } + +/** Allocate a heap buffer to hold a descriptor parameter and copy it in. + +This method leaves if the desciptor is not valid. This method pushes the buffer +onto the cleanup stack and leaves it there when it returns. + +@param aMessage +The IPC message. + +@param aIndex +The index of the parameter to read from the IPC message. + +@return +A pointer to an array populated with the descriptor data. A copy of the pointer +is left on the cleanup stack. +*/ +HBufC8* CSendAsMessage::GetDesParam8LC(const RMessage2& aMessage, TInt aIndex) + { + __ASSERT_DEBUG((aIndex >=0 && aIndex <=KSendAsMaxMessageIndex), PanicClientL(aMessage, ESendAsClientPanicBadRequestArgument)); + + TInt len = User::LeaveIfError(aMessage.GetDesLength(aIndex)); + HBufC8* buffer = HBufC8::NewLC(len); + TPtr8 ptr(buffer->Des()); + aMessage.Read(aIndex, ptr); + return buffer; + } + +/** +Create a message in the drafts folder and set up this CSendAsMessage instance to +operate on it. + +This method requires either an MTM UID or a valid account ID. + +If the MTM UID is NULL, then the account ID must be valid otherwise the method will +leave. If the account ID is valid, then the MTM of the account is used. + +@param aMtm +The UID of the MTM for the new message. + +@param aAccount +The account ID under which the message is created. + +@leave KErrNotFound +The MTM UID is NULL and the account ID is not valid. +*/ +void CSendAsMessage::DoCreateMessageL(TUid aMtm, TSendAsAccount aAccount) + { + // new context + CMsvEntry* msvEntry = CMsvEntry::NewL(iSession.GetMsvSessionL(), aAccount, TMsvSelectionOrdering()); + CleanupStack::PushL(msvEntry); + if( aMtm == KNullUid ) + { + if( aAccount == KMsvRootIndexEntryId || msvEntry->Entry().iType != KUidMsvServiceEntry ) + { + // not a valid account + User::Leave(KErrNotFound); + } + // get associated mtm uid + aMtm = msvEntry->Entry().iMtm; + } + + // create mtm by uid + DeleteClientMtm(); + iClientMtm = iSession.GetClientMtmL(aMtm); + + // if no account provided, get default for this MTM + if( aAccount == KMsvRootIndexEntryId ) + { + aAccount = iClientMtm->DefaultServiceL(); + } + + // create message in drafts folder + msvEntry->SetEntryL(KMsvDraftEntryId); + + // mtm takes ownership of entry context + CleanupStack::Pop(msvEntry); + iClientMtm->SetCurrentEntryL(msvEntry); + + // create message + iClientMtm->CreateMessageL(aAccount); + iMessageType = aMtm; + + // Change the context of the client MTM to the newly created message entry + iClientMtm->SwitchCurrentEntryL(iClientMtm->Entry().EntryId()); + + // Load the message created so far into the client MTMs cache. This ensures + // that SendAs will not overwrite any body text added during the + // CreateMessageL routine (such as signature text). + // Some MTMs don't actually write anything to store during the + // CreateMessageL phase, so the LoadMessageL can leave with KErrNotFound. + // It is safe to trap and ignore this as it just means there is no message + // body text to be loaded. + TRAPD(err, iClientMtm->LoadMessageL()); + + if (err != KErrNone && err != KErrNotFound) + { + User::Leave(err); + } + + iState = EMessageCreated; + } + +/** Create a new message in the drafts folder, using the specified account. + +The account to use is passed from the client in a package buffer, held in +parameter 0 of the message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::CreateMessageForAccountL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EIdle, PanicClientL(aMessage, ESendAsClientPanicSubsessionInUse) ); + + TPckgBuf accountBuf; + aMessage.Read(0, accountBuf); + + DoCreateMessageL(KNullUid, accountBuf()); + } + +/** Create a new message in the drafts folder, of the specified type. + +The message type (a TUid) to create is passed from the client in a package buffer, +held in parameter 0 of the message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::CreateMessageByTypeL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EIdle, PanicClientL(aMessage, ESendAsClientPanicSubsessionInUse) ); + + // extract mtm uid + TPckgBuf uidBuf; + aMessage.Read(0, uidBuf); + + // create message using default account for this mtm + DoCreateMessageL(uidBuf()); + } + +/** Delete the open message from the drafts folder. +@param aMessage +The IPC message. +*/ +void CSendAsMessage::DeleteMessageL(const RMessage2& aMessage) + { + // Client code should cancel any outstanding requests prior to deleting + // the message. Failure to do so will result in the client being panicked. + switch (iState) + { + case EMessageCreated: + { + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + iClientMtm->Session().RemoveEntry(iClientMtm->Entry().EntryId()); + iState = EMessageDeleted; + } break; + case EIdle: + case ESendingMessageComplete: + case EPendingClose: + break; + case ESendingMessage: + { + PanicClientL(aMessage, ESendAsClientPanicSendingMessage); + } break; + case EAddingOrCreatingAttachment: + { + PanicClientL(aMessage, ESendAsClientPanicAddingCreatingAttachment); + } break; + case EMessageDeleted: + default: + PanicClientL(aMessage, ESendAsClientPanicMessageAlreadyDeleted); + break; + } + + DeleteClientMtm(); + } + +/** Set the body text of the open message. + +The body text is specified using a CRichText object. This object is streamed +into a descriptor which is passed over the IPC boundary in parameter 0 of the +message. The CRichText object is then internalised by the server. + +@param aMessage +The IPC message. + +@param aFirstChunk +A flag that indicates whether this is the first chunk of rich text received +from the client. +*/ +void CSendAsMessage::SetBodyTextL(const RMessage2& aMessage, TBool aFirstChunk) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + TInt response = 0; + User::LeaveIfError(iClientMtm->QueryCapability(KUidMtmQuerySupportedBody, response)); + + // This location in the body text at which the text is to be inserted. + TInt insertLoc = 0; + + if( aFirstChunk ) + { + // Store the size of the signature text. This is added to the body + // text on creation of the message by the client MTM. + // All added body text is to be added before the signature text. + iSignatureSize = iClientMtm->Body().DocumentLength(); + } + else + { + // insert added text before the signature text. + insertLoc = iClientMtm->Body().DocumentLength() - iSignatureSize; + } + + TInt chunkLength = User::LeaveIfError(aMessage.GetDesLength(0)); + HBufC* textBuffer = GetDesParamLC(aMessage, 0); + // Append the next chunk to the mtm's CRichText object. + TRAPD(insertError, iClientMtm->Body().InsertL(insertLoc, *textBuffer)); + if( insertError != KErrNone ) + { + iClientMtm->Body().Reset(); + User::Leave(insertError); + } + CleanupStack::PopAndDestroy(textBuffer); + } + +/** Set the subject of the open message. + +The subject is plain text, held in a descriptor passed in parameter 0 of the +message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::SetSubjectL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + HBufC* subject = GetDesParamLC(aMessage, 0); + iClientMtm->SetSubjectL(*subject); + + CleanupStack::PopAndDestroy(subject); + } + +/** Set the BIO type of the message. + +The BIO type is passed from the client in a package buffer, pointed to by +parameter 0 of the message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::SetBioTypeL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + TPckgBuf uidBuf; + aMessage.Read(0, uidBuf); + iClientMtm->BioTypeChangedL(uidBuf()); + + TUid bioTypeUid = uidBuf(); + iClientMtm->SaveMessageL(); + + TMsvEntry tempEntry = iClientMtm->Entry().Entry(); + tempEntry.iBioType = bioTypeUid.iUid; + iClientMtm->Entry().ChangeL(tempEntry); + } + +/** Add a recipient (plus alias) to the message. + +The recipient's address is passed in a descriptor, pointed to by parameter 0 of +the message. The alias is also passed in as a descriptor, this time pointed to +by parameter 1. + +The recipient type (which indicates if a recipient is a To, Cc or Bcc type) is +passed inside a package buffer pointed to by parameter 2. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddRecipientWithAliasL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + HBufC* address = GetDesParamLC(aMessage, 0); + HBufC* alias = GetDesParamLC(aMessage, 1); + + TPckgBuf recipientTypeBuf; + aMessage.Read(2, recipientTypeBuf); + + // client MTM manages handling of Bcc recipients + iClientMtm->AddAddresseeL(recipientTypeBuf(), *address, *alias); + + CleanupStack::PopAndDestroy(2, address); // alias, address + } + +/** Add a recipient to the message. + +The recipient's address is passed in a descriptor, pointed to by parameter 0 of +the message. + +The recipient type (which indicates if a recipient is a To, Cc or Bcc type) is +passed inside a package buffer pointed to by parameter 1. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddRecipientL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + HBufC* address = GetDesParamLC(aMessage, 0); + + TPckgBuf recipientTypeBuf; + aMessage.Read(1, recipientTypeBuf); + + // call new implementation - let it manage handling of Bcc recipients + iClientMtm->AddAddresseeL(recipientTypeBuf(), *address); + + CleanupStack::PopAndDestroy(address); + } + +/** Prepare the message for subsequent add or create attachment operations. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::PrepareAddCreateAttachmentL(const RMessage2& aMessage) + { + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + // delete any orphaned attachment object. + delete iAttachment; + iAttachment = NULL; + + // queue the IPC message - used to notify the client when the attachment + // operation is complete. + iQueuedMessage = aMessage; + iAttachment = CSendAsAttachment::NewL(*this, *iClientMtm); + } + +/** Take ownership of the RFile attachment + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::TransferAttachmentFileL(const RMessage2& aMessage) + { + // Close if left open previously. + iAttachmentFile.Close(); + + // need to adopt the file from the client. + User::LeaveIfError(iAttachmentFile.AdoptFromClient(aMessage, 0, 1)); + } + +/** Add an attachment to the message. + +The attachment added to the message is copied into a new attachment by the +message server. The new attachment lives in the message store which is private +to the msg server. + +The client must pass a file handle over to the server. The server assumes that +this handle allows it to have read access to the open file once it has been adopted. + +The file handles are passed over in the usual manner. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddAttachmentL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->AddExistingFileAttachmentL(iAttachmentFile); + iState = EAddingOrCreatingAttachment; + } + +/** Add an attachment to the message. + +The attachment added to the message is copied into a new attachment by the +message server. The new attachment lives in the message store which is private +to the msg server. + +The client must pass a file handle over to the server. The server assumes that +this handle allows it to have read access to the open file once it has been adopted. + +The mime type of the attachment is specified in aMessage parameter 2. + +The file handles are passed over in the usual manner. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddAttachmentWithMimeTypeL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + HBufC8* mimeType = GetDesParam8LC(aMessage, 0); + TUint charset = aMessage.Int1(); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->AddExistingFileAttachmentWithMimeTypeL(iAttachmentFile, *mimeType, charset); + iState = EAddingOrCreatingAttachment; + + CleanupStack::PopAndDestroy(mimeType); + } + +/** Add an attachment to the message. + +The filename of the attachment is linked to the message. + +The file must be in a location which the message server is able to read and it +must also remain until the message has been sent or deleted. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddLinkedAttachmentL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + HBufC* fileName = GetDesParamLC(aMessage, 0); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->AddFileLinkAttachmentL(*fileName); + iState = EAddingOrCreatingAttachment; + + CleanupStack::PopAndDestroy(fileName); + } + +/** Add an attachment to the message. + +The filename of the attachment is linked to the message. + +The file must be in a location which the message server is able to read and it +must also remain until the message has been sent or deleted. + +The mime type of the linked file is supplied. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::AddLinkedAttachmentWithMimeTypeL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + HBufC* fileName = GetDesParamLC(aMessage, 0); + HBufC8* mimeType = GetDesParam8LC(aMessage, 1); + TUint charset = aMessage.Int2(); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->AddFileLinkAttachmentWithMimeTypeL(*fileName, *mimeType, charset); + iState = EAddingOrCreatingAttachment; + + CleanupStack::PopAndDestroy(2, fileName); // mimeType, fileName + } + +/** Create an empty attachment in the open message. + +The SendAs server makes the empty attachment and returns a file handle to the +client ready for writing. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::CreateAttachmentL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + HBufC* fileName = GetDesParamLC(aMessage, 1); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->CreateNewFileAttachmentL(iAttachmentFile, *fileName); + iState = EAddingOrCreatingAttachment; + + CleanupStack::PopAndDestroy(fileName); + } + +/** Create an empty attachment in the open message. + +The mime type of the attachment is specified. + +The SendAs server makes the empty attachment and returns a file handle to the +client ready for writing. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::CreateAttachmentWithMimeTypeL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + + HBufC* fileName = GetDesParamLC(aMessage, 1); + + HBufC8* mimeType = GetDesParam8LC(aMessage, 2); + + PrepareAddCreateAttachmentL(aMessage); + iAttachment->CreateNewFileAttachmentWithMimeTypeL(iAttachmentFile, *fileName, *mimeType, iCharSet); + iState = EAddingOrCreatingAttachment; + + CleanupStack::PopAndDestroy(2, fileName); // mimeType, fileName + } + +void CSendAsMessage::SetCharsetInfoForAttachment(const RMessage2& aMessage) + { + iCharSet = aMessage.Int0(); + } + + +/** Cancel any asynchronous activity being done by the message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::Cancel() + { + CancelMessage(); + } + +/** Launch the message editor for the open message. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::LaunchEditorL(const RMessage2& aMessage) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + + iClientMtm->SaveMessageL(); + + // the observer and owner is the SendAs Server, via active container. + CSendAsEditWatcher* editWatcher = CSendAsEditWatcher::NewL(iSession.ActiveContainer(), iSession.EditUtilsPluginUid()); + iSession.ActiveContainer().AddEditWatcherL(*editWatcher); + + TMsvId entryId = iClientMtm->Entry().EntryId(); + DeleteClientMtm(); + editWatcher->LaunchEditorL(entryId); + + iState = EPendingClose; + } + +/** Send the message. + +This method handles both confirmed and unconfirmed sends. If an unconfirmed send +is attempted but the client does not have the MTM-required capabilities then the +send will be downgraded to a confirmed send. + +@param aMessage +The IPC message. + +@param aConfirmed +A value of true indicates that the send should be confirmed. + +@param aBackground +A value of true indicates that the send should be done in the background. This +implies that the client does not wish to be notified of the progress of the send. + +@leave KErrNotSupported +The client MTM does not support SendAs message sending. +*/ +void CSendAsMessage::SendMessageL(const RMessage2& aMessage, TBool aConfirmed, TBool aBackground) + { + __ASSERT_ALWAYS( iState == EMessageCreated, PanicClientL(aMessage, ESendAsClientPanicNoCurrentMessage) ); + __ASSERT_DEBUG( iClientMtm != NULL, User::Invariant() ); + +#if (defined SYMBIAN_USER_PROMPT_SERVICE) + TBool hasCapability = EFalse; +#endif + // for unconfirmed send, check capabilities of client + if( !aConfirmed ) + { + VerifyCallerCapabilitiesL(aMessage, aConfirmed); +#if (defined SYMBIAN_USER_PROMPT_SERVICE) + if(!aConfirmed) + { + hasCapability = ETrue; + } +#endif + } + + + TBool result = KErrNone; + if( iClientMtm->QueryCapability(KUidMtmQuerySendAsMessageSendSupport, result) == KErrNotSupported ) + { + User::Leave(KErrNotSupported); + } + // save message + DoSaveMessageL(); + + // create the sender obejct + CSendAsSender* sender = NULL; + if( aBackground ) + { + // this a background send - the observer and owner is the active container. + sender = CSendAsSender::NewL(iSession.ActiveContainer()); + iSession.ActiveContainer().AddSenderL(*sender); + + iState = ESendingMessageComplete; + } + else + { + // this is a non-background send - the observer and owner is the send-as + // message. + delete iSender; + iSender = NULL; + + sender = CSendAsSender::NewL(*this); + iSender = sender; + + // queue the IPC message - used to notify the client when the send is complete + iQueuedMessage = aMessage; + + iState = ESendingMessage; + } + // new sender takes ownership of the client MTM + sender->SetClientMtm(*iClientMtm); + +#if (defined SYMBIAN_USER_PROMPT_SERVICE) + TRAPD(err, sender->SendMessageL(aMessage, hasCapability)); + if(err!=KErrNone) + { + // There was a error while sending the message... so delete the entry + iClientMtm->Session().RemoveEntry(iClientMtm->Entry().EntryId()); + iClientMtm = NULL; + iState = ESendingMessageComplete; + User::Leave(err); + } + iClientMtm = NULL; +#else + iClientMtm = NULL; + if( aConfirmed ) + { + TSecurityInfo securityInfo(aMessage); + sender->SendMessage(securityInfo, iSession.NotifierUid()); + } + else + { + sender->SendMessage(); + } +#endif + } + +void CSendAsMessage::DeleteClientMtm() + { + delete iClientMtm; + iClientMtm = NULL; + } + +/** Get progress information + +Used to provide progress information to the client. + +1. Before sending, the progress is set to ESendStateInPreparation. + +2. Whilst sending, the progress is obtained from the send operation. + +3. After sending, the final progress is returned to the client (always a TMsvSendOperationProgress). + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::ProgressL(const RMessage2& aMessage) + { + switch( iState ) + { + case EIdle: + case EMessageCreated: + case EAddingOrCreatingAttachment: + case EMessageDeleted: + case EPendingClose: + { + iProgress().iState = CMsvSendOperation::ESendStateInPreparation; + iProgress().iError = KErrNone; + iProgress().iProgressMax = 0; + iProgress().iProgress = 0; + } break; + case ESendingMessage: + { + __ASSERT_DEBUG( iSender != NULL, User::Invariant() ); + + iSender->ProgressL(iProgress); + } break; + case ESendingMessageComplete: + { + // return the final progress + if( iProgress().iError == KErrNone ) + { + iProgress().iState = CMsvSendOperation::ESendStateDone; + } + else + { + iProgress().iState = CMsvSendOperation::ESendStateFailed; + } + } break; + default: + User::Invariant(); + break; + } + + aMessage.WriteL(0, iProgress); + } + +/** Verify that the client has the required capabilities to use the server MTM +to send this message unconfirmed. Otherwise changes request to confirmed send. + +@param aMessage +The IPC message. +*/ +void CSendAsMessage::VerifyCallerCapabilitiesL(const RMessage2& aMessage, TBool& aConfirmed) + { + // get required capabilities + TCapabilitySet caps; + iClientMtm->Session().GetMtmRequiredCapabilitiesL(iMessageType, caps); + + // get client thread capabilities + TSecurityInfo securityInfo(aMessage); + + // check client capabilities with required capabilities + if( !securityInfo.iCaps.HasCapabilities(caps) ) + { + // fail capability check... + caps.Remove(securityInfo.iCaps); + PlatSec::CapabilityCheckFail(aMessage, caps, __PLATSEC_DIAGNOSTIC_STRING("Unconfirmed send downgraded to confirmed by CSendAsMessage::VerifyCallerCapabilitiesL")); + aConfirmed = ETrue; + } + } + +/** Panic the client + +@param aMessage +The IPC message. + +@param aPanic +The panic code. +*/ +void CSendAsMessage::PanicClientL(const RMessage2& aMessage, TSendAsClientPanic aPanic) const + { + iSession.PanicClient(aMessage, aPanic); + } + +/* + * Methods from MSendAsSenderObserver + */ + +/** Notify the client that the send has completed + +@param aError +The error code. +*/ +void CSendAsMessage::SenderComplete(TInt aError) + { + __ASSERT_ALWAYS( iState == ESendingMessage, User::Invariant() ); + __ASSERT_DEBUG( iSender != NULL, User::Invariant() ); + + // get final progress from the sender. + iSender->FinalProgress(iProgress); + + // capture error + if( iProgress().iError == KErrNone ) + { + iProgress().iError = aError; + } + // notify the client that the send has completed - always complete with + // KErrNone as actual error is in the progress. + iQueuedMessage.Complete(KErrNone); + + // Set the state to complete so we can call FinalProgress + iState = ESendingMessageComplete; + } + +/* + * Methods from MSendAsAttachmentObserver + */ + +/** Notify the client that the attachment operation has completed + +If the message is complete, the client is notified, otherwise the created +attachment is transferred to the client. + +@param aError +The error code. + +@param aCompleteMessage +Indicates if the message is complete. +*/ +void CSendAsMessage::AttachmentCompleteL(TInt aError, TBool aCompleteMessage) + { + __ASSERT_ALWAYS( iState == EAddingOrCreatingAttachment, User::Invariant() ); + + // notify the client that the attachment operation has completed - if an + // error has occurred the error code is returned in the complete status. + if (aCompleteMessage) + { + iQueuedMessage.Complete(aError); + } + else + { + // transfer the created file to the client. Argument 0 of iQueuedMessage + // contains the address to which the client should receive the file handle. + User::LeaveIfError(iAttachmentFile.TransferToClient(iQueuedMessage, 0)); + iAttachmentFile.Close(); + } + // the attachment file is now closed - set it to a blank RFile so that any + // later closes don't panic. + iAttachmentFile = RFile(); + + // The client can continue editing the message - move to appropriate state + // and delete attachment object + + // Delete the attachment object after use. If this is a notification of a + // cancel then the delete should happen after the cancel operation to avoid + // a second cancel in the attachment object's destructor. + if( aError != KErrCancel ) + { + delete iAttachment; + iAttachment = NULL; + } + + iState = EMessageCreated; + } + +/** Sets the message entry's Visible flag to ETrue and inPreparation flag to EFalse +saves the message, commits the changes +*/ + +void CSendAsMessage::DoSaveMessageL() + { + iClientMtm->SaveMessageL(); + + // set the InPreparation to false and visible to true + TMsvEntry tempEntry= iClientMtm->Entry().Entry(); + tempEntry.iDate.UniversalTime(); + tempEntry.SetVisible(ETrue); + tempEntry.SetInPreparation(EFalse); + + iClientMtm->Entry().ChangeL(tempEntry); + } + +/** +Sets the character encoding value. The character encoding value options are 7-bit, +8-bit and 16-Bit Unicode. By default the character set encoding is 7 bit encoding. +It call the MTM functionality for setting the charset value. +@param aMessage The IPC message. +@return void +*/ + +void CSendAsMessage::SetMessageCharacterSetL(const RMessage2& aMessage) + { + TInt err = iClientMtm->SetMessageCharacterSet(aMessage.Int0()); + TPckgBuf pckg = err; + aMessage.WriteL(1,pckg); + }