// Copyright (c) 1998-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 "SMTS.H"
#include "SMTSUTIL.H"
#include "IMSM.H"
#include <smtpcmds.h>
#include <msventry.h>
#include <miutset.h>

EXPORT_C CSmtpSrvMtm* CSmtpSrvMtm::NewL(CRegisteredMtmDll& aRegisteredMtmDll,CMsvServerEntry*  aEntry)
	{
	CSmtpSrvMtm* mysvrmtm = new CSmtpSrvMtm(aRegisteredMtmDll, aEntry);
	if (mysvrmtm==NULL)
		{
		aRegisteredMtmDll.ReleaseLibrary();
		User::Leave(KErrNoMemory);
		}
	CleanupStack::PushL(mysvrmtm);
	mysvrmtm->ConstructL();
	CleanupStack::Pop();
	return mysvrmtm;
	}

CSmtpSrvMtm::CSmtpSrvMtm(CRegisteredMtmDll& aRegisteredMtmDll, CMsvServerEntry* aEntry):
	CBaseServerMtm(aRegisteredMtmDll, aEntry)
	{
	__DECLARE_NAME(_S("CStmpSvrMtm"));
	}
		
void CSmtpSrvMtm::ConstructL()
	{
	iSessionError=KErrNone;
	iProgressBuffer().SetStatus(EMsgOutboxProgressDone);
#if (defined SYMBIAN_USER_PROMPT_SERVICE)
	iHasCapability = ETrue;
#endif
	CActiveScheduler::Add(this);
	}

CSmtpSrvMtm::~CSmtpSrvMtm()
	{
	Cancel();	//classes derived from CActive MUST call Cancel()
	delete iOutboxSend;	//should already be dead
	delete iSendSelection;
	//  Set the Service Entry to be NOT connected
    SetServiceToConnected(EFalse);
   	}


// aServiceId is ignored and the service ids of the messages in the selection
// are used instead

void CSmtpSrvMtm::CopyFromLocalL(const CMsvEntrySelection& aSelection, TMsvId aServiceId, /* Info for protocol....*/ TRequestStatus& aStatus)
	{
	iServiceId=aServiceId;
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	__ASSERT_DEBUG(iServerEntry != NULL,gPanic(EEntryNotSet));
	if (IsActive())
		User::Leave(KErrServerBusy);

	if (iSendSelection)
		iSendSelection->Reset();
	else
		iSendSelection=new (ELeave) CMsvEntrySelection;

	CopyToOurSelectionL(aSelection);
	AddWaitingInOutboxToSelectionL();

	if (iSendSelection->Count())
		DoSendMessagesL(aStatus);
	else
		{
		iSessionError=KSmtpNoMsgsToSendWithActiveSettings;
		TRequestStatus* pStatus=&aStatus;
		User::RequestComplete(pStatus,KErrNone);//nothing to send
		}
	}



void CSmtpSrvMtm::DoSendMessagesL(TRequestStatus& aStatus)
	{
	iSessionError = KErrNone;
    
	// Set the Service entry to be Connected
	User::LeaveIfError(SetServiceToConnected(ETrue));
	
	delete iOutboxSend;
	iOutboxSend=NULL;
	iOutboxSend = CMsgImOutboxSend::NewL(*iSendSelection,*iServerEntry,iServiceId);

#if (defined SYMBIAN_USER_PROMPT_SERVICE)
	iOutboxSend->StartL(iStatus, iClientThreadId, iHasCapability, iIsSilent);
#else
	iOutboxSend->StartL(iStatus, iIsSilent);
#endif
	
	SetActive();
	aStatus = KRequestPending;
	iReport = &aStatus;
	}


//
//	Progress()
//
//	Args:			None.
//	
//	Return Value:	TDesC& -- reference to a TBufPckg<TImSmtpProgress>
//					maintained by the instantiated CMsgImOutboxSend
//					object...
//
//	Remarks:				
//
const TDesC8& CSmtpSrvMtm::Progress()
	{
	//	load in latest progress, otherwise use saved progress
	if(iOutboxSend!=NULL)
		{
		iProgressBuffer = iOutboxSend->Progress();
		}
	else
		iProgressBuffer().SetError(iSessionError);
	return iProgressBuffer;
	}

