/*
* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:  The FeedsServer main server class.
*
*/

#include "FeedsDatabase.h"
#include "FeedHandler.h"
#include "FeedsServer.h"
#include <feedsservermsg.h>
#include "FeedsServerSession.h"
#include "OpmlParser.h"
#include "OpmlWriter.h"
#include "PackedFolder.h"
#include "ServerHttpConnection.h"
#include "Logger.h"
#include <xmlutils.h>
#include "BackRestoreHandler.h"
#include "UpdateManager.h" 

//  CONSTANTS
_LIT(KFeedsServerPanic, "FeedsServer");
// opml files are generally smaller in size.But if a malformed opml 
// greater than 400kB would cause the system to be irresponsive.
// So limit the max size to 400 kB
const TInt KMaxOPMLFileSize = 409600;
//_LIT(KFileSchema, "file://c:");
_LIT(KDefaultFeed, "default_feeds.xml");

_LIT(KDefaultOPMLFileParam, "");

// -----------------------------------------------------------------------------
// CFeedsServer::NewL
//
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CFeedsServer* CFeedsServer::NewL()
    {
    CFeedsServer* self = new (ELeave) CFeedsServer(EPriorityNormal);

    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    
    return self;
    }


// -----------------------------------------------------------------------------
// CFeedsServer::NewL
//
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CFeedsServer::ConstructL()
    {
		StartL(KFeedsServerName);
		iBackRestoreHandler = CBackRestoreHandler::NewL(this);
		iBackRestoreHandler->StartListening();
    }


// -----------------------------------------------------------------------------
// CFeedsServer::CFeedsServer
//
// C++ default constructor can NOT contain any code that
// might leave.
// -----------------------------------------------------------------------------
//
CFeedsServer::CFeedsServer(TInt aPriority):
        CServer2(aPriority), iLeakTracker(CLeakTracker::EFeedsServer), iIsSleeping(ETrue)
    {
    }


// -----------------------------------------------------------------------------
// CFeedsServer::~CFeedsServer
//
// Destructor
// -----------------------------------------------------------------------------
//
CFeedsServer::~CFeedsServer()
    {
    SleepServer();    
    delete iBackRestoreHandler;
    iBackRestoreHandler = NULL;   
    }


// -----------------------------------------------------------------------------
// CFeedsServer::NewSessionL
//
// Create session(s) to client(s)
// -----------------------------------------------------------------------------
//
CSession2* CFeedsServer::NewSessionL(const TVersion& aVersion, 
        const RMessage2& /*aMessage*/) const
    {
    // check we're the right version
    TVersion srvVersion(KFeedsServerMajorVersionNumber,
                        KFeedsServerMinorVersionNumber,
                        KFeedsServerBuildVersionNumber);

    if (!User::QueryVersionSupported(srvVersion, aVersion))
        {
        User::Leave(KErrNotSupported);
        }

    // make new session
    return (CFeedsServerSession::NewL(*(const_cast<CFeedsServer*>(this))));
    }


// -----------------------------------------------------------------------------
// CFeedsServer::RunServerL
// 
// Runs the server.
// -----------------------------------------------------------------------------
//
void CFeedsServer::RunServerL()
	{
	CActiveScheduler*  scheduler = NULL;
	CFeedsServer*      server = NULL;

    // Naming the server thread after the server helps to debug panics.
	User::LeaveIfError(User::RenameThread(KFeedsServerName));

	// Create and install the active scheduler.
	scheduler = new (ELeave) CActiveScheduler;
	CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler);

	// Create the server (leave it on the cleanup stack)
	server = CFeedsServer::NewL();
	CleanupStack::PushL(server);

	// Initialisation complete, now signal the client
	RProcess::Rendezvous(KErrNone);

	// Ready to run.  This method doesn't return until the server is shutdown.
	CActiveScheduler::Start();

	// Cleanup.
	CleanupStack::PopAndDestroy(server);
	CleanupStack::PopAndDestroy(scheduler);
	}

// -----------------------------------------------------------------------------
// CFeedsServer::PanicClient
//
// Utility to panic the client.
// -----------------------------------------------------------------------------
void CFeedsServer::PanicClient(const RMessage2& aMessage, TInt aPanic)
    {
    aMessage.Panic(KFeedsServerPanic, aPanic);
    }


