messagingappbase/sendui/meetingrequestsender/src/MeetingRequestSender.cpp
changeset 0 72b543305e3a
child 16 e00582ce7ecd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/sendui/meetingrequestsender/src/MeetingRequestSender.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,891 @@
+/*
+* Copyright (c) 2002-2005 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: MeetingRequestSender implementation
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <e32base.h>
+#include <e32std.h>
+#include <SendUi.h>                     // TSendingCapabilities
+#include <e32svr.h>
+#include <mtclreg.h>                    // CClientMtmRegistry
+#include <mtclbase.h>                   // CBaseMtm
+#include <miuthdr.h>
+#include <txtrich.h>
+#include <MuiuMsvUiServiceUtilities.h>
+#include <s32mem.h>                     // CBufStore
+#include <apmstd.h>
+#include <mtmextendedcapabilities.hrh>  // KUidMsvMtmQuerySupportLinks
+#include <apgcli.h>
+#include <mtmuids.h>
+#include <mtmdef.h>
+
+#include <CSendingServiceInfo.h>
+#include <CMessageAddress.h>
+#include <SendUiConsts.h>               // UIDs for provider and service
+#include <CMessageData.h>               // CMessageData
+#include <MeetingRequestData.h>         // CMeetingRequestData
+#include <senduisingleton.h>
+
+#include "SendUiDummyMtm.h"
+#include "SendUiOperationWait.h"
+#include "SenduiDataUtils.h"
+#include "SenduiFileRightsEngine.h"
+#include "CSendUiAttachment.h"
+#include <MeetingRequestIds.hrh>
+#include "MeetingRequestSender.h"
+
+namespace
+    {
+    _LIT8( KContentType, "multipart" );
+    _LIT8( KContentSubType,"/alternative; boundary=mr_part; method=");
+    _LIT( KMsgInvalidSessionEvent, "CMeetingRequestSender: Invalid session event %x" );
+    _LIT( KMsgEmptyAddreesseeArray,"CMeetingRequestSender:: Addressee array is empty! Message cannot be created." );
+    _LIT( KMailtoMatchPattern, "mailto:*" ); // this is never localized
+    const TInt KMailtoLength = 7; // "mailto:" length
+    
+    //  LOCAL CONSTANTS 
+    // For address information separation (start)
+    const TUint KSendUiMsgAddressStartChar         ('<');
+
+    // For address information separation (end)
+    const TUint KSendUiMsgAddressEndChar           ('>');
+
+    const TInt KRichTextStoreGranularity = 512;
+    const TInt KContentSubTypeMaxLength( 60 );
+    
+    const TUint KTransferBufAdditionalInfoMaxLen = 20;
+    }
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CMeetingRequestSender* CMeetingRequestSender::NewL( TSendingServiceParams* aParams )
+    {
+    CMeetingRequestSender* self = CMeetingRequestSender::NewLC( aParams );
+    CleanupStack::Pop();
+
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::NewLC
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+CMeetingRequestSender* CMeetingRequestSender::NewLC( TSendingServiceParams* aParams )
+    {
+    CMeetingRequestSender* self = new( ELeave ) CMeetingRequestSender( aParams->iCoeEnv, aParams->iSingleton );
+    CleanupStack::PushL( self );
+
+    self->ConstructL();
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::~CMeetingRequestSender()
+// Destructor.
+// -----------------------------------------------------------------------------
+//
+CMeetingRequestSender::~CMeetingRequestSender()
+    {
+    iServiceArray.ResetAndDestroy();
+    iServiceArray.Close();
+
+    delete iStore;
+
+    delete iClientMtm;
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::CMeetingRequestSender
+// Constructor.
+// -----------------------------------------------------------------------------
+//
+
+CMeetingRequestSender::CMeetingRequestSender( CCoeEnv& aCoeEnv, CSendUiSingleton& aSingleton ) : 
+    CSendingService( aCoeEnv, aSingleton )
+    {
+    }
+    
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::ConstructL()
+    {
+    InitializeServiceL();   
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::PopulateServicesListL
+// Populates given list with the services provided by this plugin.
+// The ownership of the pointed objects remains.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::PopulateServicesListL(
+    RPointerArray<CSendingServiceInfo>& aList )
+    {
+    for ( TInt cc = 0; cc < iServiceArray.Count(); cc++ )
+        {
+        User::LeaveIfError( aList.Append( iServiceArray[cc] ) );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::CreateAndSendMessageL
+// Creates message to be sent and calls MTM to send the message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::CreateAndSendMessageL(
+    TUid aServiceUid,
+    const CMessageData* aMessageData,
+    TBool /*aLaunchEditorEmbedded*/)
+    {
+
+    if( !aMessageData ||
+        aServiceUid != KMeetingRequestSender ||
+        aMessageData->DataType() != KSenduiMsgDataMeetingRequest ) 
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+    CMeetingRequestData* mrData = CMeetingRequestData::NewLC();
+    mrData->ReadFromBufferL( aMessageData->OpaqueData() );
+
+    InitResourcesL( mrData );
+
+    //In case of SMTP we just create message and don't check capabilities,
+    //because SMTP MTM doesn't understand capabilities at the moment.
+    if( iClientMtm->Type() == KSenduiMtmSmtpUid )
+        {
+        DoCreateAndSendMessageL( *aMessageData, mrData );
+        }
+    else
+        {
+        TUid supportMeetingRequestUid =  TUid::Uid(KMtmUiSupportMeetingRequest);
+        TUid supportICalCapability =  TUid::Uid(KMtmUiSupportICalCapability);
+        TInt capabilityResponse;
+
+        TInt errorCode = iClientMtm->QueryCapability(supportMeetingRequestUid, capabilityResponse);
+                
+        //Check if Meeting request is supported at all. If not then leave.
+        if(errorCode == KErrNotSupported || capabilityResponse == 0)
+            {
+            User::Leave( KErrNotSupported );
+            }
+
+        errorCode = iClientMtm->QueryCapability(supportICalCapability, capabilityResponse);
+        
+        //Check if MTM supports supportICalCapability. 
+        if ( (errorCode != KErrNotSupported) && (capabilityResponse != 0))
+            {
+            DoCreateAndSendMessageL( *aMessageData, mrData );
+            }
+        else
+            {
+            TransferIdsToClientL( *aMessageData );
+            
+            DoCreateAndSendMessageL( *aMessageData, mrData );
+            }
+        }
+    CleanupStack::PopAndDestroy( mrData );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::DoCreateAndSendMessageL
+// Creates message to be sent and calls MTM to send the message.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::DoCreateAndSendMessageL(
+    const CMessageData& aMessageData,
+    CMeetingRequestData* aMeetingRequestData )
+    {
+    CreateMessageL( aMessageData );
+    
+    TInt capabilityResponse;
+    //Capability to support some kind of body checked 
+    TInt errorCode = iClientMtm->QueryCapability( 
+        TUid::Uid( KUidMtmQuerySupportedBodyValue ), 
+        capabilityResponse );
+    
+    if(capabilityResponse)
+        {
+        SetBodyL( aMessageData );    
+        }
+
+    AddAttachmentsL( aMessageData );
+
+    // Create message header(s)
+    CreateHeaderL( aMessageData, aMeetingRequestData );
+
+    iClientMtm->SaveMessageL();
+        
+    //QueryCapability() can't be used here because client gives false answer.
+    //So only way to do this is check MTM type.
+    if(iClientMtm->Type() == KSenduiMtmSmtpUid)
+        {
+        if( !ValidateEmail( ) )
+            {
+            User::Leave( KErrCompletion );
+            }
+        }
+        
+    SendEmailL( );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::TransferIdsToClientL
+// Transfers meeting request IDs to client MTM.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::TransferIdsToClientL(
+    const CMessageData& aMessageData )
+    {
+    // Create copy of opaque data. Needed because InvokeSyncFunctionL wants 
+    // non-const buffer.
+    HBufC8* tempBuf = aMessageData.OpaqueData().AllocLC();
+    TPtr8 ptr = tempBuf->Des();
+
+    // Dummy entry selection
+    CMsvEntrySelection* entrySelection = new ( ELeave ) CMsvEntrySelection;
+    CleanupStack::PushL( entrySelection );
+    
+    //Send data to MTM with InvokeSyncFunction()
+    iClientMtm->InvokeSyncFunctionL(
+            KMTMSendMeetingRequestFunctionId,
+            *entrySelection,
+            ptr );
+                        
+   CleanupStack::PopAndDestroy( 2, tempBuf ); // entrySelection, buf
+    
+   }
+
+// ---------------------------------------------------------
+// CMeetingRequestSender::ServiceProviderId
+// Returns the id of the service provider.
+// (other items were commented in a header).
+// ---------------------------------------------------------
+//
+TUid CMeetingRequestSender::ServiceProviderId() const
+    {
+    return KMeetingRequestSender; // defined in SendUiConsts.h
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::InitializeServiceL
+// Initializes service info.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::InitializeServiceL()
+    {
+    if( iServiceArray.Count() )
+        {
+        // Already initialized so do nothing
+        return;
+        }
+
+    CSendingServiceInfo* serviceInfo = CSendingServiceInfo::NewLC();
+
+    // Set service type.
+    serviceInfo->SetServiceProviderId( KMeetingRequestSender );
+    serviceInfo->SetServiceId( KMeetingRequestSender );
+    iServiceArray.AppendL( serviceInfo );
+
+    CleanupStack::Pop( serviceInfo );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::CreateMessageL
+// Creates new message with subject. Index entry is initialized and 
+// new message is created in drafs folder. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::CreateMessageL(
+    const CMessageData& aMessageData )
+    {
+    CMsvEntry* draftEntry = iSingleton.MsvSessionL().GetEntryL( KMsvDraftEntryId );
+    CleanupStack::PushL(draftEntry);
+    iClientMtm->SetCurrentEntryL( draftEntry ); // mtm takes ownership
+    CleanupStack::Pop(draftEntry);
+
+    // Create new message under drafts
+    iClientMtm->CreateMessageL( iMailServiceId );
+
+    // Load email signature, if any
+    iClientMtm->LoadMessageL();
+
+    // Set message subject
+    iClientMtm->SetSubjectL( aMessageData.Subject() );
+
+    // Set attributes on index entry
+    TMsvEntry indexEntry = iClientMtm->Entry().Entry();
+    indexEntry.SetInPreparation( ETrue );
+    indexEntry.SetVisible( EFalse );
+    indexEntry.iDate.UniversalTime();
+    indexEntry.SetAttachment( EFalse );
+
+    iClientMtm->Entry().ChangeL( indexEntry );
+    
+    
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::InitResourcesL
+// Client MTM is initialized. Used SMTP service is validated. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::InitResourcesL(
+    CMeetingRequestData* aMeetingRequestData )
+    {
+    iMailServiceId = aMeetingRequestData->Mailbox();
+
+    if ( iMailServiceId == KMsvNullIndexEntryId )
+        {
+        User::Leave( KErrArgument );
+        }
+
+    ResolveClientMTMTypeL( iMailServiceId );
+
+    if ( iClientMtmType == KNullUid )
+        {
+        User::Leave( KErrArgument );
+        }
+
+    // Create client side MTM object.
+    delete iClientMtm;
+    iClientMtm = NULL;
+
+    iClientMtm = iSingleton.ClientMtmRegistryL().NewMtmL( iClientMtmType );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::ResolveClientMTMTypeL
+// Resolves the client MTM type by using service id. KNullUid is returned if
+// service id doesn't match to any available service.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::ResolveClientMTMTypeL( TMsvId aServiceId )
+    {
+    TInt i = 0;
+
+    CMsvEntrySelection* entrySelection = 
+        MsvUiServiceUtilities::GetListOfAccountsL( iSingleton.MsvSessionL(), ETrue );
+    CleanupStack::PushL( entrySelection );
+
+    CMsvEntry* rootEntry = CMsvEntry::NewL(
+        iSingleton.MsvSessionL(),
+        KMsvRootIndexEntryId,
+        TMsvSelectionOrdering( KMsvNoGrouping, EMsvSortByNone, ETrue ) );
+    CleanupStack::PushL( rootEntry );    
+
+    TMsvId msvId;
+    for ( i = 0; i < entrySelection->Count(); i++ )
+        {
+        msvId = entrySelection->At( i );
+        
+        if ( msvId == aServiceId )
+            {
+            TMsvEntry tEntry = rootEntry->ChildDataL( msvId );
+            iClientMtmType = tEntry.iMtm;
+            break;    
+            }
+        }
+
+    CleanupStack::PopAndDestroy( 2, entrySelection ); // entrySelection, rootEntry
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::SetBodyL
+// Sets email body. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::SetBodyL(
+    const CMessageData& aMessageData )
+    {
+    // If body has been not set return immediately
+    if( !aMessageData.BodyText()->DocumentLength() )
+        {
+        return;
+        }
+
+    const CRichText& bodyText = *( aMessageData.BodyText() );
+    CRichText& mtmBody = iClientMtm->Body();
+    
+    delete iStore;
+    iStore = NULL;
+    
+    iStore = CBufStore::NewL( KRichTextStoreGranularity );
+
+    // If signature has been set then append it at the end of email
+    if ( mtmBody.DocumentLength() )
+        {
+        CRichText* text = CRichText::NewL(
+            static_cast<CEikonEnv&>( iCoeEnv ).SystemParaFormatLayerL(),
+            static_cast<CEikonEnv&>( iCoeEnv ).SystemCharFormatLayerL() );
+        CleanupStack::PushL( text );
+
+        TInt len1 = bodyText.DocumentLength();
+        TInt len2 = mtmBody.DocumentLength();
+
+        HBufC* buf = HBufC::NewLC( len1 );
+        TPtr ptr = buf->Des();
+        bodyText.Extract( ptr, 0 );
+        text->InsertL( 0, *buf );
+        CleanupStack::PopAndDestroy( buf );
+
+        len1 = text->DocumentLength();
+        text->InsertL( len1, CEditableText::EParagraphDelimiter );
+        len1 = text->DocumentLength();
+        text->InsertL( len1, mtmBody.Read( 0, len2 ) );
+
+        // Save and restore the rich text
+        TStreamId id = text->StoreL( *iStore );
+        mtmBody.RestoreL( *iStore, id );
+
+        CleanupStack::PopAndDestroy(text);
+        }
+    else
+        {
+        // Save and restore the rich text
+        TStreamId id = bodyText.StoreL( *iStore );
+        mtmBody.RestoreL( *iStore, id );
+        }
+
+    // Get access to protected data
+    CSendUiDummyMtm* mtm = ( CSendUiDummyMtm* ) iClientMtm;
+
+    mtm->SetCharFormatL( *bodyText.GlobalCharFormatLayer() );
+    mtm->SetParaFormatL( *bodyText.GlobalParaFormatLayer() );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::CreateHeaderL
+// Create email headers. "Standard" and MIME header is initialized.  
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::CreateHeaderL(
+    const CMessageData& aMessageData,
+    CMeetingRequestData* aMeetingRequestData )
+    {
+    InitStdHeaderL( aMessageData );
+    
+    //At the moment only way to check mime capability is to use iClientMtm->Type()
+    if(iClientMtm->Type() == KSenduiMtmSmtpUid)
+        {
+        InitMimeHeaderL( aMeetingRequestData );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::InitStdHeaderL
+// Initialize standard header. To, cc and bcc fields are set. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::InitStdHeaderL(
+    const CMessageData& aMessageData )
+    {
+    // Nothing to do if aTo array is empty
+    if ( aMessageData.ToAddressArray().Count() == 0 ) 
+        {
+        RDebug::Print( KMsgEmptyAddreesseeArray );
+        User::Leave( KErrArgument );
+        }
+
+    AddAddresseesL( EMsvRecipientTo, aMessageData.ToAddressArray() );
+
+    AddAddresseesL( EMsvRecipientCc, aMessageData.CcAddressArray() );    
+
+    AddAddresseesL( EMsvRecipientBcc, aMessageData.BccAddressArray() );
+    }
+    
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::AddAddresseesL
+// Add To addressees.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//    
+void CMeetingRequestSender::AddAddresseesL(
+    TMsvRecipientType               aRecipientType,
+    const CMessageAddressArray&     aAddressees )
+    {
+    TInt i(0);
+
+    for( i = 0; i < aAddressees.Count(); i++ )
+        {
+        if( aAddressees[i]->Address().Length() > 0 )
+            {
+            // If address has an alias, process it            
+            if( aAddressees[i]->Alias().Length() > 0 )
+                {
+                TPtrC alias = aAddressees[i]->Alias();
+
+                // Check if alias contains illegal characters. If it does
+                // remove them. 
+                if( ( alias.Locate( KSendUiMsgAddressStartChar ) != KErrNotFound  ) ||
+                    ( alias.Locate( KSendUiMsgAddressEndChar ) != KErrNotFound ) )
+                    {
+                    HBufC* aliasNew = alias.AllocLC();
+                    RemoveIllegalChars( aliasNew );
+                    iClientMtm->AddAddresseeL(
+                        aRecipientType, 
+                        AttendeeAdressWithoutPrefix( aAddressees[i]->Address() ), 
+                        *aliasNew );
+
+                    CleanupStack::PopAndDestroy( aliasNew );
+                    }
+                else
+                    {
+                    iClientMtm->AddAddresseeL(
+                        aRecipientType, 
+                        AttendeeAdressWithoutPrefix(aAddressees[i]->Address() ), 
+                        alias );
+                    }
+                }
+            else 
+                {
+                // Address entry didn't have alias
+                iClientMtm->AddAddresseeL( aRecipientType, 
+                    AttendeeAdressWithoutPrefix( aAddressees[i]->Address() ));
+                }
+            }
+        // Done setting one addressee
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::RemoveIllegalChars
+// Remove illegal chars from string. Illegal chars are replaced with space
+// and trimmed string is returned.
+// Should everything be removed between KSendUiMsgAddressStartChar and
+// KSendUiMsgAddressEndChar?
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//    
+void CMeetingRequestSender::RemoveIllegalChars( HBufC* aCheckedString )
+    {
+    TInt stringLength = aCheckedString->Length();
+    TPtr string = aCheckedString->Des();
+
+    while( stringLength-- )
+        {
+        if( string[stringLength] == KSendUiMsgAddressStartChar ||
+            string[stringLength] == KSendUiMsgAddressEndChar )
+            {
+            string[stringLength] = CEditableText::ESpace;
+            }
+        }
+    string.TrimAll();
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::InitMimeHeaderL
+// Mime message header is initialized with user defined content type.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//    
+void CMeetingRequestSender::InitMimeHeaderL(
+    CMeetingRequestData* aMeetingRequestData )
+    {
+    CMsvEntry& entry = iClientMtm->Entry();
+    CMsvStore* msvStore = entry.EditStoreL();
+    CleanupStack::PushL( msvStore );
+
+    CImMimeHeader* header = CImMimeHeader::NewLC();
+
+    // Set content type
+    header->SetContentTypeL( KContentType ); 
+    
+    TBuf8<KContentSubTypeMaxLength> subtype( KContentSubType() );
+    subtype.Append( aMeetingRequestData->MailHeaderMethod() );
+    header->SetContentSubTypeL( subtype ); 
+
+    header->StoreL( *msvStore );
+    msvStore->CommitL();
+
+    CleanupStack::PopAndDestroy( 2, msvStore );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::AddAttachmentsL
+// Attachments are added to the email. Method will leave if there is not 
+// enough space for attachments.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//    
+void CMeetingRequestSender::AddAttachmentsL(
+    const CMessageData& aMessageData )
+    {
+    TInt i(0);
+    
+
+    CArrayPtrFlat<CSendUiAttachment>* attachments;
+    attachments = CSendUiAttachment::InitAttachmentArrayLCC( 
+        aMessageData.AttachmentArray(), 
+        aMessageData.AttachmentHandleArray(),
+        iCoeEnv.FsSession() );
+    
+    
+    // If there are no attachments, return immediately. 
+    if( !attachments->Count() )
+        {
+        return;
+        }
+
+    // Get total size of attachments.
+    CSendUiFileRightsEngine* fileRightsEngine =
+        CSendUiFileRightsEngine::NewLC( iCoeEnv.FsSession() );
+
+    // Check if linked attachments are supported. 
+    TInt supportsLinks = 0;
+    TUid uidQuery = { KUidMsvMtmQuerySupportLinks };
+    iClientMtm->QueryCapability( uidQuery, supportsLinks );
+  
+    TInt totalSize = fileRightsEngine->CalculateTotalSizeOfFiles( attachments, supportsLinks );
+
+    CleanupStack::PopAndDestroy( fileRightsEngine );
+    // following will leave if there is not enough space
+    CheckDiskSpaceL( totalSize );
+  
+    const RArray<RFile>& attHandles = aMessageData.AttachmentHandleArray();
+    const CDesCArray& attFilePaths = aMessageData.AttachmentArray();
+
+
+    for( i = 0; i < attachments->Count(); i++ )
+        {
+        AddAttachmentL( attachments->At(i), supportsLinks );
+        }
+
+    CleanupStack::PopAndDestroy(2); // *attachments, attacments
+    TMsvEntry entry = iClientMtm->Entry().Entry();
+    entry.SetAttachment( ETrue );
+    iClientMtm->Entry().ChangeL(entry);
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::AddAttachmentL
+// Add attachment (file descriptor)
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+void CMeetingRequestSender::AddAttachmentL(
+    CSendUiAttachment* aAttachment, 
+    TInt aLinksSupported )
+    {
+    TDataType mimeType;
+    
+    CSendUiDataUtils* dataUtils = CSendUiDataUtils::NewL( iCoeEnv.FsSession() );
+    CleanupStack::PushL( dataUtils );
+    CSendUiOperationWait* waiter = CSendUiOperationWait::NewLC();
+
+    if ( aAttachment->Type() == CSendUiAttachment::EAttachmentPath )
+    	{
+        dataUtils->ResolveFileMimeTypeL( *(aAttachment->Path()), mimeType );
+	  	
+	  	if ( aLinksSupported )
+	  		{
+	  		iClientMtm->AddLinkedAttachmentL(
+                *(aAttachment->Path()), mimeType.Des8(), 0, waiter->iStatus );
+        	}
+        	else
+        		{
+        	    iClientMtm->AddAttachmentL(
+                    *(aAttachment->Path()), mimeType.Des8(), 0, waiter->iStatus );
+        		}
+	  	}
+	  else
+  		{
+        dataUtils->ResolveFileMimeTypeL( *(aAttachment->Handle()), mimeType );
+	  	RFile attachmentFile;
+	    User::LeaveIfError( attachmentFile.Duplicate( *(aAttachment->Handle()) ) );
+	    CleanupClosePushL( attachmentFile );
+	    
+	    // File handle ownership transferred.
+	    iClientMtm->AddAttachmentL(
+	        attachmentFile,
+	        mimeType.Des8(),
+	        NULL,
+	        waiter->iStatus );
+        CleanupStack::Pop( &attachmentFile );
+
+  		}
+    
+
+    waiter->Start( iClientMtm );
+
+    CleanupStack::PopAndDestroy( 2, dataUtils ); // waiter, dataUtils
+    }
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::CheckDiskSpaceL
+// Check if there are enough space for email (includes attachments). Leaves 
+// if there are not enough space. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//            
+void CMeetingRequestSender::CheckDiskSpaceL(
+    TInt aMsgSize )
+    {
+    if( MsvUiServiceUtilities::DiskSpaceBelowCriticalLevelL(
+            iSingleton.MsvSessionL(), aMsgSize ) )
+        {
+        User::Leave( KErrDiskFull );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::ValidateEmail
+// Validates email before sending it. Returns true if email is valid. 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//            
+TBool CMeetingRequestSender::ValidateEmail( )
+    {
+    TMsvPartList msgCheckParts =
+        KMsvMessagePartBody |
+        KMsvMessagePartRecipient |
+        KMsvMessagePartOriginator |
+        KMsvMessagePartDate |
+        KMsvMessagePartDescription;
+
+    TMsvPartList msgFailParts =
+        iClientMtm->ValidateMessage( msgCheckParts );
+
+    return ( msgFailParts == KMsvMessagePartNone );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::SendEmailL
+// Sends an email.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//            
+void CMeetingRequestSender::SendEmailL( )
+    {
+    TMsvEntry entry = iClientMtm->Entry().Entry();
+
+    entry.SetInPreparation( EFalse );
+    entry.SetVisible( ETrue );
+    entry.SetSendingState( KMsvSendStateWaiting );
+    
+    iClientMtm->Entry().ChangeL( entry );
+
+    iClientMtm->LoadMessageL();
+    entry = iClientMtm->Entry().Entry();
+    TMsvId id = entry.Id();
+
+    if( entry.Parent() != KMsvGlobalOutBoxIndexEntryId )
+        {
+        // move message to outbox
+        MoveMessageEntryL( KMsvGlobalOutBoxIndexEntryId );
+        }
+
+    CMsvEntrySelection*  entrySelection;
+    entrySelection = new ( ELeave ) CMsvEntrySelection;
+    CleanupStack::PushL( entrySelection );
+
+    entrySelection->AppendL( iMailServiceId );
+    entrySelection->AppendL( id );
+    
+    TBuf8<1> params;
+
+    CSendUiOperationWait* waiter = CSendUiOperationWait::NewLC();
+
+    CMsvOperation* op = 
+        iClientMtm->InvokeAsyncFunctionL(
+            KMTMStandardFunctionsSendMessage,
+            *entrySelection,
+            params,
+            waiter->iStatus );
+
+    waiter->Start( op );
+
+    CleanupStack::PopAndDestroy( 2, entrySelection ); // entrySelectoin, waiter
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::MoveMessageEntryL
+// Moves email to target folder (usually outbox). 
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//            
+void CMeetingRequestSender::MoveMessageEntryL( TMsvId aTarget )
+    {
+    TMsvSelectionOrdering sort;
+    // we want to also handle the invisible entries
+    sort.SetShowInvisibleEntries( ETrue );
+
+    // Take a handle to the parent entry
+    TMsvEntry msvEntry( iClientMtm->Entry().Entry() );
+    CMsvEntry* parentEntry = 
+        CMsvEntry::NewL(
+            iClientMtm->Session(),
+            msvEntry.Parent(),
+            sort );
+    CleanupStack::PushL( parentEntry );
+
+    // Move original from the parent to the new location
+    parentEntry->MoveL( msvEntry.Id(), aTarget );
+
+    CleanupStack::PopAndDestroy( parentEntry );
+    }
+
+// -----------------------------------------------------------------------------
+// CMeetingRequestSender::AttendeeAdressWithoutPrefix
+// Returns a new descriptor where "MAILTO:" prefix is removed if it existed.
+// (other items were commented in a header).
+// -----------------------------------------------------------------------------
+//
+TPtrC CMeetingRequestSender::AttendeeAdressWithoutPrefix( const TDesC& aAddress )
+    {
+    TPtrC attendeeAddrWithoutPrefix;
+    if ( aAddress.MatchF( KMailtoMatchPattern ) != KErrNotFound )
+        {
+        attendeeAddrWithoutPrefix.Set( aAddress.Mid( KMailtoLength ) );
+        }
+    else
+        {
+        attendeeAddrWithoutPrefix.Set( aAddress );
+        }
+    return attendeeAddrWithoutPrefix;
+    }
+
+// -----------------------------------------------------------------------------
+// TechnologyType
+// -----------------------------------------------------------------------------
+//
+TUid CMeetingRequestSender::TechnologyTypeId( ) const
+    {
+    return KNullUid;
+    }
+    
+//  END OF FILE