void CSmtpSrvMtm::DoCancel()
	{
	if(iOutboxSend)
		{
		iOutboxSend->Cancel();
		Progress();	//update iProgressBuffer
		delete  iOutboxSend;
		iOutboxSend = NULL;
		}

    //  Set the Service Entry to be NOT connected
    SetServiceToConnected(EFalse);

	User::RequestComplete(iReport,KErrCancel);
	}

void CSmtpSrvMtm::DoRunL()
	{
	iSessionError = iStatus.Int();
	Progress();

	//	Delete the CImOutboxSend object.
	delete  iOutboxSend;
	iOutboxSend = NULL;

    //  Set the Service Entry to be NOT connected
    SetServiceToConnected(EFalse);

	User::RequestComplete(iReport,KErrNone);	// always returns KErrNone; errors are returned in Progress
	}

void CSmtpSrvMtm::DoComplete(TInt aError)
	{
	iSessionError=aError;	// store error away to set Progress() later
	User::RequestComplete(iReport,KErrNone);	// always returns KErrNone; errors are returned in Progress
	}


TBool CSmtpSrvMtm::CommandExpected()
	{
	return EFalse;
	}

void CSmtpSrvMtm::StartCommandL(CMsvEntrySelection& aSelection,
                                TInt aCommand,
                                const TDesC8& /*aParameter*/,
                                TRequestStatus& aStatus)
	{
	TInt error = KErrNone;

    switch (aCommand)
        {
    //  Is session connected...
    case KSMTPMTMIsConnected:
		if (iOutboxSend)
			iOutboxSend->SessionIsConnected();
		else 
            error = KErrDisconnected;    
        break;
    case KSMTPMTMSendOnNextConnection:
    case KSMTPMTMSilentlySendOnNextConnection:
        if(aCommand == KSMTPMTMSilentlySendOnNextConnection)
            {
            iIsSilent = ETrue;
            }
        else
            {
            iIsSilent = EFalse;
            }
		CopyFromLocalL(aSelection, aSelection.At(0), aStatus);
		return;
    default:
        error = KErrNotSupported;
        break;
        }
	iReport=&aStatus;
	aStatus=KRequestPending;
	User::RequestComplete(iReport, error);
	}


void CSmtpSrvMtm::CopyToOurSelectionL(const CMsvEntrySelection& aSourceSelection)
	{
  	TInt count=aSourceSelection.Count();
  	TMsvEntry entry;
  	while (count--)
  		{
  		TInt err=iServerEntry->SetEntry(aSourceSelection[count]);
		if(err==KErrNone)
			{
  			entry=iServerEntry->Entry();
  
			// If this entry is a message using the SMTP service & it hasn't been sent, 
			// then add it to the list of entries to send.
  			if (entry.iMtm==KUidMsgTypeSMTP && entry.iType==KUidMsvMessageEntry &&	
				entry.iServiceId==iServiceId && entry.SendingState() != KMsvSendStateSent)
				{
				iSendSelection->AppendL(aSourceSelection[count]);						
				}
			}
		}
  	}

void CSmtpSrvMtm::AddWaitingInOutboxToSelectionL()
	{
	User::LeaveIfError(iServerEntry->SetEntry(KMsvGlobalOutBoxIndexEntryId));
	CMsvEntrySelection *sel = new (ELeave) CMsvEntrySelection;
	CleanupStack::PushL(sel);
	User::LeaveIfError(iServerEntry->GetChildrenWithService(iServiceId,*sel));
	TInt count=sel->Count();
	TMsvEntry entry;
	while (count--)
		{
		TInt err=iServerEntry->SetEntry((*sel)[count]);
		if(err==KErrNone)
			{
			entry=iServerEntry->Entry();
			if ((entry.SendingState()==KMsvSendStateWaiting ||entry.SendingState()==KMsvSendStateScheduled || entry.SendingState()==KMsvSendStateResend) && //i.e send on next connection
				iSendSelection->Find((*sel)[count])==KErrNotFound && 
				entry.iType==KUidMsvMessageEntry) 
				iSendSelection->AppendL((*sel)[count]);						
			}
		}
	CleanupStack::PopAndDestroy(sel);
	}


