mmsengine/mmspushhandler/src/CMmsPushHandler.cpp
changeset 31 ebfee66fde93
child 47 5b14749788d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmspushhandler/src/CMmsPushHandler.cpp	Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,869 @@
+/*
+* Copyright (c) 2002-2007 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:   ECOM plugin .dll for handling MMS related WAP push messages.
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <e32std.h>
+#include <msvapi.h>
+#include <msvids.h>
+#include <msvuids.h>
+#include <e32math.h>
+#include <apparc.h>
+
+#include <push/cpushhandlerbase.h>
+#include <ecom/implementationproxy.h>
+#include <push/pluginkiller.h>
+#include <push/pushmessage.h>
+#include <push/pushlog.h>
+
+#include "mmsconst.h"
+#include "mmscmds.h"
+#include "CMmsPushHandler.h"
+#include "mmsgenutils.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES  
+
+// CONSTANTS
+const TUint KMinimumNumberOfMessageFields = 6;
+// ECOM framework related
+const TImplementationProxy ImplementationTable[] = 
+    {
+    // different definition for new compiler
+    IMPLEMENTATION_PROXY_ENTRY(0x101F4692, CMmsPushHandler::NewL)
+    };
+
+// MACROS
+// LOCAL CONSTANTS AND MACROS
+// MODULE DATA STRUCTURES
+// LOCAL FUNCTION PROTOTYPES
+// FORWARD DECLARATIONS
+// ============================= LOCAL FUNCTIONS ===============================
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::New
+// 1st phase constructor
+// Returns: <new CMmsPushHandler>:
+// -----------------------------------------------------------------------------
+//
+CMmsPushHandler* CMmsPushHandler::NewL()
+    {
+    CMmsPushHandler* self = new( ELeave ) CMmsPushHandler();
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    LOGTEXT(_L("***** CMmsPushHandler NewL() *****"));
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CMmsPushHandler
+// Initialises the object with zero/NULL values.
+// -----------------------------------------------------------------------------
+//
+CMmsPushHandler::CMmsPushHandler()
+    :CPushHandlerBase(),
+    iState( EInitial ),
+    iServiceId( KMsvNullIndexEntryId ),
+    iMsvSession( NULL ),
+    iMsvOperation( NULL ),
+    iPushMsg( NULL ),
+    iBody( NULL ),
+    iRetryCount( 0 )
+    {
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::ConstructL
+//
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::ConstructL()
+    {
+    // Connect to filesystem
+    User::LeaveIfError( iFs.Connect() );
+    User::LeaveIfError( iTimer.CreateLocal() );
+
+    // Add plugin to AS
+    CActiveScheduler::Add( this );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::~CMmsPushHandler
+// Destructor. Calls also baseclass destructor which calls
+//  REcomSession::DestroyedImplementation(iDtor_ID_Key)
+// -----------------------------------------------------------------------------
+//
+CMmsPushHandler::~CMmsPushHandler()
+    {
+    LOGTEXT(_L("~CMmsPushHandler()"));
+
+    // AO's destructor always calls Cancel()
+    Cancel();
+
+    // Delete the necessary instance attributes
+    delete iMsvSession;
+    delete iMsvOperation;
+    delete iPushMsg;
+    delete iBody;
+    iFs.Close();
+    iTimer.Close();
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::HandleMessageL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::HandleMessageL(
+    CPushMessage* aPushMsg,
+    TRequestStatus& aStatus )
+    {
+    LOGTEXT(_L("Asynchronous HandleMessageL() called but not implemented."));
+
+    iPushMsg = aPushMsg;
+    SetConfirmationStatus( aStatus );
+    SignalConfirmationStatus( KErrNotSupported );
+    iPluginKiller->KillPushPlugin();
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::HandleMessageL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::HandleMessageL( CPushMessage* aPushMsg )
+    {
+    LOGTEXT( _L("Synchronous HandleMessageL() called.") );
+
+    iPushMsg = aPushMsg;
+
+    //
+    // Do sanity checks for the message.
+    //
+    TInt error = PerformChecks();
+    if ( error )
+        {
+        LOGTEXT2( _L("HandleMessageL(): PerformChecks failed with code %d, returning silently. Notification is lost."), error );
+        
+        iPluginKiller->KillPushPlugin();        
+        return;
+        }
+
+    //
+    // Store the messagebody into handler's attribute
+    // If memory runs out, tough luck..
+    //
+    TPtrC8 messageBodyPtr;
+    if ( iPushMsg->GetMessageBody( messageBodyPtr ) )
+        {
+        iBody = messageBodyPtr.Alloc();
+        if ( !iBody )
+            {
+        	LOGTEXT( _L("HandleMessageL(): Out of memory when allocating body buffer") );
+        	// Commit suicide - the caller expects it even if we leave
+        	iPluginKiller->KillPushPlugin();
+        	User::Leave( KErrNoMemory );
+            }
+        }
+    
+    //
+    // Setting 'this' active and complete in order to get to RunL
+    //
+    TRequestStatus* status = &iStatus;
+    iStatus = KRequestPending;
+    User::RequestComplete( status, KErrNone ); //iStatus = KErrNone
+    SetActive();
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CancelHandleMessage
+// Cancels the pending asynchronous HandleMessageL.
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::CancelHandleMessage()
+    {
+    LOGTEXT( _L("CancelHandleMessage() called, but not supported.") );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::HandleSessionEventL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::HandleSessionEventL(
+    TMsvSessionEvent aEvent, 
+    TAny* /*aArg1*/, 
+    TAny* /*aArg2*/, 
+    TAny* /*aArg3*/)
+    {
+    switch (aEvent)
+        {
+        // If session is closed down, the object will be deleted
+        case EMsvCloseSession:
+            LOGTEXT(_L("HandleSessionEventL(): Session event EMsvCloseSession"));
+            // fall through on purpose
+        case EMsvServerTerminated:
+            LOGTEXT(_L("HandleSessionEventL(): Session event EMsvServerTerminated"));
+            if ( iMsvSession )
+                {
+                delete iMsvSession;
+                iMsvSession = NULL;
+                }
+            break;
+        case EMsvMediaUnavailable:
+            LOGTEXT(_L("HandleSessionEventL(): EMsvMediaUnavailable"));
+            break;
+        default:
+            //Nothing
+            break;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::DoCancel
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::DoCancel()
+    {
+    LOGTEXT( _L("DoCancel()") );
+    if( iState == ETransferCommand && iMsvOperation )
+        {
+        iMsvOperation->Cancel();
+        }
+    else if( iState == EMsDriveChange && iMsvOperation )
+        {
+        iMsvOperation->Cancel();
+        }
+    else if( iState == ETimer )
+        {
+        iTimer.Cancel();
+        }
+    else if( iState == EDiskSpaceWait )
+        {
+        iFs.NotifyDiskSpaceCancel();
+        }
+
+    // Finally clean up
+    iPluginKiller->KillPushPlugin();
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::RunL
+// Loops forever if MessageServer/ServerMtm respond correctly
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::RunL()
+    {
+    LOGTEXT(_L("RunL(): Starting"));
+
+    // Check for cancel
+    if( iStatus == KErrCancel )
+        {
+        LOGTEXT( _L("RunL(): Operation cancelled.") );
+        iPluginKiller->KillPushPlugin();
+        return;
+        }
+
+    // Checking the retry count, because if something just keeps going wrong
+    // there is nothing that can be done
+    if( ++iRetryCount > KRetryCount )
+        {
+        LOGTEXT( _L("RunL(): Retrycount exceeded, quitting. Notification is lost.") );
+        iPluginKiller->KillPushPlugin();
+        return;
+        }
+
+    TInt error = KErrNone;
+
+    //
+    // Operation: TransferCommand
+    //
+    if( iState == ETransferCommand )
+        {
+        // Clean up
+        if( iMsvOperation )
+            {
+            delete iMsvOperation;
+            iMsvOperation = NULL;
+            }
+
+        // Go through statuses
+        if( iStatus == KErrNone )
+            {
+            LOGTEXT( _L("RunL(): TransferCommandL() succeeded.") );
+            iPluginKiller->KillPushPlugin();
+            return;
+            }
+        else if( iStatus == KErrLocked ) // for a short wait
+            {
+            LOGTEXT2( _L("RunL(): TransferCommandL failed with code %d, entering short wait."), iStatus.Int() );
+            iTimer.After( iStatus, KShortWait );
+            iState = ETimer;
+            SetActive();
+            return;
+            }
+        else // default is long wait
+            {
+            LOGTEXT2( _L("RunL(): TransferCommandL failed with code %d, entering long wait."), iStatus.Int() );
+            iTimer.After( iStatus, KLongWait );
+            iState = ETimer;
+            SetActive();
+            return;
+            }
+        }
+
+    //
+    // Open session if not open
+    // Session is needed to check drive
+    //
+    if ( !iMsvSession  )
+        {
+        TRAP( error, OpenSessionL() );
+        if( error )
+            {
+            LOGTEXT2( _L("RunL: OpenSessionL left with code %d, entering long wait."), error );
+            iState = ETimer;
+            iTimer.After( iStatus, KLongWait );
+            SetActive();
+            return;
+            }
+        }
+    //
+    // Operation: MessageStore Drive Change
+    //
+    if( iState == EMsDriveChange )
+        {
+        // Clean up
+        if( iMsvOperation )
+            {
+            delete iMsvOperation;
+            iMsvOperation = NULL;
+            }
+
+        // Drive should be now on 'C:'
+        // Asserting this:
+        if( iMsvSession->CurrentDriveL() != EDriveC )
+            {
+            // if not, giving up without retrying
+            LOGTEXT( _L("RunL(): Drive change attempt did not work, returning silently. Notification is lost." ) );
+            iPluginKiller->KillPushPlugin();
+            return;                        
+            }
+        LOGTEXT( _L("RunL(): Drive change attempt succeeded." ) );        
+        }
+
+    //
+    // Find MMS default serviceId if not yet found
+    //
+    if( iServiceId == 0 )
+        {
+        TRAP( error, FindServiceL() );
+        // If MessageStore is not available and push message is a notification,
+        // The message store must be moved to C:
+        // (delivery reports are handled on best effort basis)
+        if( ( error == KErrCorrupt ||         
+              error == KErrNotReady )          
+            && (*iBody)[1] == 0x82 )
+            { 
+            // Try changing MessageStore to C:
+            LOGTEXT2( _L("RunL: FindServiceL left with code %d, changing drive."), error );
+            TRAP( error, { iMsvOperation = iMsvSession->ChangeDriveL( EDriveC, iStatus ); } );
+            if( error == KErrNone )
+                {
+                iState = EMsDriveChange;
+                SetActive();
+                return;
+                }
+            else
+                {
+                LOGTEXT2( _L("RunL: ChangeDriveL left with code %d."), error );
+                // This falls to the next if statement (below) on purpose
+                }
+            }
+        if( error != KErrNone ) // error is something else
+            {
+            LOGTEXT2( _L("RunL(): FindServiceL left with code %d, returning silently. Notification is lost."), error );
+            iPluginKiller->KillPushPlugin();
+            return;            
+            }
+        if( iServiceId == 0 ) // if iService still is 0 (FindServiceL should have found this)
+            {
+            LOGTEXT( _L("RunL(): FindServiceL didn't find MMS serviceId, returning silently. Notification is lost." ) );        
+            iPluginKiller->KillPushPlugin();
+            return;            
+            }
+        }
+
+    //
+    // Checking disk space before using it
+    //
+    TRAP( error, { error = CheckDiskSpaceL(); } );
+    if( error == KErrDiskFull )
+        {
+        // CheckDiskSpaceL has handled the situation, i.e. we should active already
+        return;
+        }
+    else if( error != KErrNone ) // other error
+        {
+        LOGTEXT2( _L("RunL(): CheckDiskSpaceL left with code %d, entering short wait."), error );
+        iTimer.After( iStatus, KShortWait );
+        iState = ETimer;
+        SetActive();
+        return;
+        }
+
+    //
+    // Transfer the push message to MessageServer side
+    //
+    TRAP( error, TransferMessageL() );
+    if( error == KErrCorrupt     // MMC is not present
+        && (*iBody)[1] == 0x82 ) // push message is a notification
+        {
+        LOGTEXT( _L("RunL: TransferMessageL left with code %d, changing drive.") );
+        // Try changing MessageStore to C:
+        TRAP( error, { iMsvOperation = iMsvSession->ChangeDriveL( EDriveC, iStatus ); } );
+        if( error == KErrNone )
+            {
+            iState = EMsDriveChange;
+            SetActive();
+            return;
+            }
+        else
+            {
+            LOGTEXT2( _L("RunL: ChangeDriveL left with code %d."), error );
+            // This falls to the next if statement (below) on purpose
+            }
+        }
+    if( error != KErrNone ) // other error
+        {
+        LOGTEXT2( _L("RunL(): TransferMessageL left with code %d, entering short wait."), error );
+        iTimer.After( iStatus, KShortWait );
+        iState = ETimer;
+        SetActive();
+        return;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CPushHandlerBase_Reserved1
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::CPushHandlerBase_Reserved1()
+    {
+    User::Panic(
+        _L("CPushHandlerBase_Reserved1() not supported."), 
+        KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CPushHandlerBase_Reserved2
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::CPushHandlerBase_Reserved2()
+    {
+    User::Panic(
+        _L("CPushHandlerBase_Reserved2() not supported."),
+        KErrNotSupported );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::PerformChecksL
+// 
+// -----------------------------------------------------------------------------
+//
+TInt CMmsPushHandler::PerformChecks()
+    {
+    // Check that message is not equal to NULL
+    if( !iPushMsg )
+        {
+        LOGTEXT( _L("ERROR: Message == NULL.") );
+        return KErrCorrupt;
+        }
+
+    // Check that body is not equial to NULL
+    TPtrC8 messageBodyPtr;
+    TBool bodyPresent = EFalse;
+    bodyPresent = iPushMsg->GetMessageBody( messageBodyPtr );
+    if( !bodyPresent )
+        {
+        LOGTEXT( _L("ERROR: Message has NULL body.") );
+        return KErrCorrupt;
+        }
+
+    // KMinimumNumberOfMessageFields (6) is minimum number of message fields. 
+    // This is a very "mild" check but
+    // at least it guarantees that there is no empty body in the message
+    if( messageBodyPtr.Size() < KMinimumNumberOfMessageFields )
+        {
+        LOGTEXT( _L("ERROR: Message has too short body.") );
+        return KErrCorrupt;
+        }
+
+    // Check message-type
+    TPtrC contentTypePtr;
+    iPushMsg->GetContentType( contentTypePtr );
+    _LIT( KMmsContentType, "application/vnd.wap.mms-message" );
+    if( contentTypePtr.Compare( KMmsContentType ) != 0 )
+        {
+        LOGTEXT2( _L("ERROR: ContentType == %S"), &contentTypePtr );
+        return KErrCorrupt;
+        }
+
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::OpenSessionL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::OpenSessionL()
+    {
+    LOGTEXT( _L("OpenSessionL()") );
+    iMsvSession = CMsvSession::OpenSyncL( *this );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::FindServiceL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::FindServiceL()
+    {
+    LOGTEXT( _L("FindServiceL()") );
+    
+    // Use first service found. There should never be more than one MMS Service
+    
+    // Create a new entry, showing invisible entries (because the service entry may be invisible)
+    TMsvSelectionOrdering ordering(KMsvNoGrouping, EMsvSortByNone, ETrue);
+    CMsvEntry* entry = CMsvEntry::NewL(*iMsvSession, KMsvRootIndexEntryId, ordering);
+    CleanupStack::PushL(entry);
+	
+ 	CMsvEntrySelection *sel=entry->ChildrenWithMtmL(KUidMsgTypeMultimedia);
+	CleanupStack::PushL(sel);
+	if(sel->Count() == 0)
+	    {
+	    User::Leave(KErrNotFound);
+	    } 
+	iServiceId=sel->At(0);
+ 	CleanupStack::PopAndDestroy( sel );
+ 	CleanupStack::PopAndDestroy( entry );
+ 	return;
+    
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::TransferMessageL
+// 
+// -----------------------------------------------------------------------------
+//
+void CMmsPushHandler::TransferMessageL()
+    {
+    LOGTEXT( _L("TransferMessageL()") );
+
+    // In order to get to mms service fast (not wait for our turn)
+    // we create an entry into our invisible mms directory.
+    // This entry will contain the binary notification
+    // and the service says "local service" with "MMS mtm"
+
+    //
+    // Get entry id of the notifications folder
+    //
+    TMsvId mmsFolder = GetMMSFolderL();
+
+    // Create selection array
+    CMsvEntrySelection* selection = new( ELeave ) CMsvEntrySelection;
+    CleanupStack::PushL( selection );
+
+    TMsvId tMsvId = CreateEntryL( mmsFolder );
+    iMsvSession->CleanupEntryPushL( tMsvId );
+    LOGTEXT2( _L("TransferMessageL(): Entry Created: %d"), tMsvId );
+
+    //
+    // The binary notification has been streamed into this entry.
+    // Now we have an entry that says: local service, MMS MTM
+    //
+    if ( tMsvId != KMsvNullIndexEntryId )
+        {
+        LOGTEXT( _L("TransferMessageL(): iPushMessageId != KMsvNullIndexEntryId") );
+        selection->AppendL( tMsvId );
+        }
+    else
+        {
+        LOGTEXT( _L("TransferMessageL(): iPushMessageId == KMsvNullIndexEntryId") );
+        selection->AppendL( iServiceId );
+        }
+    LOGTEXT( _L("TransferMessageL(): iPushMessageId Appended to Selection.") );
+
+    // Watcher parameters have become obsolete as all data is transferred in the entry
+    TWatcherParameters parameters;
+    TWatcherParametersBuf paramPack( parameters );
+
+    //
+    // Make asynch call to transfer the push message
+    //
+    LOGTEXT( _L("TransferMessageL(): invoking asynchronous TransferCommandL()") );
+    iMsvOperation = iMsvSession->TransferCommandL(
+        *selection,
+        EMmsDecodePushedMessage, 
+        paramPack, 
+        iStatus );
+    iState = ETransferCommand;
+    SetActive();
+
+    iMsvSession->CleanupEntryPop(); // tMsvId
+    CleanupStack::PopAndDestroy( selection );
+    LOGTEXT( _L("TransferMessageL() exiting") );
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CheckDiskSpaceL
+//
+// -----------------------------------------------------------------------------
+//
+TInt CMmsPushHandler::CheckDiskSpaceL()
+    {
+    LOGTEXT( _L("CheckDiskSpaceL()") );
+    TInt messageDrive = EDriveC; // C is default
+    messageDrive = iMsvSession->CurrentDriveL();
+    LOGTEXT2( _L("CheckDiskSpaceL(): Current Drive = %d"), messageDrive );
+    if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
+        &iFs, 
+        KRequiredDiskSpace, 
+        messageDrive ) )
+        {
+        LOGTEXT( _L("CheckDiskSpaceL(): Not Enough Diskspace, subscribing a notification from FileServer.") );
+        TVolumeInfo volumeInfo;
+        User::LeaveIfError( iFs.Volume( volumeInfo, messageDrive ) );
+        iFs.NotifyDiskSpace( 
+            ( volumeInfo.iFree + KRequiredDiskSpace ), 
+            messageDrive, 
+            iStatus );
+        iState = EDiskSpaceWait;
+        SetActive();
+        return KErrDiskFull;
+        }
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::GetMMSFolderL
+// Creates an MMS folder into MessageServer's message store.
+// (As a subfolder of LocalService.)
+// Returns: <new TMsvId>: Entry Id of the created MMS folder.
+// -----------------------------------------------------------------------------
+//
+TMsvId CMmsPushHandler::GetMMSFolderL()
+    {
+    LOGTEXT( _L("GetMMSFolderL()") );
+
+    // First check if proper MMS folder already exists
+    TMsvId mmsFolderId = FindMMSFolderL();
+
+    // If below is true, the folder did not exist already and has to be created.
+    // This should not happen unless the message store is corrupted
+    if ( mmsFolderId == KMsvNullIndexEntryId )
+        {
+        mmsFolderId = CreateMMSFolderL();
+        }
+
+    return mmsFolderId;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CreateMMSFolderL
+// Creates MMSNotifications folder into Message Store.
+// (As a subfolder of LocalService.)
+// Returns: <new TMsvId>: Entry Id of the created MMS folder.
+// -----------------------------------------------------------------------------
+//
+TMsvId CMmsPushHandler::CreateMMSFolderL()
+    {
+    LOGTEXT( _L("CreateMMSFolderL()") );
+    //
+    // Create entry
+    //
+    TMsvEntry entry;
+    entry.iType = KUidMsvFolderEntry;
+    entry.iMtm = KUidMsvLocalServiceMtm;
+    entry.iDetails.Set( KMMSNotificationFolder );
+    entry.SetVisible( EFalse );
+    entry.SetInPreparation( EFalse );
+    entry.iServiceId = KMsvLocalServiceIndexEntryId;
+
+    //
+    // Make the created entry to be a subfolder of LocalService
+    //
+    CMsvEntry* cMsvEntry 
+        = iMsvSession->GetEntryL( KMsvLocalServiceIndexEntryId );
+    CleanupStack::PushL( cMsvEntry );
+    cMsvEntry->CreateL( entry );
+    CleanupStack::PopAndDestroy( cMsvEntry );
+
+    return entry.Id();
+    }
+
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::FindMMSFolderL
+// Checks if MMS folder entry already exists.
+// Returns: KMMSNotificationFolder: Entry Id of the found MMS folder
+//          KMsvNullIndexEntryId:   MMS folder was not found
+// -----------------------------------------------------------------------------
+//
+TMsvId CMmsPushHandler::FindMMSFolderL()
+    {
+    LOGTEXT( _L("FindMMSFolderL()") );
+
+    TMsvId mmsFolderId = KMsvNullIndexEntryId;
+
+    // Get Local Service as parent entry
+    CMsvEntry* cMsvEntry = iMsvSession->GetEntryL( KMsvLocalServiceIndexEntryId );
+    CleanupStack::PushL( cMsvEntry );
+    // show invisible entries
+    cMsvEntry->SetSortTypeL( TMsvSelectionOrdering( KMsvNoGrouping, 
+                                                    EMsvSortByIdReverse, 
+                                                    ETrue ) );
+    // Get all folder entries.                                                    
+    CMsvEntrySelection* selection 
+        = cMsvEntry->ChildrenWithTypeL( KUidMsvFolderEntry );
+    CleanupStack::PushL( selection );
+
+    // Now we should have a list of all local folders.
+    // Prune away the standard folders.
+    // They should be at the end of the list
+    TInt count = selection->Count();
+    TInt i;
+    for ( i = count - 1; i >= 0; --i )
+        {
+        if ( selection->At( i ) <= KMsvDeletedEntryFolderEntryId )
+            {
+            // Anything below this should not be ours
+            selection->Delete( i );
+            }
+        }
+
+    // Check if anything left.
+    count = selection->Count();
+    // Loop through the rest and find possible correct folder
+    for ( i = 0; i < count && mmsFolderId == KMsvNullIndexEntryId; ++i )
+        {
+        cMsvEntry->SetEntryL( selection->At( i ) );
+        // must be exact match
+        if (cMsvEntry->Entry().iDetails.Compare( KMMSNotificationFolder ) == 0)
+            {
+            mmsFolderId = selection->At( i );
+            }
+        }
+    CleanupStack::PopAndDestroy( selection );
+    CleanupStack::PopAndDestroy( cMsvEntry );
+
+    // If folder does not exist -> returns KMsvNullIndexEntryId
+    // If folder exists -> returns its Id
+    return mmsFolderId;
+    }
+
+// -----------------------------------------------------------------------------
+// CMmsPushHandler::CreateEntryL
+// 
+// -----------------------------------------------------------------------------
+//
+TMsvId CMmsPushHandler::CreateEntryL( TMsvId aFolder )
+    {
+    LOGTEXT( _L("CreateEntryL()") );
+    //
+    // Basic "NULL test"
+    //
+    if( aFolder == KMsvNullIndexEntryId )
+        {
+        // No folder, no entry
+        return KMsvNullIndexEntryId;
+        }
+
+    //
+    // Get CMsvEntry context of the target folder
+    //
+    CMsvEntry* cMsvEntry = iMsvSession->GetEntryL( aFolder );
+    CleanupStack::PushL( cMsvEntry ); // ***
+
+    //
+    // Create an entry representing the notification
+    //
+    TMsvEntry tMsvEntry;
+    tMsvEntry.iType = KUidMsvMessageEntry;
+    tMsvEntry.iMtm = KUidMsgTypeMultimedia;
+    tMsvEntry.iServiceId = KMsvLocalServiceIndexEntryId;
+    tMsvEntry.iRelatedId = iServiceId;
+    tMsvEntry.iMtmData2 = KMmsNotificationBinary;
+    // Visible and inPreparation flags will be changed after data has been
+    // successfully streamed
+    tMsvEntry.SetVisible( EFalse );
+    tMsvEntry.SetInPreparation( ETrue );
+    cMsvEntry->CreateL( tMsvEntry );
+
+    //
+    // Stream 
+    // 1) length of the data as 32 bit integer
+    // 2) pushed message data
+    // into created entry's stream  
+    //
+    cMsvEntry->SetEntryL( tMsvEntry.Id() );
+    CMsvStore* store = cMsvEntry->EditStoreL();
+    CleanupStack::PushL( store );   // ***
+    RMsvWriteStream outs;
+    outs.AssignLC( *store, KUidBinaryNotificationStream ); // ***
+    outs.WriteUint32L( iBody->Size() );
+    LOGTEXT2( _L(" - streamed %d bytes into dummy-entry's store"), iBody->Size() );
+    outs.WriteL( *iBody );
+    outs.CommitL();
+    outs.Close();
+    store->CommitL();
+    CleanupStack::PopAndDestroy( &outs );  //outs
+    CleanupStack::PopAndDestroy( store );
+    //
+    // Now, change the flags to their final values
+    //
+    tMsvEntry.SetVisible( ETrue );
+    tMsvEntry.SetInPreparation( EFalse );
+    cMsvEntry->ChangeL( tMsvEntry );
+    CleanupStack::PopAndDestroy( cMsvEntry );  
+
+    return tMsvEntry.Id();
+    }
+
+// ========================== OTHER EXPORTED FUNCTIONS =========================
+
+// -----------------------------------------------------------------------------
+// ImplementationGroupProxy
+// Returns: TImplementationProxy*: Implementation table to the ECOM framework 
+// -----------------------------------------------------------------------------
+//
+EXPORT_C const TImplementationProxy* ImplementationGroupProxy( TInt& aTableCount )
+    {
+    aTableCount = sizeof( ImplementationTable ) / sizeof( TImplementationProxy );
+
+    return ImplementationTable;
+    }
+
+//  End of File