// -----------------------------------------------------------------------------
// CFeedsServer::PanicServer
//
// Utility to panic the server.
// -----------------------------------------------------------------------------
void CFeedsServer::PanicServer(TInt aPanic)
    {
    User::Panic(KFeedsServerPanic, aPanic);
    }


// -----------------------------------------------------------------------------
// CFeedsServer::WakeupServerL
//
// Ensures the server is ready for processing requests.
// -----------------------------------------------------------------------------
//
void CFeedsServer::WakeupServerL()
    {
    if (iIsSleeping)
        {
        // Wake up the server.
        TRAPD(err, WakeupServerHelperL());
        
        // If the server didn't fully wake up then sleep it and re-leave the error.
        if (err != KErrNone)
            {
            SleepServer();
            User::Leave(err);
            }
        }
    }

// -----------------------------------------------------------------------------
// CFeedsServer::WakeupServerHelperL
//
// A helper method to ensure the server is fully started.
// -----------------------------------------------------------------------------
//
void CFeedsServer::WakeupServerHelperL()
    {
    TBool    created;

    if (iIsSleeping)
        {            
        // Create the shared xml utils instance.
        iXmlUtils = CXmlUtils::NewL();

        // Create the handlers.
        iFeedHandler = CFeedHandler::NewL(*iXmlUtils);
        iFolderHandler = COpmlParser::NewL(*iXmlUtils);

        // Create the database instance.
        iFeedsDatabase = CFeedsDatabase::NewL(this,created);
        
        // If the database was newly created then load the default feeds into the 
        // the database.
        if (created)
            {
            RFs                 rfs;
            TBuf<KMaxFileName>  path;
            RFile               file;
            CPackedFolder*      packedFolder = NULL;
            
            // Ensure that the default folder file is copied from ROM. 
            EnsureDefaultFolderFileL();
            
            // Get the path to the default folder.
            User::LeaveIfError(rfs.Connect());
            CleanupClosePushL(rfs);

            rfs.PrivatePath(path);
            path.Append(KDefaultFeed);
            User::LeaveIfError(file.Open(rfs, path, EFileRead | EFileShareReadersOnly));
            CleanupClosePushL(file);

            // Import the default folder file synchronously.
            packedFolder = ImportFolderL(file, KDefaultOPMLFileParam);
            
            CleanupStack::PopAndDestroy(/*file*/);
            CleanupStack::PopAndDestroy(/*rfs*/);

            CleanupStack::PushL(packedFolder);

            // Load it into the database.
            iFeedsDatabase->ImportFolderL(KDefaultFolderListId, *packedFolder); 
            CleanupStack::PopAndDestroy(packedFolder);
            
            // Compact the database.
            iFeedsDatabase->Compact();

            }

        ScheduleUpdateManagerL(); 
                
        iIsSleeping = EFalse;
        }
    }


// -----------------------------------------------------------------------------
// CFeedsServer::SleepServer
//
// Put the server in a resource reduced mode.
// -----------------------------------------------------------------------------
//
void CFeedsServer::SleepServer()
    {
    iIsSleeping = ETrue;
    
    for( int i=0; i<iUpdateManagerList.Count(); i++ )
        {
        delete iUpdateManagerList[ i ];
        }
    iUpdateManagerList.Reset();

    delete iFeedHandler;
    iFeedHandler = NULL;
    
    delete iFolderHandler;
    iFolderHandler = NULL;
    
    delete iFeedsDatabase;
    iFeedsDatabase = NULL;
    
    delete iXmlUtils;
    iXmlUtils = NULL;
    }


// -----------------------------------------------------------------------------
// CFeedsServer::NotifyFolderListChanged
//
// Notify the open sessions that the given folder-list changed.
// -----------------------------------------------------------------------------
//
void CFeedsServer::NotifyFolderListChanged(TInt aFolderListId)
    {
    CSession2*  session = NULL;

    // Notify all of the open sessions.    
    iSessionIter.SetToFirst();
    while ((session = iSessionIter++) != NULL)
        {
        static_cast<CFeedsServerSession*>(session)->NotifyFolderListChanged(aFolderListId);
        }
    }


// -----------------------------------------------------------------------------
// CFeedsServer::NotifySettingsChanged
//
// Notify the open sessions that the given folder-list changed.
// -----------------------------------------------------------------------------
//
void CFeedsServer::NotifySettingsChanged(TInt aFolderListId)
    {
    CSession2*  session = NULL;

    // Notify all of the open sessions.    
    iSessionIter.SetToFirst();
    while ((session = iSessionIter++) != NULL)
        {
        static_cast<CFeedsServerSession*>(session)->NotifySettingsChanged(aFolderListId);
        }
    }
    
    