TInt CSmtpSrvMtm::SetServiceToConnected(TBool aConnected)
// Set the Service Entry to be either connected or not. 
	{
	TInt error = iServerEntry->SetEntry( iServiceId );
    if (error == KErrNone)
        {
        TMsvEntry entry = iServerEntry->Entry();

		// Change the connected flag if not already set to this value
		if (entry.Connected()!=aConnected)
			{
		    entry.SetConnected(aConnected);
			error = iServerEntry->ChangeEntry(entry);
			}
        }

	// returns any error that occured
	return error;
	}


//*****************************************************************************
//The rest of the commands are unsupported
//*****************************************************************************

void CSmtpSrvMtm::MoveFromLocalL(const CMsvEntrySelection& /*aSelection*/,
                                 TMsvId /*aServiceId*/,
                                 TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::CopyToLocalL(const CMsvEntrySelection& /*aSelection*/,
                               TMsvId /*aDestination*/, 
                               TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::MoveToLocalL(const CMsvEntrySelection& /*aSelection*/,
                               TMsvId /*aDestination*/,
                               TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::CopyWithinServiceL(const CMsvEntrySelection& /*aSelection*/,
                                     TMsvId /*aDestination*/, TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::MoveWithinServiceL(const CMsvEntrySelection& /*aSelection*/,
                                     TMsvId /*aDestination*/,
                                     TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::DeleteAllL(const CMsvEntrySelection& /*aSelection*/,
                             TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::CreateL(TMsvEntry /*aNewEntry*/,
                          TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

void CSmtpSrvMtm::ChangeL(TMsvEntry /*aNewEntry*/,
                          TRequestStatus& aStatus)
	{
	__ASSERT_DEBUG(iOutboxSend == NULL,gPanic(ESmtsBusy));
	iReport=&aStatus;
	User::RequestComplete(iReport,KErrNotSupported);
	}

/**
The extension method provides a polymorphic behaviour to call the correct
MTM.
@param aExtensionId The Uid passed in as KUIDMsgClientThreadInfo to copy the
					Client Thread Info.
@return KErrNone.
*/
TInt CSmtpSrvMtm::Extension_(TUint aExtensionId, TAny *&a0, TAny *a1)	
	{
	switch(aExtensionId)
		{
#if (defined SYMBIAN_USER_PROMPT_SERVICE)		
		case KUIDMsgClientThreadInfo:
			{
			iClientThreadId = *(TThreadId*) (a1);
	    	iHasCapability = (TBool)*(TInt*)(a0);
	    	return KErrNone;
			}
#endif

		case KUidMsgNonOperationMtmData:
			{
			TNonOperationMtmDataType* mtmDataType = reinterpret_cast<TNonOperationMtmDataType*>(a0);
			TPtrC8* mtmDataBuffer = reinterpret_cast<TPtrC8*>(a1);
			return GetNonOperationMtmData(*mtmDataType, *mtmDataBuffer);
			}

		default:
			{
			// Chain to base class
			return CBaseServerMtm::Extension_(aExtensionId, a0, a1);
			}
		}
	}

/**
Gets MTM information that is not related to the current operation

@param aMtmDataType Type of data to fetch
@param aMtmDataBuffer On return this points to a descriptor holding the data

@return KErrNone if successful, or a system wide error code
*/
TInt CSmtpSrvMtm::GetNonOperationMtmData(TNonOperationMtmDataType aMtmDataType, TPtrC8& aMtmDataBuffer)
	{
	if (aMtmDataType == EMtmDataAccessPointId)
		{
		if (iOutboxSend)
			{
			TNonOperationMtmDataAccessPointId mtmDataAccessPointId;

			TInt err = iOutboxSend->GetAccessPointIdForConnection(mtmDataAccessPointId.iAccessPointId);

			if (err == KErrNone)
				{
				iMtmDataAccessPointIdBuffer = TNonOperationMtmDataAccessPointIdBuffer(mtmDataAccessPointId);
				aMtmDataBuffer.Set(iMtmDataAccessPointIdBuffer);
				return KErrNone;
				}
			}

		return KErrNotFound;
		}

	return KErrNotSupported;
	}