#if 0
// -----------------------------------------------------------------------------
// CFeedsServer::PurgeOldItems
//
// A method used to purge old feed-items in the background every so often.
// -----------------------------------------------------------------------------
//
TInt CFeedsServer::PurgeOldItems(TAny *aPtr)
    {
    CFeedsServer*  self = static_cast<CFeedsServer*>(aPtr);
    
    TRAPD(err, self->PurgeOldItemsL());    
    return err;
    }
    
    
// -----------------------------------------------------------------------------
// CFeedsServer::PurgeOldItemsL
//
// A method used to purge old feed-items in the background every so often.
// -----------------------------------------------------------------------------
//
void CFeedsServer::PurgeOldItemsL()
    {
    WakeupServerL();

    // Purge any old items.
    TTime yesterday;
    
    yesterday.UniversalTime();
    yesterday -= TTimeIntervalDays(1);
    
    iFeedsDatabase->PurgeOldItemsL(yesterday);
    }
#endif
    
    
// -----------------------------------------------------------------------------
// CFeedsServer::ImportFolderL
//
// Loads and parsers the folder of feeds at the given path.
// -----------------------------------------------------------------------------
//
CPackedFolder* CFeedsServer::ImportFolderL(const RFile aFile, const TDesC& aOPMLFileName)
    {
    TInt            size;
    HBufC8*         buffer = NULL;
    TPtr8           bufferPtr(NULL, 0);
    CPackedFolder*  packedFolder = NULL;

    // Read file
    User::LeaveIfError(aFile.Size(size));
    if ( size > KMaxOPMLFileSize )
        {
        User::Leave(KErrCorrupt);	
        }
    buffer = HBufC8::NewLC(size);
    bufferPtr.Set(buffer->Des());

    User::LeaveIfError(aFile.Read(bufferPtr, size));

    // Parse the buffer
    packedFolder = iFolderHandler->ParseL(*buffer, KNullDesC, KNullDesC, aOPMLFileName);

    // Clean up.
    CleanupStack::PopAndDestroy(buffer);

    return packedFolder;
    }

void CFeedsServer::ExportFolderL(RArray<TInt> &aEntries, const TDesC& aExportFileName)
	{
    // Export folders to an OPML buffer
	OpmlWriter::ExportL(aEntries, aExportFileName);
	}

// -----------------------------------------------------------------------------
// CFeedsServer::EnsureDefaultFolderFileL
//
// If need be copy default folder file from ROM.
// -----------------------------------------------------------------------------
//
void CFeedsServer::EnsureDefaultFolderFileL()
    {
    TInt                err;
    RFs                 defaultRfs;
    TUint               attValue = 0;
    TBuf<KMaxFileName>  path;

    // Open a connection to the working drive.
    User::LeaveIfError(defaultRfs.Connect());
    CleanupClosePushL(defaultRfs);
    User::LeaveIfError(defaultRfs.SetSessionPath(_L("c:\\")));
    
    // Build the path to the file.
    defaultRfs.PrivatePath(path);
    path.Append(KDefaultFeed);

    // Test whether or not the folder file is present.
    err = defaultRfs.Att(path, attValue);

    // The file is there, just return.
    if (err == KErrNone)
        {
        CleanupStack::PopAndDestroy(/*defaultRfs*/);    
        return;
        }
        
    // If the file is missing copy it from ROM.
    if ((err == KErrNotFound) || (err == KErrPathNotFound))
        {
        RFs      romRfs;
        RFile    file;
        RFile    romFile;
        TInt     size;
        HBufC8*  buffer = NULL;    

        // Ensure the private path is defined on the c drive.
        (void) defaultRfs.CreatePrivatePath(EDriveC);
        
        // Open an rfs for the z drive.        
        User::LeaveIfError(romRfs.Connect());
        CleanupClosePushL(romRfs);
        User::LeaveIfError(romRfs.SetSessionPath(_L("z:\\")));

        // Create the destination file.
        User::LeaveIfError(file.Create(defaultRfs, path, EFileWrite));
        CleanupClosePushL(file);
        
        // Open the source file.
        User::LeaveIfError(romFile.Open(romRfs, path, EFileRead));
        CleanupClosePushL(romFile);
        
        // Copy the file.
        User::LeaveIfError(romFile.Size(size));
        buffer = HBufC8::NewLC(size);
        TPtr8 bufferPtr(buffer->Des());
        
        User::LeaveIfError(romFile.Read(bufferPtr, size));
        User::LeaveIfError(file.Write(bufferPtr, size));
          
        // Clean up
        CleanupStack::PopAndDestroy(buffer);
        CleanupStack::PopAndDestroy(/*romFile*/);
        CleanupStack::PopAndDestroy(/*file*/);
        CleanupStack::PopAndDestroy(/*romRfs*/);    
        CleanupStack::PopAndDestroy(/*defaultRfs*/);    
        }
        
    // Otherwise leave.
    else
        {
        User::LeaveIfError(err);
        }
    }

// -----------------------------------------------------------------------------
// CFeedsServer::SessionOpened
//
// Called from CFeedsServerSession when a session is opened.
// -----------------------------------------------------------------------------
//
void CFeedsServer::SessionOpened()
    {
    iSessionCount++;
    }

// -----------------------------------------------------------------------------
// CFeedsServer::SessionClosed
//
// Called from CFeedsServerSession when a session is closed.
// -----------------------------------------------------------------------------
//
void CFeedsServer::SessionClosed()
    {
    iSessionCount--;
    
    // If all of the sessions are closed then terminate the server.
    //
    // TODO: Create a timer to delay the shutdown for 20 mins or so, in case
    //       the client crashes.
    // Don't want to shut down feedsserver to ensure autoupdate can occur
    if ( iSessionCount <= 0 && !CheckForUpdate() )
         {
         CActiveScheduler::Stop();
         }
    }

// -----------------------------------------------------------------------------
// CFeedsServer::SetAutoUpdateSettingsL
//
// Called from CFeedsServerSession when setting changed.
// -----------------------------------------------------------------------------
//
void CFeedsServer::SetAutoUpdateSettingsL( TInt aFolderListId, TBool aAutoUpdate, TInt aAutoUpdateFreq, TUint32 aAutoUpdateAP, TBool aAutoUpdateWhileRoaming ) 
    {
    TBool    autoUpdate = EFalse;
    TInt     autoUpdateFreq = 0;
    TUint32  autoUpdateAP = 0;
    TBool	 autoUpdateWhileRoaming = EFalse;
    
    TRAPD( err, iFeedsDatabase->ExtractAutoUpdateSettingsL( aFolderListId, autoUpdate, autoUpdateFreq, autoUpdateAP, autoUpdateWhileRoaming ) );
    if( err != KErrNotFound && err != KErrNone )
        {
        User::Leave( err );
        }

    // if any setting changed regarding auto update, restart the scheduler
    if( autoUpdate != aAutoUpdate || autoUpdateFreq != aAutoUpdateFreq || autoUpdateAP != aAutoUpdateAP || autoUpdateWhileRoaming != aAutoUpdateWhileRoaming)
        {
        iFeedsDatabase->CommitAutoUpdateSettingsL( aFolderListId, aAutoUpdate, aAutoUpdateFreq, aAutoUpdateAP, aAutoUpdateWhileRoaming );
                
        // notify all clients with the same folderListId about setting changes
        NotifySettingsChanged( aFolderListId );

        ScheduleUpdateManagerL( aFolderListId, aAutoUpdate, aAutoUpdateFreq, aAutoUpdateAP, aAutoUpdateWhileRoaming );        
        }
    }

// -----------------------------------------------------------------------------
// CFeedsServer::ScheduleUpdateManagerL
//
// Schedule the update manager for all folder list ID
// -----------------------------------------------------------------------------
//
void CFeedsServer::ScheduleUpdateManagerL()
    {
    RArray<TInt>     folderListIds;
	CleanupClosePushL(folderListIds);
    TBool    autoUpdate = EFalse;
    TInt     autoUpdateFreq = 0;
    TUint32  autoUpdateAP = 0;
	TBool    autoUpdateWhileRoaming = EFalse;
    
    TRAPD( err, iFeedsDatabase->ExtractFolderListIdInSettingsL( folderListIds ) );
    if (err == KErrNone)
        {               
        for( int i=0; i < folderListIds.Count(); i++)
            {
            // When err is KErrNotFound, autoUpdate will remain false
            TRAPD( err, iFeedsDatabase->ExtractAutoUpdateSettingsL( folderListIds[i], autoUpdate, autoUpdateFreq, autoUpdateAP, autoUpdateWhileRoaming ) );
            if (err == KErrNone)
                {
                ScheduleUpdateManagerL( folderListIds[i], autoUpdate, autoUpdateFreq, autoUpdateAP, autoUpdateWhileRoaming );
                }
            }
        }
    
    CleanupStack::PopAndDestroy(); //folderListIds
    }

// -----------------------------------------------------------------------------
// CFeedsServer::ScheduleUpdateManagerL
//
// Schedule the update manager for all folder list ID
// -----------------------------------------------------------------------------
//
void CFeedsServer::ScheduleUpdateManagerL( TInt aFolderListId)
    {
    TBool    autoUpdate = EFalse;
    TInt     autoUpdateFreq = 0;
    TUint32  autoUpdateAP = 0;
	TBool    autoUpdateWhileRoaming = EFalse;

    // When err is KErrNotFound, autoUpdate will remain false
    TRAPD( err, iFeedsDatabase->ExtractAutoUpdateSettingsL( aFolderListId, autoUpdate, autoUpdateFreq, autoUpdateAP, autoUpdateWhileRoaming ) );
    if (err == KErrNone)
        {
        ScheduleUpdateManagerL( aFolderListId, autoUpdate, autoUpdateFreq, autoUpdateAP, autoUpdateWhileRoaming );
        }
    }

// -----------------------------------------------------------------------------
// CFeedsServer::ScheduleUpdateManagerL
//
// Schedule the background update all task for one folder list ID.
// -----------------------------------------------------------------------------
//
void CFeedsServer::ScheduleUpdateManagerL( TInt aFolderListId, TBool /*aAutoUpdate*/, TInt aAutoUpdateFreq, TUint32 aAutoUpdateAP, TBool aAutoUpdateWhileRoaming )
    {
    // Clear the old timer.

    for( int i=0; i < iUpdateManagerList.Count(); i++)
        {
        if( iUpdateManagerList[ i ]->FolderListId() == aFolderListId )
            {
            delete iUpdateManagerList[ i ];
            iUpdateManagerList.Remove(i); 
            break;
            }
        }
    CUpdateManager* updateManager = CUpdateManager::NewL( aFolderListId, aAutoUpdateAP, aAutoUpdateFreq, aAutoUpdateWhileRoaming, *this );
    updateManager->StartL();
    iUpdateManagerList.AppendL( updateManager );
    } 

// -----------------------------------------------------------------------------
// CFeedsServer::ResetXmlUtilsL
//
// Called from CUpdateFeedTask::LoadCompleted when an error occurs while parsing
// the feed.
// -----------------------------------------------------------------------------
//
void CFeedsServer::ResetXmlUtilsL()
    {
    if (iFolderHandler != NULL)
        {
        delete iFolderHandler;
        iFolderHandler = NULL;
        }
        
    if (iFeedHandler != NULL)
        {
        delete iFeedHandler;
        iFeedHandler = NULL;
        }
        
    if (iXmlUtils != NULL)
        {
        delete iXmlUtils;
        iXmlUtils = NULL;
        }
    
    iXmlUtils = CXmlUtils::NewL();
    iFeedHandler = CFeedHandler::NewL(*iXmlUtils);
    iFolderHandler = COpmlParser::NewL(*iXmlUtils);
    }

// -----------------------------------------------------------------------------
// CFeedsServer::CheckForUpdate
//
// Checks whether update is happening
// -----------------------------------------------------------------------------
//
TBool CFeedsServer::CheckForUpdate()
    {
    for(int i = 0;i < iUpdateManagerList.Count();i++)
        {
        if(iUpdateManagerList[i]->iQueueArray.Count() > 0)
            return ETrue;
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CFeedsServer::UpdateFeedL()
//
// Update the feed ID in Update manager
// -----------------------------------------------------------------------------
//
void CFeedsServer::UpdateFeedL(TInt aFolderListId, TInt aFeedId, TBool aDeleteFeed)
    {
    for( int i=0; i < iUpdateManagerList.Count(); i++)
        {
        if( iUpdateManagerList[ i ]->FolderListId() == aFolderListId )
            {
            iUpdateManagerList[ i ]->UpdateFeedL(aFeedId,aDeleteFeed);
            return;
            }
         else if(aFolderListId == KNoFolderListId)
            {
            iUpdateManagerList[ i ]->UpdateFeedL(aFeedId,aDeleteFeed);
            }
        }	
    }

