// EIKIRDA.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

#pragma warning( disable : 4710 )
#pragma warning( disable : 4100 )

#include <eikirda.h>
#include <eikirprv.h>
#include <eikirobs.h>
#include <eikirda.pan>
#include <eikenv.h>
#include <eiklabel.h>
#include <eikprogi.h>
#include <eikfutil.h>
#include <c32comm.h>
#include <basched.h>
#include <eikimgar.h>
#include <coeutils.h>
#include <eikcfdlg.h>
#include <e32hal.h>
#include <plpsess.h>
#include <eiktxtut.h>
#include <apparc.h>

#include <eikon.rsg>
#include "eikpriv.hrh"
#include "eikdialg.hrh"
#include "eikcfdlg.hrh"

GLDEF_C void Panic(TEikIrdaPanic aPanic)
	{
	User::Panic(__EIKON_IRDA, aPanic);
	}

GLDEF_C void IrCleanup(TAny* aIr)
	{
	CEikonEnv::Static()->Flush(2000000);
	delete((CEikIrMain*)aIr);
	}

const TInt numberOfDiscoveryAttempts=80;
const TInt KIrDialogTextMargin=5;
// IAS constants
#define EIKON_IRDA_V1_IAS_CLASSNAME _L8("Epoc32:EikonIr:v1.0")
#define EIKON_IRDA_V2_IAS_CLASSNAME _L8("Epoc32:EikonIr:v2.0")
#define EIKON_IRDA_IAS_ATTRIBUTE _L8("IrDA:TinyTP:LsapSel")
const TInt KEikIrDALsapSel=8;

//
// class CEikIrActive
//
   
CEikIrActive::CEikIrActive() : CActive(CActive::EPriorityStandard)
    {
	iCurrentFile=0;
	iNumOfFiles=1;
	iVersion=2;
	}

CEikIrActive::~CEikIrActive()
    {
	Cancel();
	}

CEikIrActive* CEikIrActive::NewL(CEikIrMain* aIrMain)
	{
	CEikIrActive *This = new(ELeave) CEikIrActive;
	This->Construct(aIrMain);
	CActiveScheduler::Add(This);
	return(This);
	}

void CEikIrActive::Construct(CEikIrMain* aIrMain)
	{
	iIrMain=aIrMain;
	}

void CEikIrActive::Start()
	{
	iPhase=EInitial;
	TRequestStatus* pS=&iStatus;
	User::RequestComplete(pS,0);
	SetActive();
	}
																									
void CEikIrActive::RunL()
	{
	TInt ret=iStatus.Int();	// from previous request
	if ((ret!=KErrNone) && (iPhase!=EDiscovery) && (iPhase!=EIASResponse)) 
		{
		iIrMain->HandleIrError(ret);
		return;
		}
	UserHal::ResetAutoSwitchOffTimer();
	if (iIrMain->Type()==CEikIrMain::ESender)
		SenderRunL();
	else
		ReceiverRunL();
	}

void CEikIrActive::SenderRunL()
	{
	CEikIrSender* sender=(CEikIrSender*)iIrMain;
	TRequestStatus* pS=&iStatus;
	switch (iPhase)
		{
	case EInitial:
		{
		TInt err=sender->Discover(iStatus);
		if (err != KErrNone)
			return;
		iPhase=EDiscovery;
		}
		break;
	case EDiscovery:
		{
		TInt err=sender->HandleDiscovery(iStatus);
		switch (err)
			{
		case KErrNone:
			iPhase=EIASQuery;
			User::RequestComplete(pS,0);
			break;
		case KErrNotFound:
		case KErrAlreadyExists:
			return;
		default:  //	More disco's left!
			break;
			}
		break;
		}
	case EIASQuery:
		iPhase=EIASResponse;
		sender->IASQueryVersion1(iStatus);
		break;
	case EIASResponse:
		if (iStatus.Int()==KErrNone)
			{
			sender->iRemotePort=iIrMain->IASResponseGetRemotePort();
			iPhase=EConnect;
			}
		else
			{
			if (iVersion==1)
				{
				iIrMain->HandleIrError(iStatus.Int()); // All IAS failed
				return;
				}
			else
				{
				iVersion--;
				iPhase=EIASQuery;
				}
			}
		User::RequestComplete(pS,0);
		break;
	case EConnect:
		{
		iPhase=ENegotiate;
		sender->Connect(iStatus);
		}
		break;
	case ENegotiate:
		{
		if (!(sender->iBuf))
			sender->AllocatePacketBufferL();
		TBool proceed=sender->NegotiateL(iStatus);
		if (!proceed)
			return;
		}
		break;
	case ETransfer:
		sender->HandleProgressUpdateL();
		sender->DoSendL(iStatus);
		break;
	case EComplete:
		sender->HandleProgressUpdateL();
		sender->RequestDisconnectIndication(iStatus);	// ensure all buffered frames received
		iPhase=EClosed;
		break;
	case EClosed:
		{
		TCleanupItem item(&IrCleanup, sender);
		CleanupStack::PushL(item);
		sender->SetTopLabel(R_EIK_TBUF_IR_TRANSFER_COMPLETE);
		sender->HandleTransferCompleteL();
		if (iIrMain->iIrObserver)
			iIrMain->iIrObserver->HandleIrEventL(sender, MEikIrObserver::ETransferComplete);
		CBaActiveScheduler::LeaveNoAlert();
		}
		return;	// i.e don't re-issue request
	default:
		;
		}
	SetActive();
	}

void CEikIrActive::ReceiverRunL()
	{
	TInt ret=iStatus.Int();	// from previous request
	if (ret!=KErrNone)
		{
		iIrMain->HandleIrError(ret);
		return;
		}
	CEikIrReceiver* receiver=(CEikIrReceiver*)iIrMain;
	TRequestStatus* pS=&iStatus;
	switch (iPhase)
		{
	case EInitial:
		{
		TInt err=receiver->Initialize();
		if (err != KErrNone)
			return;
		iPhase=EIASResponse; // no secondary IAS querying in version 1
		User::RequestComplete(pS,0);
		}
		break;
	case EIASResponse:
		{
		TInt err=receiver->RegisterIASClasses();
		if (err != KErrNone)
			return;
		iPhase=EWaitToBeDiscovered;
		User::RequestComplete(pS,0);
		}
		break;
	case EWaitToBeDiscovered:
		iPhase=EWaitForConnect;
		receiver->WaitToBeDiscovered(iStatus);
		break;
	case EWaitForConnect:
		iPhase=ENegotiate;
		receiver->WaitForConnect(iStatus);
		break;
	case ENegotiate:
		{
		if (!(receiver->iBuf))
			{
			receiver->MakeSendImageVisible(ETrue);
			receiver->AllocatePacketBufferL();
			}
		TBool proceed=receiver->NegotiateL(iStatus);
		if (!proceed)
			return;
		}
		break;
	case ETransfer:
		receiver->HandleProgressUpdateL();
		receiver->DoReceiveL(iStatus);
		break;
	case EComplete:
		receiver->HandleProgressUpdateL();
		receiver->iReader.Shutdown(RSocket::ENormal, iStatus);
		iPhase=EClosed;
		break;
	case EClosed:
		{
		TCleanupItem item(&IrCleanup, receiver);
		CleanupStack::PushL(item);
		receiver->SetTopLabel(R_EIK_TBUF_IR_TRANSFER_COMPLETE);
		receiver->HandleTransferCompleteL();
		if (iIrMain->iIrObserver)
			iIrMain->iIrObserver->HandleIrEventL(receiver, MEikIrObserver::ETransferComplete);
		CBaActiveScheduler::LeaveNoAlert(); 
		}
		return;	// i.e don't re-issue request
	default:
		;
		}
	SetActive();
	}

void CEikIrActive::DoCancel()
	{
	if ((iPhase==EIASQuery || iPhase==EIASResponse) && iIrMain->Type()==CEikIrMain::ESender)
		iIrMain->iNetDb.Cancel();
	}  

void CEikIrActive::NextFile()
	{
	iPhase=ENegotiate;
	++iCurrentFile;
	}  

//
// class CEikIrMain
//

EXPORT_C CEikIrMain::~CEikIrMain()
	{
	TBool enableLink=EFalse;
	if (iActive)
		enableLink=iActive->iReEnableLink;
	delete iActive;
	iNetDb.Close();
	iSockServ.Close();
	delete iBuf;
	delete iBufPtr;
	iEikonEnv->Flush();
	if (enableLink)
		ReEnableRemoteLink();
	}

void CEikIrMain::PreLayoutDynInitL()
	{
	DisableRemoteLink();
	MakeProgressBarVisible(EFalse);
	iActive->Start();
	}

TBool CEikIrMain::OkToExitL(TInt aButtonId)
	{
	if (aButtonId!=EEikBidCancel)
		return EFalse;
	// User cancelled transfer
	HandleUserCancelL();
	if (iIrObserver)
		iIrObserver->HandleIrEventL(this, MEikIrObserver::ETransferCanceled);
	return ETrue;
	}

void CEikIrMain::HandleUserCancelL()
	{
	}

void CEikIrMain::HandleTransferCompleteL()
	{
	}

void CEikIrMain::HandleIrError(TInt aError)
	{
	TInt topId=0;
	TInt botId=0;
	TBuf<32> topText,botText;
	switch (aError)
		{
	case KErrAlreadyExists:
		topId=R_EIK_TBUF_IR_IN_USE_TOP;
		break;
	case KErrDisconnected:
		topId=R_EIK_TBUF_IR_TRANSFER_INTERRUPT;
		botId=R_EIK_TBUF_IR_CHECK_AIM_AND_DISTANCE;
		break;
	case KErrAccessDenied:
	case KErrInUse:
		topId=R_EIK_TBUF_IR_ERROR;
		botId=R_EIK_TBUF_IR_CHECK_SERIAL;
		break;
	case KErrTimedOut:
		topId=R_EIK_TBUF_IR_COULD_NOT_CONNECT;
		botId=R_EIK_TBUF_IR_CHECK_AIM_AND_DISTANCE;
		break;
	case KErrBadName: // IAS query class name failure
	case KErrBadHandle: // IAS query atrrib name failure
		topId=R_EIK_TBUF_IR_COULD_NOT_CONNECT;
		botId=R_EIK_TBUF_IR_REMOTE_INCOMPATIBLE;
		iNetDb.Close();
		break;
	case KErrNotSupported: // SIBO discovery
		topId=R_EIK_TBUF_IR_COULD_NOT_CONNECT;
		botId=R_EIK_TBUF_IR_REMOTE_INCOMPATIBLE;
		break;
	case KErrNoMemory:
		iEikonEnv->GetErrorText(topText,KErrNoMemory);
		break;
	default:
		iEikonEnv->GetErrorText(topText,aError);
		break;
		}
	if (topId)
		SetTopLabel(topId);
	else
		SetTopLabel(topText);
	if (botId)
		SetBottomLabel(botId);
	else if (botText.Length())
		SetBottomLabel(botText);
	else
		ClearBottomLabel();
	if (iType==ESender)
		MakeRecvImageVisible(EFalse);
	else
		MakeSendImageVisible(EFalse);

#if defined(_DEBUG)
	TBuf<20> buf;
	buf.Format(_L("Error number %d"), aError); // !! for now
	iEikonEnv->VerboseInfoMsg(buf);
#endif

	}

void CEikIrMain::ConstructL()
	{
	iActive=CEikIrActive::NewL(this);
	}

TInt CEikIrMain::LoadIrTinyTPProtocol() 
	{
	TProtocolDesc protoInfo;
	TUint numProtocols;
	TInt err=iSockServ.NumProtocols(numProtocols);
	if (err!=KErrNone)
		return err;
	err=iSockServ.FindProtocol(_L("IrTinyTP"), protoInfo);
	iAddrFamily=protoInfo.iAddrFamily;
	iSockType=protoInfo.iSockType;
	iProtocol=protoInfo.iProtocol;
	return err;
	}

TInt CEikIrMain::RegisterIASClasses()
	//
	// Register the appropriate IAS classes
	//
	{
	TInt err=iNetDb.Open(iSockServ, iAddrFamily, iProtocol);
	if (err)
		{
		HandleIrError(err);
		return err;
		}
	for (TInt ii=1;ii<=2;ii++)
		{
		TIASDatabaseEntry ent;
		ent.SetClassName(EIKON_IRDA_V1_IAS_CLASSNAME);
		TPtrC8 className;
		switch (ii)
			{
			case 1:
				className.Set(EIKON_IRDA_V1_IAS_CLASSNAME);
				break;
			case 2:
				className.Set(EIKON_IRDA_V2_IAS_CLASSNAME);
				break;
			}
		ent.SetClassName(className);
		ent.SetAttributeName(EIKON_IRDA_IAS_ATTRIBUTE);
		ent.SetToInteger(KEikIrDALsapSel);
		err=iNetDb.Add(ent);
		if (err)
			break;
		}
	iNetDb.Close();
	if (err)
		{
		HandleIrError(err);
		return err;
		}
	return KErrNone;
	}

void CEikIrMain::IASQueryVersion1(TRequestStatus& aStatus)
	//
	// Perform an IAS query for a matching version 1 EPOC machine
	//
	{
	if ((iActive->iVersion)!=1)
		{
		TInt err=iNetDb.Open(iSockServ, iAddrFamily, iProtocol);
		if (err)
			{
			TRequestStatus* pS=&aStatus;
			User::RequestComplete(pS, err);
			}
		}

	iIASQuery.Set(EIKON_IRDA_V1_IAS_CLASSNAME, EIKON_IRDA_IAS_ATTRIBUTE, iRemoteDevAddr);
	TPtrC8 className;
	switch (iActive->iVersion)
		{
	case 2:
		className.Set(EIKON_IRDA_V2_IAS_CLASSNAME);
		break;
	case 1:
		className.Set(EIKON_IRDA_V1_IAS_CLASSNAME);
		break;
		}
	iIASQuery.Set(className, EIKON_IRDA_IAS_ATTRIBUTE, iRemoteDevAddr);
	iNetDb.Query(iIASQuery,iIASResponse,aStatus);
	}

TInt CEikIrMain::IASResponseGetRemotePort()
	//
	// After successful IAS query, get remote LsapSel port from iIASResponse
	//
	{
	iNetDb.Close();
	TInt remPort;
	iIASResponse.GetInteger(remPort);
	return remPort;
	}

CEikProgressInfo* CEikIrMain::Progress() const
	{
	CEikProgressInfo* info=(CEikProgressInfo*)Control(EEikCidIrDialogProgInfo);
	return info;
	}

void CEikIrMain::MakeProgressBarVisible(TBool aVisible) 
	{
	MakeLineVisible(EEikCidIrDialogProgInfo, aVisible);
	}

void CEikIrMain::MakeSendImageVisible(TBool aVisible) 
	{
	CEikImageArray* imageArray=(CEikImageArray*)Control(EEikCidIrDialogImageArray);
	imageArray->MakeImageVisible(0, aVisible);
	}

void CEikIrMain::MakeRecvImageVisible(TBool aVisible) 
	{
	CEikImageArray* imageArray=(CEikImageArray*)Control(EEikCidIrDialogImageArray);
	imageArray->MakeImageVisible(1, aVisible);
	}

void CEikIrMain::SetTopLabel(TInt aResId)
	{
	TBuf<128> buf;
	iEikonEnv->ReadResource(buf, aResId);
	SetTopLabel(buf);
	}

void CEikIrMain::SetTopLabel(TDesC& aDes)
	{
	SetLabel(EEikCidIrDialogTopLabel, aDes);
	}

void CEikIrMain::SetBottomLabel(TInt aResId)
	{
	TBuf<128> buf;
	iEikonEnv->ReadResource(buf, aResId);
	SetBottomLabel(buf);
	}

void CEikIrMain::SetBottomLabel(TDesC& aDes)
	{
	SetLabel(EEikCidIrDialogBottomLabel, aDes);
	}

void CEikIrMain::ClearBottomLabel()
	{
	SetLabel(EEikCidIrDialogBottomLabel, _L(""));
	}

const CFont* CEikIrMain::BestFontForText(const TDesC& aDes)
	{
	const CFont* font=iEikonEnv->NormalFont();
	if ((font->TextWidthInPixels(aDes)>(iSize.iWidth-2*KIrDialogTextMargin)) && (IsActivated()))
		font=iEikonEnv->LegendFont();
	return font;
	}

void CEikIrMain::SetLabel(TInt aResId, const TDesC& aDes)
	{
	const CFont* font=BestFontForText(aDes);
	CEikLabel* label=(CEikLabel*)Control(aResId);
	label->SetFont(font);
	label->SetTextL(aDes); // won't actually leave.  label preallocates to 128 bytes
	label->DrawNow();
	}

void CEikIrMain::DisableRemoteLink()
	//
	// Disable the remote link if it's active
	//
	{
	iActive->iReEnableLink=EFalse;
	RSemaphore sem;
	RRemoteLink link;
	if(sem.OpenGlobal(REMOTELINK_CONFIG_SEMAPHORE)==KErrNone)
		{
		if(link.Open()==KErrNone)
			{
			link.Disable();
			iActive->iReEnableLink=ETrue;
			link.Close();
			}
		sem.Close();
		}
	}

void CEikIrMain::ReEnableRemoteLink()
	//
	// Re-enble the remote link at the previous settings 
	//
	{
	RRemoteLink link;
	TInt err=link.Open();
	if (err==KErrNone)
		{
		link.Enable(EBps115200, TPtrC(0,0), TPtrC(0,0)); // use previous stored settings
		link.Close();
		}
	}

//
// class CEikIrSender
//

EXPORT_C CEikIrSender::~CEikIrSender()
	{
	//iSender.CancelSend();
	iHostRslv.Close();
	iSender.Close();
	}

void CEikIrSender::HandleProgressUpdateL()
	{
	Progress()->SetAndDraw(iNumSent);
	}

void CEikIrSender::PreLayoutDynInitL()
	{
	MakeRecvImageVisible(EFalse);
	SetTitleL(R_EIK_TBUF_IR_SEND_TITLE);
	Progress()->SetFinalValue(iNumToSend);
	CEikIrMain::PreLayoutDynInitL();
	}

TInt CEikIrSender::HandleDiscovery(TRequestStatus &aStatus)
	{
	TInt err=aStatus.Int();
	if (err==KErrNone)
		{
		iHostRslv.Close();
		TIrdaSockAddr addr(iLog().iAddr);	// cast socket address to IrDA
		iRemoteDevAddr=addr.GetRemoteDevAddr();
		MakeRecvImageVisible(ETrue);
		SetBottomLabel(R_EIK_TBUF_IR_DISCOVERED);
		return KErrNone;
		}
	else if (err==KErrAlreadyExists)
		{
		SetTopLabel(R_EIK_TBUF_IR_IN_USE_TOP);	
		return KErrAlreadyExists;
		}
	if (++iDiscoverAttempts==numberOfDiscoveryAttempts)
		{
		SetTopLabel(R_EIK_TBUF_IR_NO_REMOTE_PSION);	// No remote psion found
		SetBottomLabel(R_EIK_TBUF_IR_CHECK_AIM_AND_DISTANCE);
		return KErrNotFound;
		}
	//	
	//	Play it again, Sam.
	iHostRslv.GetByName(iHostName, iLog, aStatus);
	return KErrNotReady;
	}

TInt CEikIrSender::Discover(TRequestStatus &aStatus)
	{
	// Discovery phase - single slot discovery - search for remote psion
	//
	TInt err=0;
	err=iSockServ.Connect();
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
 	//
	err=LoadIrTinyTPProtocol();
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}   
	//
	err=iSender.Open(iSockServ, iAddrFamily, iSockType, iProtocol);
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	//
	err=iHostRslv.Open(iSockServ, iAddrFamily, iProtocol);
	if (err==KErrNone)
		{
#if defined(__WINS__)
		TPckgBuf<TUint> slots(6);	// 6 discovery slot
#else
		TPckgBuf<TUint> slots(1);	// 1 discovery slot
#endif
		iSender.SetOpt(KDiscoverySlotsOpt,KLevelIrlap, slots); 
		iHostName=_S("*");
		iDiscoverAttempts=0;
		iHostRslv.GetByName(iHostName, iLog, aStatus);
		}
	return err;
	}

void CEikIrSender::Connect(TRequestStatus& aStatus)
	//
	// Connect to a discovered psion
	//
	{
	TInt err=0;
	// Discovery successful
	TIrdaSockAddr addr(iLog().iAddr);	// cast socket address to IrDA
	//
	TUint8 home=KEikIrDALsapSel;
	addr.SetPort(home);
	err=iSender.Bind(addr);	// Bind home port 8 to socket 1
	if (err!=KErrNone)
		{
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS, err);
		return;
		}
	addr.SetPort(iRemotePort);
	iSender.Connect(addr, aStatus);	// connect to remote port iRemotePort  with socket 1      
	}

void CEikIrSender::DoSendL(TRequestStatus& aStatus)
	{
	TUint packetSize=iBufPtr->MaxLength();
	TUint next=iNumSent+packetSize;
	if (next>iNumToSend)
		next=iNumToSend;

	ReadChunkL(next-iNumSent);
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	iSender.Write(*iBufPtr, aStatus);
	iNumSent=next;
	// An iteration would generally be a file
	// iNumToSend is the number of bytes in the files
	if (iNumSent==iNumToSend)
		{
		if (iActive->CurrentFile()>=iActive->NumOfFiles()-1)
			iActive->TransferComplete();
		else 
			iActive->NextFile();
		}
	}

void CEikIrSender::RequestDisconnectIndication(TRequestStatus& aStatus)
	{
	iSender.Ioctl(KDisconnectIndicationIoctl, aStatus, iBufPtr, iAddrFamily);
	}

void CEikIrSender::AllocatePacketBufferL()
	//
	// allocates iBuf to optimum size for the link
	//
	{
	TPckgBuf<TUint> size;
	iSender.GetOpt(KRemoteMaxDataSizeOpt, KLevelIrlap, size); 
	TInt len=size();

	iBuf=HBufC8::NewL(len);
	iBufPtr=new(ELeave) TPtr8(iBuf->Des()); // having to do this sucks
	}

void CEikIrSender::NegotiationComplete()
	{
	MakeProgressBarVisible(ETrue);
	ClearBottomLabel();
	iActive->NegotiationComplete();
	}

TBool CEikIrSender::ParseAck() const
	//
	// Ensures *iBufPtr contains an ACK and determines whether it's +ve (Y) or -ve (N)
	//
	{
	TChar delimiter=' ';
	TInt pos=iBufPtr->Locate(delimiter);
	if (pos!=KErrNotFound)
		{
		TPtrC8 ack=iBufPtr->Left(pos);
		if (ack.CompareF(_L8("ACK"))!=0)
			Panic(EEikIrPanicInvalidAck);
		TPtrC8 truth=iBufPtr->Right(1);
		if (truth.CompareF(_L8("Y"))==0)
			return ETrue;
		}
	return EFalse;
	}

//
// class CEikIrReceiver
//

EXPORT_C CEikIrReceiver::~CEikIrReceiver()
	{
	//iReader.CancelRead();
	//iListener.CancelAccept();
	iReader.Close();
	iListener.Close();
	}

void CEikIrReceiver::HandleProgressUpdateL()
	{
	Progress()->SetAndDraw(iNumRecvd);
	}

void CEikIrReceiver::PreLayoutDynInitL()
	{
	SetTitleL(R_EIK_TBUF_IR_RECEIVE_TITLE);
	MakeSendImageVisible(EFalse);
	CEikIrMain::PreLayoutDynInitL();
	}

TInt CEikIrReceiver::Initialize()
	{
	// Listen for discovery
	TInt err=0;
	err=iSockServ.Connect();
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	//
	TUint8 home=KEikIrDALsapSel;
	err=LoadIrTinyTPProtocol();
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	err=iListener.Open(iSockServ, iAddrFamily, iSockType, iProtocol);
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	TSockAddr addr;
	addr.SetPort(home);
	err=iListener.Bind(addr);
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	err=iListener.Listen(5);	// listen for an attempted connection
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}

	err=iReader.Open(iSockServ);	// open null socket to hold connection
	if (err!=KErrNone)
		{
		HandleIrError(err);
		return err;
		}
	return KErrNone;
	}

void CEikIrReceiver::WaitToBeDiscovered(TRequestStatus& aStatus)
	{
	iListener.Ioctl(KDiscoveryIndicationIoctl,aStatus,&iLog,iAddrFamily);
	}

void CEikIrReceiver::WaitForConnect(TRequestStatus& aStatus)
	{
	const TPtrC SienaName = _L("Psion Siena");
	const TPtrC Series3Name = _L("Psion Series3");
	THostName Temp=iLog().iName;
	//
//	if (iLog().iName.PtrZ()==_S("Psion Series3"))
	if (Temp.FindF(SienaName)!=KErrNotFound || Temp.FindF(Series3Name)!=KErrNotFound) 
		{// Can't connect to Series3 or Siena
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS,KErrNotSupported);
		}
	else
		iListener.Accept(iReader, aStatus);
	}

void CEikIrReceiver::DoReceiveL(TRequestStatus& aStatus)
	{
	// iBuf contains some data
	TUint packetSize=iBufPtr->MaxLength();
	iNumRecvd=iNumRecvd+packetSize;
	if (iNumRecvd>iNumToRecv)
		iNumRecvd=iNumToRecv;
	WriteChunkL();
	if (iNumRecvd==iNumToRecv)
		{
		if (iActive->CurrentFile() >= iActive->NumOfFiles()-1)
			iActive->TransferComplete();
		else 
			iActive->NextFile();
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS, KErrNone);
		}
	else
		{
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);	// read next chunk
		}
	}

void CEikIrReceiver::AllocatePacketBufferL()
	//
	// allocates iBuf to optimum size for the link
	//
	{
	TPckgBuf<TUint> size;
	iReader.GetOpt(KRemoteMaxDataSizeOpt, KLevelIrlap, size); // !! should really be Host - bug in IRDA
	TInt len=size();

	iBuf=HBufC8::NewL(len);
	iBufPtr=new(ELeave) TPtr8(iBuf->Des()); // having to do this sucks
	}

void CEikIrReceiver::NegotiationComplete()
	{
	MakeProgressBarVisible(ETrue);
	ClearBottomLabel();
	iActive->NegotiationComplete();
	}

void CEikIrReceiver::ReadNumToRecvFromDes(const TDesC8& aDes)
	{
	TLex8 lex(aDes);
	lex.Val(iNumToRecv);
	}

//
// class CEikIrDataSender
//

EXPORT_C CEikIrDataSender::~CEikIrDataSender()
	{
	}

EXPORT_C CEikIrDataSender* CEikIrDataSender::NewL()
	// static
	{
	CEikIrDataSender* This=new(ELeave) CEikIrDataSender;
	CleanupStack::PushL(This);
	This->ConstructL();
	CleanupStack::Pop();
	return This;
	}

EXPORT_C void CEikIrDataSender::SendLD(CBufBase* aData)
	{
	iNumToSend=aData->Size();
	iNumSent=0;
	iData=aData;
	ExecuteLD(R_EIK_IR_DIALOG);
	}

void CEikIrDataSender::PreLayoutDynInitL()
	{
	CEikIrSender::PreLayoutDynInitL();
	SetTopLabel(R_EIK_TBUF_IR_READY_TO_SEND_DATA);
	SetBottomLabel(R_EIK_TBUF_IR_LOOKING_FOR_PSION);
	}

TBool CEikIrDataSender::NegotiateL(TRequestStatus& aStatus)
	//
	// Send number of following bytes
	//
	{
	switch (iNegStage)
		{
	case ESendSize:
		iBufPtr->Format(_L8("DATA %d"), iNumToSend);
		iNegStage=ESizeSentQueueReadAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Write(*iBufPtr, aStatus);
		break;
	case ESizeSentQueueReadAck:
		iNegStage=EAckReceived;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Read(*iBufPtr, aStatus);
		break;
	case EAckReceived:
		TBool proceed=ParseAck();
		if (!proceed)
			{
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_NOT_RECEIVING_TEXT);
			return EFalse;
			}
		SetTopLabel(R_EIK_TBUF_IR_SENDING_DATA);
		NegotiationComplete();
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS, KErrNone);
		break;
		}
	return ETrue;
	}

void CEikIrDataSender::ReadChunkL(TInt aLength)
	//
	// read next chunk into buffer before sending it - won't actually leave in this case
	//
	{
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	iData->Read(iNumSent, *iBufPtr, aLength);
	}

//
// class CEikIrDataReceiver
//

EXPORT_C CEikIrDataReceiver::~CEikIrDataReceiver()
	{
	}

EXPORT_C CEikIrDataReceiver* CEikIrDataReceiver::NewL()
	// static
	{
	CEikIrDataReceiver* This=new(ELeave) CEikIrDataReceiver;
	CleanupStack::PushL(This);
	This->ConstructL();
	CleanupStack::Pop();
	return This;
	}

EXPORT_C void CEikIrDataReceiver::ReceiveLD(CBufBase* aData)
	{
	iNumRecvd=0;
	iNumToRecv=0x7f;
	iData=aData;  
	iData->Reset();
	ExecuteLD(R_EIK_IR_DIALOG);
	}

void CEikIrDataReceiver::PreLayoutDynInitL()
	{  
	CEikIrReceiver::PreLayoutDynInitL();
	SetTopLabel(R_EIK_TBUF_IR_READY_TO_RECEIVE_DATA);
	SetBottomLabel(R_EIK_TBUF_IR_AIM_PSIONS);
	}

TBool CEikIrDataReceiver::NegotiateL(TRequestStatus& aStatus)
	//
	// queue read for number of following bytes
	//
	{
	switch (iNegStage)
		{
	case EQueueReadNumToRecv:
		iNegStage=EGotNumToRecvSendAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		break;
	case EGotNumToRecvSendAck:
		{
		TBool proceed=ParseFirstPacket();
		if (!proceed)
			{
			iBufPtr->Copy(_L("ACK N"));
			__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
			iReader.Write(*iBufPtr, aStatus);
			iAckNSent=ETrue;
			}
		else
			{
			iBufPtr->Copy(_L("ACK Y"));
			__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
			iReader.Write(*iBufPtr, aStatus);
			}
		}
		iNegStage=EAckSent;
		break;
	case EAckSent:
		if (iAckNSent)
			return EFalse;
		iData->ResizeL(iNumToRecv);
		SetTopLabel(R_EIK_TBUF_IR_RECEIVING_DATA);
		NegotiationComplete();
		Progress()->SetFinalValue(iNumToRecv);
		// NegotiateL MUST queue the next read on the socket
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		break;
		}
	return ETrue;
	}

void CEikIrDataReceiver::WriteChunkL()
	{
	TInt length=iBufPtr->Length();
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	iData->Write(iNumRecvd-length, *iBufPtr, length);
	}

TBool CEikIrDataReceiver::ParseFirstPacket()
	//
	// Checks packet to ensure data is being sent (i.e not a file or anything else)
	// Also reads number of bytes being sent
	//
	{
	TChar delimiter=' ';
	TInt pos=iBufPtr->Locate(delimiter);
	TPtrC8 dataType=iBufPtr->Left(pos);
	if (dataType.CompareF(_L8("DATA"))!=0)
		{
		if (dataType.CompareF(_L8("FILE"))==0)
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_FILE);
		else
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_UNSUPPORTED);
		return EFalse;
		}
	TPtrC8 numToRecv=iBufPtr->Right(iBufPtr->Length()-pos-1);
	ReadNumToRecvFromDes(numToRecv);
	return ETrue;
	}

//
// class CEikIrFileSender
//

EXPORT_C CEikIrFileSender::~CEikIrFileSender()
	{
	iFile.Close();
	delete iTargetPath;
	}

EXPORT_C CEikIrFileSender* CEikIrFileSender::NewL()
	// static
	{
	CEikIrFileSender* This=new(ELeave) CEikIrFileSender;
	CleanupStack::PushL(This);
	This->ConstructL();
	CleanupStack::Pop();
	return This;
	}

EXPORT_C void CEikIrFileSender::SendLD(const TFullName& aFileName, const TDesC* aTargetPath)
	{
	CleanupStack::PushL(this);
	if (aTargetPath)
		CopyTargetPathL(aTargetPath);
	iParse.Set(aFileName, NULL, NULL);
	iFs=&(iEikonEnv->FsSession());
	TEntry entry;
	if (iFs->Entry(aFileName,entry)!=KErrNone)
		User::Leave(KErrNotFound);
	iNumToSend=entry.iSize;
	User::LeaveIfError(iFile.Open(*iFs, aFileName, 0));
	CleanupStack::Pop(); // this
	ExecuteLD(R_EIK_IR_DIALOG);
	}

void CEikIrFileSender::CopyTargetPathL(const TDesC* aTargetPath)
	//
	// Allocate and copy target path to iTargetPath
	//
	{
	__ASSERT_DEBUG(aTargetPath, Panic(EEikIrPanicNullTargetPath));
	iTargetPath=aTargetPath->AllocL();
	}

void CEikIrFileSender::PreLayoutDynInitL()
	{
	CEikIrSender::PreLayoutDynInitL();
	TBuf<50> resBuf;
	iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_READY_TO_SEND_FILE);
	HBufC* buf=HBufC::NewLC(KMaxFileName+50);
	TPtr des=buf->Des();
	TPtrC nameAndExt(iParse.NameAndExt());
	des.Format(resBuf, &nameAndExt);
	TextUtils::ClipToFit(des, *(iEikonEnv->NormalFont()), Progress()->MinimumSize().iWidth);
	SetTopLabel(*buf);
	CleanupStack::PopAndDestroy(); // buf
	SetBottomLabel(R_EIK_TBUF_IR_LOOKING_FOR_PSION);
	}

TBool CEikIrFileSender::NegotiateL(TRequestStatus& aStatus)
	{
#if defined(_UNICODE)
	TBuf8<KMaxFileName> unicodePathTruncatedToNarrow;
#endif
	switch (iNegStage)
		{
	case ESendFileNameAndDetails:
		{
		TEntry entry;
		iFs->Entry(iParse.FullName(),entry);
		TInt64 time=entry.iModified.Int64();
		TPtrC remotePath=(iTargetPath)? (TPtrC)(iTargetPath->Des()) : iParse.NameAndExt();
#if defined(_UNICODE)
		unicodePathTruncatedToNarrow.Copy(remotePath);
		iBufPtr->Format(_L8("FILE %d %d %u %u %S"), entry.iSize, entry.iAtt, time.High(), time.Low(), &unicodePathTruncatedToNarrow);
#else
		iBufPtr->Format(_L("FILE %d %d %u %u %S"), entry.iSize, entry.iAtt, time.High(), time.Low(), &remotePath);
#endif
		}
		iNegStage=EFileNameAndDetailsSentQueueReadAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Write(*iBufPtr, aStatus);
		break;
	case EFileNameAndDetailsSentQueueReadAck:
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Read(*iBufPtr, aStatus);
		iNegStage=EAckReceived;
		break;
	case EAckReceived:
		{
		TBool proceed=ParseAck();
		if (!proceed)
			{
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_NOT_RECEIVING_FILE);
			return EFalse; 
			}
		TBuf<50> resBuf;
		iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_SENDING_FILE);
#if defined(_UNICODE)
		HBufC* messageText=HBufC::NewLC(resBuf.Length()+iParse.NameAndExt().Length());
		TPtr messageTextPointer=messageText->Des();
		TPtrC nameAndExt(iParse.NameAndExt());
		messageTextPointer.Format(resBuf, &nameAndExt);
		TextUtils::ClipToFit(messageTextPointer, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(messageTextPointer);
		CleanupStack::PopAndDestroy(); // messageText
#else
		TPtrC nameAndExt(iParse.NameAndExt());
		iBufPtr->Format(resBuf, &nameAndExt);
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		TextUtils::ClipToFit(*iBufPtr, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(*iBufPtr);
#endif
		NegotiationComplete();
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS, KErrNone);
		}
		break;
		}
	return ETrue;
	}

void CEikIrFileSender::ReadChunkL(TInt aLength)
	//
	// Read the next chunk from the file
	//
	{
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	User::LeaveIfError(iFile.Read(iNumSent, *iBufPtr, aLength));
	}

//
// class CEikIrFileReceiver
//

EXPORT_C CEikIrFileReceiver::~CEikIrFileReceiver()
	{
	iFile.Close();
	delete iRemoteFileName;
	}

EXPORT_C CEikIrFileReceiver* CEikIrFileReceiver::NewL()
	// static
	{
	CEikIrFileReceiver* This=new(ELeave) CEikIrFileReceiver;
	CleanupStack::PushL(This);
	This->ConstructL();
	CleanupStack::Pop();
	return This;
	}

EXPORT_C void CEikIrFileReceiver::ReceiveLD(const TDesC& aPath)
	{
/*
	A valid path must:
		- specify a drive
		- fully specify an existing directory
		- end in a backslash
		- not specify a file
*/
	iFs=&(iEikonEnv->FsSession());
	CleanupStack::PushL(this);
	User::LeaveIfError(iParse.Set(_L("*.*"), &aPath, NULL));
	TVolumeInfo volInfo;
	TInt driveId;
	User::LeaveIfError(RFs::CharToDrive(aPath[0], driveId));
	iFs->Volume(volInfo, driveId);
	TMediaType mediaType=volInfo.iDrive.iType;
	TInt mediaAtt=volInfo.iDrive.iMediaAtt;
	if (mediaAtt&KMediaAttWriteProtected)
		mediaType=EMediaRom;
	switch (mediaType)
		{
	case EMediaNotPresent:
	case EMediaCdRom:
	case EMediaRom:
		{
		TPtrC drive=aPath.Left(1);
		iEikonEnv->InfoMsg(R_EIK_TBUF_IR_DISK_IS_ROM, &drive);
		CleanupStack::Pop(); // this
		return;
		}
		break;
	default:
		break;
		}
	if (!(EikFileUtils::PathExists(aPath)))
		User::Leave(KErrPathNotFound);
	CleanupStack::Pop(); // this
	ExecuteLD(R_EIK_IR_DIALOG);
	}

EXPORT_C void CEikIrFileReceiver::ReceiveLD()
	//
	// Receive a file assuming a full target path is being sent (i.e non NULL aTargetPath passed to CEikIrFileSender::SendLD() ) 
	//
	{
	iFs=&(iEikonEnv->FsSession());
	iFileRecFlags|=EFullPathNameSupplied;
	ExecuteLD(R_EIK_IR_DIALOG);
	}

EXPORT_C void CEikIrFileReceiver::FileName(TDes& aFileName) const
	//
	// Copies the filename of the received file into aFileName
	//
	{
	TPtrC fileName=iParse.FullName();
	aFileName.Copy(fileName); // callers responsibility for overflow
	}

void CEikIrFileReceiver::HandleTransferCompleteL()
	{
	// transfer complete - apply timestamp and attributes
	iFile.Close();
	TFileName newFileName=iParse.FullName();
	TBool fileSaved=ETrue;
	if (iFileRecFlags&ETempNameUsed)
		{
		fileSaved=QueryRenameFileL(newFileName);
		DrawNow();
		}
	if (fileSaved)
		User::LeaveIfError(iFs->SetEntry(newFileName, iFileModified, iFileAtt, 0));
	}

TBool CEikIrFileReceiver::QueryRenameFileL(TFileName& aNewFileName)
	{
	// aNewFileName contents useless, but cuts down on stack usage
	TInt maxWidth=iEikonEnv->ScreenDevice()->SizeInPixels().iWidth-50;
	FindUniqueFileNameL(aNewFileName);
	TBuf<64> temp;
	iEikonEnv->ReadResource(temp, R_EIK_TBUF_IR_RECEIVED_FILE_EXISTS);
	HBufC* titleBuf=HBufC::NewLC(KMaxFileName+64);
	TPtr title=titleBuf->Des();
	title.Format(temp, iRemoteFileName);
	TextUtils::ClipToFit(title, *(iEikonEnv->NormalFont()), maxWidth);
	TInt err=0;
	do
		{
		CEikFileSaveAsDialog* dialog=new(ELeave) CEikFileSaveAsDialog(&aNewFileName, &title, NULL, EFalse);
		if (dialog->ExecuteLD(R_EIK_DIALOG_FILE_SAVEAS))
			{
			err=KErrNone;
			if (ConeUtils::FileExists(aNewFileName))
				err=iFs->Delete(aNewFileName);
			if (err==KErrInUse)
				{
				TParsePtrC parse(aNewFileName);
				TPtrC name=parse.NameAndExt();
				iEikonEnv->ReadResource(temp, R_EIK_TBUF_FILE_IN_USE);
				HBufC* msgBuf=HBufC::NewLC(KMaxFileName+64);
				TPtr msg=msgBuf->Des();
				msg.Format(temp, &name);
				TextUtils::ClipToFit(msg, *(iEikonEnv->NormalFont()), maxWidth);
				iEikonEnv->AlertWin(msg,_L(""));
				CleanupStack::PopAndDestroy(); // msgBuf
				}
			else
				{
				err=iFs->Rename(iParse.FullName(), aNewFileName);
				if (!err)
					err=iFs->SetAtt(aNewFileName, 0, KEntryAttHidden);
				User::LeaveIfError(err);
				iEikonEnv->InfoMsg(R_EIK_TBUF_SAVED);
				}
			}
		else
			{
			User::LeaveIfError(iFs->Delete(iParse.FullName()));
			iEikonEnv->InfoMsg(R_EIK_TBUF_IR_RECEIVED_FILE_DISCARDED);
			CleanupStack::PopAndDestroy(); // titleBuf
			return EFalse;
			}
		} while (err!=KErrNone);
	CleanupStack::PopAndDestroy(); // titleBuf
	return ETrue;
	}

void CEikIrFileReceiver::PreLayoutDynInitL()
	{  
	CEikIrReceiver::PreLayoutDynInitL();
	SetTopLabel(R_EIK_TBUF_IR_READY_TO_RECEIVE_FILE);
	SetBottomLabel(R_EIK_TBUF_IR_AIM_PSIONS);
	}

TBool CEikIrFileReceiver::NegotiateL(TRequestStatus& aStatus)
	{
	switch (iNegStage)
		{
	case EQueueReadFileNameAndDetails:
		iNegStage=EGotFileNameAndDetailsSendAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());		
		iReader.Read(*iBufPtr, aStatus);
		break;
	case EGotFileNameAndDetailsSendAck:
		{
		// *iBufPtr contains remote (sending) filename and extension (no path) & details
		TBool proceed=ParseFirstPacketL();
		if (proceed)
			proceed=CheckFreeSpaceL();
		if (!proceed)
			{
			iBufPtr->Copy(_L("ACK N"));
			__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
			iReader.Write(*iBufPtr, aStatus);
			iFileRecFlags|=EAckNSent;
			}
		else
			{
			iBufPtr->Copy(_L("ACK Y"));
			__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
			iReader.Write(*iBufPtr, aStatus);
			}
		iNegStage=EAckSent;
		}
		break;
	case EAckSent:
		if (iFileRecFlags&EAckNSent)
			return EFalse;
		if (CheckFileExists())
			{
			TPtrC driveAndPath=iParse.DriveAndPath();
			TFileName fileName;
			User::LeaveIfError(iFile.Temp(*iFs, driveAndPath, fileName, EFileWrite));
			iFileRecFlags|=EFileOpened;
			User::LeaveIfError(iFile.SetAtt(KEntryAttHidden, 0)); // hide temp files
			iParse.Set(fileName, &driveAndPath, NULL);
			}
		else
			{
			User::LeaveIfError(iFile.Replace(*iFs, iParse.FullName(), EFileWrite));
			iFileRecFlags|=EFileOpened;
			}
		TBuf<50> resBuf;
		iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_RECEIVING_FILE);
#if defined(_UNICODE)
		HBufC* messageText=HBufC::NewLC(resBuf.Length()+iRemoteFileName->Des().Length());
		TPtr messageTextPointer=messageText->Des();
		TPtr remoteFileName((iRemoteFileName->Des()));
		messageTextPointer.Format(resBuf, &remoteFileName);
		TextUtils::ClipToFit(messageTextPointer, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(messageTextPointer);
		CleanupStack::PopAndDestroy(); // messageText
#else
		TPtr ptr=iRemoteFileName->Des();
		iBufPtr->Format(resBuf, &ptr);
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		TextUtils::ClipToFit(*iBufPtr, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(*iBufPtr);
#endif
		NegotiationComplete();
		Progress()->SetFinalValue(iNumToRecv);
		// !! following line is a workaround a very bizarre bug in (probably) IRDA
		// Depending on the size of iBufPtr, sometimes only that amount of data is read
		// instead of the Maximum size of the buffer
		iBufPtr->Zero();
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		// NegotiateL MUST queue the next read on the socket
		break;
		}
	return ETrue;
	}

void CEikIrFileReceiver::WriteChunkL()
	{
	TInt length=iBufPtr->Length();
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	User::LeaveIfError(iFile.Write(iNumRecvd-length, *iBufPtr, length));
	}

TBool CEikIrFileReceiver::ParseFirstPacketL()
	//
	// Checks packet to ensure a file is being sent (i.e not data or anything else)
	// Also reads filename being sent
	//
	// !! Some opportunity for code sharing here with CEikIrDataReceiver
	{
	TChar delimiter=' ';
	TInt pos=iBufPtr->Locate(delimiter);
	TPtrC8 dataType=iBufPtr->Left(pos);
	if (dataType.CompareF(_L8("FILE"))!=0)
		{
		if (dataType.CompareF(_L8("DATA"))==0)
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_TEXT);
		else
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_UNSUPPORTED);
		return EFalse;
		}
	TPtrC8 fileDetails=iBufPtr->Right(iBufPtr->Length()-pos-1);
	ParseFileNameAndDetailsL(fileDetails);
	return ETrue;
	}

void CEikIrFileReceiver::ParseFileNameAndDetailsL(const TDesC8& aNameAndDetails)
	//
	// Extract file name, size (iNumToRecv), attributes and time stamp from aDetails
	//
	{
	TChar delimiter=' ';
	TInt pos=0;
	pos=aNameAndDetails.Locate(delimiter);	// end of file size
	TLex8 lex(aNameAndDetails.Left(pos));
	lex.Val(iNumToRecv);  // read file size

	TPtrC8 next(aNameAndDetails.Right(aNameAndDetails.Length()-pos-1));
	pos=next.Locate(delimiter);	// end of file att's
	lex.Assign(next.Left(pos));
	lex.Val(iFileAtt);	// read file att's

	TPtrC8 next2=next.Right(next.Length()-pos-1);
	pos=next2.Locate(delimiter);	// end of time high 
	lex.Assign(next2.Left(pos));
	TUint high;
	lex.Val(high); // read time high

	TPtrC8 next3=next2.Right(next2.Length()-pos-1);
	pos=next3.Locate(delimiter);
	lex.Assign(next3.Left(pos));
	TUint low;
	lex.Val(low); // read time low
	TInt64 time(high, low);
	iFileModified=TTime(time);

	// MUST do filename last since it can contain spaces
#if defined(_UNICODE)
	TPtrC8 truncatedNarrowFileName=next3.Right(next3.Length()-pos-1);
	TFileName fileName;
	fileName.Copy(truncatedNarrowFileName);
#else
	TPtrC fileName=next3.Right(next3.Length()-pos-1); // read filename
#endif
	if (iFileRecFlags&EFullPathNameSupplied)
		{
		iParse.Set(fileName, NULL, NULL);
		TPtrC driveAndPath=iParse.DriveAndPath();
		TInt err=iFs->MkDirAll(driveAndPath); // ensure dest path exists
		if (err!=KErrNone && err!= KErrAlreadyExists)
			User::Leave(err);
		}
	else
		{
		TPtrC driveAndPath=iParse.DriveAndPath();
		iParse.Set(fileName, &driveAndPath, NULL);
		}
	iRemoteFileName=(iParse.NameAndExt()).AllocL();
	}

TBool CEikIrFileReceiver::CheckFreeSpaceL()
	//
	// Checks to see if the destination drive has enough free space for the file
	//
	{
	// get volume info
	TVolumeInfo info;
	TDriveUnit drive(iParse.Drive());
	User::LeaveIfError(iFs->Volume(info, TInt(drive)));
	TBool enoughSpace=(TInt64)iNumToRecv<info.iFree;
	if (!enoughSpace)
		{
		TBuf<32> topText;
		iEikonEnv->GetErrorText(topText,KErrDiskFull);
		SetTopLabel(topText);
		ClearBottomLabel();
		}
	return enoughSpace;
	}

void CEikIrFileReceiver::FindUniqueFileNameL(TFileName& aName)
	{
	// temp filename being used
	const TInt KUniqueNameLeftDelimiter='(';
	const TInt KUniqueNameRightDelimiter=')';
	// first find base name
	aName=iParse.DriveAndPath();
	aName.Append(*iRemoteFileName);
	TParsePtrC parse(aName);
	TInt len=parse.Name().Length();
	TInt pos=parse.Name().LocateReverse(KUniqueNameLeftDelimiter);
	if (pos>0 && (len-pos)>=4)
		{
		if (parse.Name()[len-1]==KUniqueNameRightDelimiter)
			{
			for (TInt i=pos+1;i<len-1;i++)
				{
				TChar ch=parse.Name()[i];
				if (!ch.IsDigit())
					return;
				}
			aName.Delete(parse.DriveAndPath().Length()+pos,len-pos);
			}
		}
	User::LeaveIfError(CApaApplication::GenerateFileName(*iFs, aName));
	}

TBool CEikIrFileReceiver::CheckFileExists()
	{
	iFileRecFlags&=(~ETempNameUsed);
	if (ConeUtils::FileExists(iParse.FullName()))
		{
		iFileRecFlags|=ETempNameUsed;
		return ETrue;
		}
	return EFalse;
	}

void CEikIrFileReceiver::HandleUserCancelL()
	//
	// Remove a partially transferred file after a user selects Cancel
	//
	{
	if (iFileRecFlags&EFileOpened)
		{
		iFile.Close();
		User::LeaveIfError(iFs->Delete(iParse.FullName()));
		}
	}

//
// class CEikIrMultipleFileSender
//

EXPORT_C CEikIrMultipleFileSender::~CEikIrMultipleFileSender()
	{
	iFile.Close();
	delete iFileNameArray;
	}

EXPORT_C CEikIrMultipleFileSender* CEikIrMultipleFileSender::NewL()
	{ // static
	CEikIrMultipleFileSender* This=new(ELeave) CEikIrMultipleFileSender;
	CleanupStack::PushL(This);
	This->ConstructMultipleFileSenderL();
	CleanupStack::Pop(); // This
	return This;
	}

EXPORT_C void CEikIrMultipleFileSender::SendLD(CDesCArraySeg* aFileNameArray,TInt aEditLength)
	{
	CleanupStack::PushL(this);
	iEditLength=aEditLength;
	iFileNameArray=aFileNameArray;
		
	iFs=&(iEikonEnv->FsSession());
	TEntry entry;

	iNumOfFiles=aFileNameArray->Count();
	iActive->SetNumOfFiles(iNumOfFiles);
	// calc iTotalBytesInMultipleFiles with loop
	for (TInt ii=0;ii<iNumOfFiles;ii++)
		{
		if (iFs->Entry((*aFileNameArray)[ii],entry)!=KErrNone)
			User::Leave(KErrNotFound);
		iTotalBytesInMultipleFiles+=entry.iSize;
		}

	TFullName fileName;
	fileName=(*iFileNameArray)[iActive->CurrentFile()];
	iParse.Set(fileName, NULL, NULL);
	if (iFs->Entry(fileName,entry)!=KErrNone)
		User::Leave(KErrNotFound);
	iNumToSend=entry.iSize;// first file
	User::LeaveIfError(iFile.Open(*iFs, fileName, 0));

	CleanupStack::Pop(); // this
	ExecuteLD(R_EIK_IR_DIALOG);
	}

CEikIrMultipleFileSender::CEikIrMultipleFileSender() 
	: CEikIrSender(),iNegStage(ESendNumberOfFiles), iTotalBytesInMultipleFilesTransferred(0), iLastFile(0)
	{}

void CEikIrMultipleFileSender::ConstructMultipleFileSenderL()
	{
	CEikIrMain::ConstructL();
	iV1Receiver=EFalse;
	}

void CEikIrMultipleFileSender::HandleProgressUpdateL()
	{
	TUint next=iNumSent+iBufPtr->MaxLength();
	if (next > iNumToSend)
		next=iNumToSend;
	TInt chunk=next-iNumSent;

	iTotalBytesInMultipleFilesTransferred+=chunk;
	Progress()->SetAndDraw(iTotalBytesInMultipleFilesTransferred);
	}

void CEikIrMultipleFileSender::CopyTargetPathL(const TDesC* aTargetPath)
	//
	// Allocate and copy target path to iTargetPath
	//
	{
	__ASSERT_DEBUG(aTargetPath, Panic(EEikIrPanicNullTargetPath));
	}

void CEikIrMultipleFileSender::PreLayoutDynInitL()
	{
	CEikIrSender::PreLayoutDynInitL();
	Progress()->SetFinalValue(iTotalBytesInMultipleFiles);// !!
	TBuf<50> resBuf;
	iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_READY_TO_SEND_FILE);
	HBufC* buf=HBufC::NewLC(KMaxFileName+50);
	TPtr des=buf->Des();
	TPtrC nameAndExt(iParse.NameAndExt());
	des.Format(resBuf, &nameAndExt);
	TextUtils::ClipToFit(des, *(iEikonEnv->NormalFont()), Progress()->MinimumSize().iWidth);
	SetTopLabel(*buf);
	CleanupStack::PopAndDestroy(); // buf
	SetBottomLabel(R_EIK_TBUF_IR_LOOKING_FOR_PSION);
	}

TBool CEikIrMultipleFileSender::NegotiateL(TRequestStatus& aStatus)
	{
	if (iLastFile < iActive->CurrentFile())
		{
		iNegStage=ESendFileNameAndDetails;
		// set iParse with the next file name to be transferred
		TInt fileNumber=iActive->CurrentFile();
		iParse.Set((*iFileNameArray)[fileNumber], NULL, NULL);
		TEntry entry;
		if (iFs->Entry((*iFileNameArray)[fileNumber],entry)!=KErrNone)
			User::Leave(KErrNotFound);

		if (iFile.Open(*iFs, (*iFileNameArray)[fileNumber], 0)!=KErrNone)
			{
			TBuf<64> temp;
			iEikonEnv->ReadResource(temp, R_EIK_TBUF_IR_OPEN_FILE);
			TBuf<KMaxFileName+64> titleBuf;
			TPtrC filename((*iFileNameArray)[fileNumber]);
			titleBuf.Format(temp, &filename);
			SetTopLabel(titleBuf);
			TPtrC blank;
			SetBottomLabel(blank);
			return EFalse;
			}
		iNumToSend=entry.iSize;// size of next file
		iNumSent=0;
		}
	iLastFile=iActive->CurrentFile();

	switch (iNegStage)
		{
	case ESendNumberOfFiles:
		{
		if (iActive->Version()==1)
			{
			// sending from a version2 to a version 1 infrared machine
			// version 1 does not support multiple beaming
			// v1 can not make sense of MULTIPLEFILES 
			iV1Receiver=ETrue;
			iNegStage=ESendFileNameAndDetails;
			iActive->SetNumOfFiles(1);
			iActive->SetCurrentFile(0);
			TRequestStatus* pS=&aStatus;
			User::RequestComplete(pS, KErrNone);
			break;
			}
		else if (iActive->Version()==2)
			iBufPtr->Format(_L8("MULTIPLEFILES %u %u"), iNumOfFiles,iTotalBytesInMultipleFiles );
		iNegStage=EReadNumOfFilesAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Write(*iBufPtr, aStatus);
		}
		break;
	case EReadNumOfFilesAck:
		{
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Read(*iBufPtr, aStatus);
		iNegStage=ENumOfFilesAckReceived;
		}
		break;
	case ENumOfFilesAckReceived:
		{
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		TBool proceed=ParseAck();
		if (proceed)
			{
			iNegStage=ESendFileNameAndDetails;
			TRequestStatus* pS=&aStatus;
			User::RequestComplete(pS, KErrNone);
			}
		else
			{
			SetBottomLabel(R_EIK_TBUF_IR_REMOTE_INCOMPATIBLE);
			return EFalse;
			}
		}
		break;
		// the above switches are only used on the first file
	case ESendFileNameAndDetails:
		{
#if defined(_UNICODE)
		TBuf8<KMaxFileName> unicodePathTruncatedToNarrow;
#endif
		TEntry entry;
		iFs->Entry(iParse.FullName(),entry);
		TInt64 time=entry.iModified.Int64();
		TFullName remotePath;
		if (iV1Receiver)
			remotePath = iParse.NameAndExt();
		else
			{
			remotePath = iParse.FullName();
			MergeFullPaths(remotePath,iEditLength);
			}
			
#if defined(_UNICODE)
		unicodePathTruncatedToNarrow.Copy(remotePath);
		iBufPtr->Format(_L8("FILE %d %d %u %u %S"), entry.iSize, entry.iAtt, time.High(), time.Low(), &unicodePathTruncatedToNarrow);
#else
		iBufPtr->Format(_L("FILE %d %d %u %u %S"), entry.iSize, entry.iAtt, time.High(), time.Low(), &remotePath);
#endif
		}
		iNegStage=EFileNameAndDetailsSentQueueReadAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Write(*iBufPtr, aStatus);
		break;
	case EFileNameAndDetailsSentQueueReadAck:
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iSender.Read(*iBufPtr, aStatus);
		iNegStage=EAckReceived;
		break;
	case EAckReceived:
		{
		TBool proceed=ParseAck();
		if (!proceed)
			{
			SetBottomLabel(R_EIK_TBUF_IR_OTHER_NOT_RECEIVING_FILE);
			return EFalse; 
			}
		TBuf<50> resBuf;
		iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_SENDING_FILE);
#if defined(_UNICODE)
		HBufC* messageText=HBufC::NewLC(resBuf.Length()+iParse.NameAndExt().Length());
		TPtr messageTextPointer=messageText->Des();
		TPtrC nameAndExt(iParse.NameAndExt());
		messageTextPointer.Format(resBuf, &nameAndExt);
		TextUtils::ClipToFit(messageTextPointer, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(messageTextPointer);
		CleanupStack::PopAndDestroy(); // messageText
#else
		TPtrC nameAndExt(iParse.NameAndExt());
		iBufPtr->Format(resBuf, &nameAndExt);
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		TextUtils::ClipToFit(*iBufPtr, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(*iBufPtr);
#endif		
		NegotiationComplete();
		if (iActive->CurrentFile()==0)
			{
			if (iActive->NumOfFiles()==1)
				Progress()->SetFinalValue(iBytesInFirstFile);
			else
				Progress()->SetFinalValue(iTotalBytesInMultipleFiles);
			}
		TRequestStatus* pS=&aStatus;
		User::RequestComplete(pS, KErrNone);
		}
		break;
		}
	return ETrue;
	}

void CEikIrMultipleFileSender::MergeFullPaths(TFullName& aFullPath,TInt aDeleteLength)
	{
	TFullName finalPath;
	TParse parseFileName;
	parseFileName.Set(aFullPath,NULL,NULL);
	finalPath.Append(parseFileName.Drive());
	finalPath.Append(_L("\\"));
	aFullPath.Delete(0,aDeleteLength);
	finalPath.Append(aFullPath);
	aFullPath=finalPath;
	}

void CEikIrMultipleFileSender::ReadChunkL(TInt aLength)
	//
	// Read the next chunk from the file
	//
	{
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	User::LeaveIfError(iFile.Read(iNumSent, *iBufPtr, aLength));
	if (iNumSent+aLength>=iNumToSend)
		iFile.Close();
	}

//
// class CEikIrMultipleFileReceiver
//

EXPORT_C CEikIrMultipleFileReceiver::~CEikIrMultipleFileReceiver()
	{
	iFile.Close();
	delete iRemoteFilesAlreadyPresent;
	delete iFilesReceivedAsTemps;
	delete iFileTimeAtt;
	}

EXPORT_C CEikIrMultipleFileReceiver* CEikIrMultipleFileReceiver::NewL()
	// static
	{
	CEikIrMultipleFileReceiver* This=new(ELeave) CEikIrMultipleFileReceiver;
	CleanupStack::PushL(This);
	This->ConstructMultipleFileReceiverL();
	CleanupStack::Pop(); // This
	return This;
	}

EXPORT_C void CEikIrMultipleFileReceiver::ReceiveLD(const TDesC& aPath)
	{
/*
	A valid path must:
		- specify a drive
		- fully specify an existing directory
		- end in a backslash
		- not specify a file
*/
	iReceivingFolderPath=aPath;
	iFs=&(iEikonEnv->FsSession());
	CleanupStack::PushL(this);
	User::LeaveIfError(iParse.Set(_L("*.*"), &aPath, NULL));
	TVolumeInfo volInfo;
	TInt driveId;
	User::LeaveIfError(RFs::CharToDrive(aPath[0], driveId));
	iFs->Volume(volInfo, driveId);
	TMediaType mediaType=volInfo.iDrive.iType;
	TInt mediaAtt=volInfo.iDrive.iMediaAtt;
	if (mediaAtt&KMediaAttWriteProtected)
		mediaType=EMediaRom;
	switch (mediaType)
		{
	case EMediaNotPresent:
	case EMediaCdRom:
	case EMediaRom:
		{
		TPtrC drive=aPath.Left(1);
		iEikonEnv->InfoMsg(R_EIK_TBUF_IR_DISK_IS_ROM, &drive);
		CleanupStack::Pop(); // this
		return;
		}
		break;
	default:
		break;
		}
	if (!(EikFileUtils::PathExists(aPath)))
		User::Leave(KErrPathNotFound);
	CleanupStack::Pop(); // this
	ExecuteLD(R_EIK_IR_DIALOG);
	}


EXPORT_C void CEikIrMultipleFileReceiver::FileName(TDes& aFileName) const
	//
	// Copies the filename of the received file into aFileName
	//
	{
	TPtrC fileName=iParse.FullName();
	aFileName.Copy(fileName); // callers responsibility for overflow
	}

void CEikIrMultipleFileReceiver::HandleTransferCompleteL()
	{
	// deal with the renaming multiple files

	TInt duplicateCount=iFilesReceivedAsTemps->Count();
	__ASSERT_DEBUG(iRemoteFilesAlreadyPresent->Count()==iFilesReceivedAsTemps->Count(),User::Invariant());
	for (TInt counter=0;counter<duplicateCount;counter++) 
		{
		iParse.Set((*iFilesReceivedAsTemps)[counter],NULL,NULL);
		iRemoteFileName=((*iRemoteFilesAlreadyPresent)[counter]);
		TFileName tempFileName=(*iFilesReceivedAsTemps)[counter];
		TBool fileSaved=QueryRenameFileL(tempFileName);
		DrawNow();
		if (fileSaved)
			{
			iFileTimeAtt->Get(counter,iFileModified,iFileAtt);
			User::LeaveIfError(iFs->SetEntry(tempFileName, iFileModified, iFileAtt, 0));
			}
		}
	}


TBool CEikIrMultipleFileReceiver::QueryRenameFileL(TFileName& aNewFileName)
	{
	TInt maxWidth=iEikonEnv->ScreenDevice()->SizeInPixels().iWidth-50;
	FindUniqueFileNameL(aNewFileName);
	TBuf<64> temp;
	iEikonEnv->ReadResource(temp, R_EIK_TBUF_IR_RECEIVED_FILE_EXISTS);
	HBufC* titleBuf=HBufC::NewLC(KMaxFileName+64);
	TPtr title=titleBuf->Des();
	title.Format(temp, &iRemoteFileName);
	TextUtils::ClipToFit(title, *(iEikonEnv->NormalFont()), maxWidth);
	TInt err=KErrNone;
	do
		{
		CEikIrFileSaveAsDialog* dialog=new(ELeave) CEikIrFileSaveAsDialog(&aNewFileName,iRemoteFileName,&title);
		if (dialog->ExecuteLD(R_EIK_DIALOG_FILE_SAVEAS))
			{
			err=KErrNone;
			if (ConeUtils::FileExists(aNewFileName))
				err=iFs->Delete(aNewFileName);
			if (err==KErrInUse)
				{
				TParsePtrC parse(aNewFileName);
				TPtrC name=parse.NameAndExt();
				iEikonEnv->ReadResource(temp, R_EIK_TBUF_FILE_IN_USE);
				HBufC* msgBuf=HBufC::NewL(KMaxFileName+64);
				TPtr msg=msgBuf->Des();
				msg.Format(temp, &name);
				TextUtils::ClipToFit(msg, *(iEikonEnv->NormalFont()), maxWidth);
				iEikonEnv->AlertWin(msg,_L(""));
				delete msgBuf;
				}
			else
				{
				err=iFs->Rename(iParse.FullName(), aNewFileName);
				if (!err)
					err=iFs->SetAtt(aNewFileName, 0, KEntryAttHidden);
				User::LeaveIfError(err);
				iEikonEnv->InfoMsg(R_EIK_TBUF_SAVED);
				}
			}
		else
			{
			User::LeaveIfError(iFs->Delete(iParse.FullName()));
			iEikonEnv->InfoMsg(R_EIK_TBUF_IR_RECEIVED_FILE_DISCARDED);
			CleanupStack::PopAndDestroy(); // titleBuf
			return EFalse;
			}
		} while (err!=KErrNone);
	CleanupStack::PopAndDestroy(); // titleBuf
	return ETrue;
	}

void CEikIrMultipleFileReceiver::PreLayoutDynInitL()
	{  
	CEikIrReceiver::PreLayoutDynInitL();
	SetTopLabel(R_EIK_TBUF_IR_READY_TO_RECEIVE_FILE);
	SetBottomLabel(R_EIK_TBUF_IR_AIM_PSIONS);
	}

TBool CEikIrMultipleFileReceiver::NegotiateL(TRequestStatus& aStatus)
	{
	// The logic in this function can deal with a file sent by using the 
	// single file negotiate CEikIrFileReceiver::NegotiateL()
	// But Note: The negoatiation behaviour is different when 
	// receiving one file rather than multiple files.
	if (iLastFile < iActive->CurrentFile())
		{
		iNegStage=EQueueReadFileNameAndDetails;
		iNumToRecv=0;
		iNumRecvd=0;
		}
	iLastFile=iActive->CurrentFile();
	switch (iNegStage)
		{
	case ESendNumberOfFiles:
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		iNegStage=EReadNumOfFilesAck;
		break;
	case EReadNumOfFilesAck:
		{
		TIrMsgState ret=ParseFirstPacketL();
		if (ret==ESingleFileFromV1Machine)
			{
			iNegStage=EGotFileNameAndDetailsSendAck;
			TRequestStatus* pS=&(iActive->iStatus);
			User::RequestComplete(pS,0);
			return ETrue;
			}
		else if (ret==EProceed)
			{
			TBool proceed=CheckFreeSpaceL();
			if (!proceed)
				ret=ENoProceed;
			}
		if (ret==ENoProceed)
			{
			iBufPtr->Copy(_L("ACK N"));
			iFileRecFlags|=EAckNSent;
			}
		else
			iBufPtr->Copy(_L("ACK Y"));
		iReader.Write(*iBufPtr, aStatus);
		iNegStage=EQueueReadFileNameAndDetails;
		}
		break;
	case ENumOfFilesAckReceived:
		break;// not used
		// the above switches are only used on the first file
	case EQueueReadFileNameAndDetails:
		iNegStage=EGotFileNameAndDetailsSendAck;
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		break;
	case EGotFileNameAndDetailsSendAck:
		{
		// *iBufPtr contains remote (sending) filename and extension (no path) & details
		TIrMsgState ret=ParseFirstPacketL();
		if (ret==EProceed)
			{
			TBool proceed=CheckFreeSpaceL();
			if (!proceed)
				ret=ENoProceed;
			}
		if (ret==ENoProceed)
			{
			iBufPtr->Copy(_L("ACK N"));
			iFileRecFlags|=EAckNSent;
			}
		else
			iBufPtr->Copy(_L("ACK Y"));
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());		
		iReader.Write(*iBufPtr, aStatus);
		iNegStage=EAckSent;
		}
		break;
	case EAckSent:
		if (iFileRecFlags&EAckNSent)
			return EFalse;
		if (CheckFileExists())
			{
			iFileTimeAtt->SetL(iFileModified,iFileAtt);
			TPtrC driveAndPath=iParse.DriveAndPath();
			TFileName fileName;
			User::LeaveIfError(iFile.Temp(*iFs, driveAndPath, fileName, EFileWrite));
			iFileRecFlags|=EFileOpened;
			User::LeaveIfError(iFile.SetAtt(KEntryAttHidden, 0)); // hide temp files
			iRemoteFilesAlreadyPresent->AppendL(iRemoteFileName);
			iFilesReceivedAsTemps->AppendL(fileName);
			iParse.Set(fileName, &driveAndPath, NULL);
			}
		else
			{
			User::LeaveIfError(iFile.Replace(*iFs, iParse.FullName(), EFileWrite));
			iFileRecFlags|=EFileOpened;
			iFileRecFlags|=EFileNotDuplicate;
			}
		TBuf<50> resBuf;
		iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_RECEIVING_FILE);
#if defined(_UNICODE)
		HBufC* messageText=HBufC::NewLC(resBuf.Length()+iRemoteFileName.Length());
		TPtr messageTextPointer=messageText->Des();
		messageTextPointer.Format(resBuf, &iRemoteFileName);
		TextUtils::ClipToFit(messageTextPointer, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(messageTextPointer);
		CleanupStack::PopAndDestroy(); // messageText
#else
		iBufPtr->Format(resBuf, &iRemoteFileName);
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		TextUtils::ClipToFit(*iBufPtr, *(iEikonEnv->NormalFont()), Progress()->Size().iWidth);
		SetTopLabel(*iBufPtr);
#endif
		NegotiationComplete();
		if (iActive->CurrentFile()==0)
			Progress()->SetFinalValue(iTotalBytesInMultipleFiles);
		// !! following line is a workaround a very bizarre bug in (probably) IRDA
		// Depending on the size of iBufPtr, sometimes only that amount of data is read
		// instead of the Maximum size of the buffer
		iBufPtr->Zero();
		__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
		iReader.Read(*iBufPtr, aStatus);
		// NegotiateL MUST queue the next read on the socket
		break;
		}
	return ETrue;
	}

void CEikIrMultipleFileReceiver::WriteChunkL()
	{
	TInt length=iBufPtr->Length();
	__ASSERT_DEBUG(iBufPtr->MaxLength(),User::Invariant());
	User::LeaveIfError(iFile.Write(iNumRecvd-length, *iBufPtr, length));
	if (iNumRecvd==iNumToRecv)
		{
		iFile.Close();
		if (iFileRecFlags&EFileNotDuplicate)
			{
			User::LeaveIfError(iFs->SetEntry(iParse.FullName(), iFileModified, iFileAtt, 0));
			iFileRecFlags&=(~EFileNotDuplicate);
			}
		}
	}

CEikIrMultipleFileReceiver::TIrMsgState CEikIrMultipleFileReceiver::ParseFirstPacketL()
	//
	// If a single file has been sent then 
	// jump several negotiation stages and complete request
	//
	// 
	{
	TChar delimiter=' ';
	TInt pos=iBufPtr->Locate(delimiter);
	TPtrC8 dataType=iBufPtr->Left(pos);
	if (dataType.CompareF(_L8("MULTIPLEFILES"))==0)
		{
		pos+=1;// for space character
		TPtrC8 numOfFilesAndTotalBytes=iBufPtr->Right(iBufPtr->Length()-pos);
		ParseNumOfFilesAndTotalBytesL(numOfFilesAndTotalBytes);
		return EProceed;
		}
	else if (dataType.CompareF(_L8("FILE"))==0)
		{
		TPtrC8 fileDetails=iBufPtr->Right(iBufPtr->Length()-pos-1);
		if (iNegStage==EReadNumOfFilesAck)
			{
			TChar delimiter=' ';
			TInt pos=0;
			pos=fileDetails.Locate(delimiter);	// end of file size
			TLex8 lex(fileDetails.Left(pos));
			lex.Val(iTotalBytesInMultipleFiles);  // read file size
			// RequestComplete and jump forward a few negotiation stages
			// to deal with receiving a single file
			return ESingleFileFromV1Machine;
			}
		ParseFileNameAndDetailsL(fileDetails);
		return EProceed;
		}
	else if (dataType.CompareF(_L8("DATA"))==0)
		SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_TEXT);
	else
		SetBottomLabel(R_EIK_TBUF_IR_OTHER_SENDING_UNSUPPORTED);
	return ENoProceed;
	}


void CEikIrMultipleFileReceiver::ParseFileNameAndDetailsL(const TDesC8& aNameAndDetails)
	//
	// Extract file name, size (iNumToRecv), attributes and time stamp from aDetails
	//
	{
	TChar delimiter=' ';
	TInt pos=0;
	pos=aNameAndDetails.Locate(delimiter);	// end of file size
	TLex8 lex(aNameAndDetails.Left(pos));
	lex.Val(iNumToRecv);  // read file size

	TPtrC8 next(aNameAndDetails.Right(aNameAndDetails.Length()-pos-1));
	pos=next.Locate(delimiter);	// end of file att's
	lex.Assign(next.Left(pos));
	lex.Val(iFileAtt);	// read file att's

	TPtrC8 next2=next.Right(next.Length()-pos-1);
	pos=next2.Locate(delimiter);	// end of time high 
	lex.Assign(next2.Left(pos));
	TUint high;
	lex.Val(high); // read time high

	TPtrC8 next3=next2.Right(next2.Length()-pos-1);
	pos=next3.Locate(delimiter);
	lex.Assign(next3.Left(pos));
	TUint low;
	lex.Val(low); // read time low
	TInt64 time(high, low);
	iFileModified=TTime(time);

	// MUST do filename last since it can contain spaces
#if defined(_UNICODE)
	TPtrC8 truncatedNarrowFileName=next3.Right(next3.Length()-pos-1);
	TFileName fileName;
	fileName.Copy(truncatedNarrowFileName);
#else
	TPtrC fileName=next3.Right(next3.Length()-pos-1); // read filename
#endif
	TInt findPos;
	findPos=fileName.Locate('\\');
		
	TFullName fullName(iReceivingFolderPath);
	fullName.Append(fileName.Mid(findPos+1));
	TInt err=iFs->MkDirAll(fullName); // ensure dest path exists
	if (err!=KErrNone && err!= KErrAlreadyExists)
		User::Leave(err);
	iParse.Set(fullName, NULL, NULL);
	iRemoteFileName=iParse.NameAndExt();
	}

void CEikIrMultipleFileReceiver::ParseNumOfFilesAndTotalBytesL(const TDesC8& aNumFilesAndBytes)
	//
	// Extract file name, size (iNumToRecv), attributes and time stamp from aDetails
	//
	{
	TChar delimiter=' ';
	TInt pos=0;
	pos=aNumFilesAndBytes.Locate(delimiter);	// end of file size
	TLex8 lex(aNumFilesAndBytes.Left(pos));
	lex.Val(iNumOfFiles);  // read number of files
	iActive->SetNumOfFiles(iNumOfFiles);

	TPtrC8 next(aNumFilesAndBytes.Right(aNumFilesAndBytes.Length()-pos-1));
	lex.Assign(next);
	lex.Val(iTotalBytesInMultipleFiles);	// read iTotalBytesInMultipleFiles
	}

TBool CEikIrMultipleFileReceiver::CheckFreeSpaceL()
	//
	// Checks to see if the destination drive has enough free space for the file
	//
	{
	// get volume info
	TVolumeInfo info;
	TDriveUnit drive(iParse.Drive());
	User::LeaveIfError(iFs->Volume(info, TInt(drive)));
	TBool enoughSpace=(TInt64)iNumToRecv<info.iFree;
	if (!enoughSpace)
		{
		TBuf<32> topText;
		iEikonEnv->GetErrorText(topText,KErrDiskFull);
		SetTopLabel(topText);
		ClearBottomLabel();
		}
	return enoughSpace;
	}

void CEikIrMultipleFileReceiver::FindUniqueFileNameL(TFileName& aName)
	{
	// temp filename being used
	const TInt KUniqueNameLeftDelimiter='(';
	const TInt KUniqueNameRightDelimiter=')';
	// first find base name
	aName=iParse.DriveAndPath();
	aName.Append(iRemoteFileName);
	TParsePtrC parse(aName);
	TInt len=parse.Name().Length();
	TInt pos=parse.Name().LocateReverse(KUniqueNameLeftDelimiter);
	if (pos>0 && (len-pos)>=4)
		{
		if (parse.Name()[len-1]==KUniqueNameRightDelimiter)
			{
			for (TInt ii=pos+1;ii<len-1;ii++)
				{
				TChar ch=parse.Name()[ii];
				if (!ch.IsDigit())
					return;
				}
			aName.Delete(parse.DriveAndPath().Length()+pos,len-pos);
			}
		}
	User::LeaveIfError(CApaApplication::GenerateFileName(*iFs, aName));
	}

TBool CEikIrMultipleFileReceiver::CheckFileExists()
	{
	iFileRecFlags&=(~ETempNameUsed);
	if (ConeUtils::FileExists(iParse.FullName()))
		{
		iFileRecFlags|=ETempNameUsed;
		return ETrue;
		}
	return EFalse;
	}

void CEikIrMultipleFileReceiver::HandleUserCancelL()
	//
	// Remove a partially transferred file after a user selects Cancel
	//
	{
	if (iFileRecFlags&EFileOpened)
		{
		iFile.Close();
		User::LeaveIfError(iFs->Delete(iParse.FullName()));
		}
	}

void CEikIrMultipleFileReceiver::HandleProgressUpdateL()
	{
	
	TUint next=iNumRecvd+iBufPtr->MaxLength();
	if (next > iNumToRecv)
		next=iNumToRecv;
	TInt chunk=next-iNumRecvd;
	iTotalBytesInMultipleFilesReceived+=chunk;
	Progress()->SetAndDraw(iTotalBytesInMultipleFilesReceived);
	}

CEikIrMultipleFileReceiver::CEikIrMultipleFileReceiver() 
	: CEikIrReceiver(), iNegStage(ESendNumberOfFiles), iLastFile(0)
	{}

void CEikIrMultipleFileReceiver::ConstructMultipleFileReceiverL()
	{
	CEikIrMain::ConstructL();
	iFileTimeAtt=CTimeStampAndFileAttributes::NewL();

	iRemoteFilesAlreadyPresent=new(ELeave) CDesCArraySeg(16);
	iFilesReceivedAsTemps=new(ELeave) CDesCArraySeg(16);
	}

//
// class CEikIrFileSaveAsDialog
//

CEikIrFileSaveAsDialog::CEikIrFileSaveAsDialog(TDes* aFileName,TDes& aRemoteFileName,const TDesC* aTitle)
	: CEikFileSaveAsDialog(aFileName,aTitle,NULL,EFalse), iRemoteFileName(aRemoteFileName)
	{}

void CEikIrFileSaveAsDialog::PreLayoutDynInitL()
	{
	CEikFileSaveAsDialog::PreLayoutDynInitL();
	InsertLineL(0,R_EIK_IR_DUPLICATE_FILES);
	TBuf<KMaxFileName> resBuf;
	TBuf<KMaxFileName> resBuf2;
	iEikonEnv->ReadResource(resBuf, R_EIK_TBUF_IR_DUPLICATE_FILES);
	const TInt resLength=resBuf.Length();
	const TInt lineBelowLengthInPixels=(Control(EEikCidFileNameEd)->Size()).iWidth;
	const TInt fileNameMaxLength=Max(lineBelowLengthInPixels-resLength,0);
	TextUtils::ClipToFit(iRemoteFileName, *(iEikonEnv->AnnotationFont()),fileNameMaxLength);
	resBuf2.Format(resBuf,&iRemoteFileName);
	CEikLabel* label=STATIC_CAST(CEikLabel*,Control(EEikCidIrSaveAsLabelId));
	label->SetTextL(resBuf2);
	label->SetFont(iEikonEnv->AnnotationFont());
	}

CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes* CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes::NewL()
	{
	
	CTimeStampAndFileAttributes* This =new(ELeave) CTimeStampAndFileAttributes;
	CleanupStack::PushL(This);

	This->ConstructL();
	CleanupStack::Pop();

	return This;

	}

CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes::~CTimeStampAndFileAttributes()
	{
	delete iFileModified;
	delete iFileAtt;
	}

void CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes::SetL(TTime& aFileModified,TUint& aFileAtt)
	{
	iFileModified->AppendL(aFileModified);
	iFileAtt->AppendL(aFileAtt);
	
	}
void CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes::Get(TInt aIndex,TTime& aFileModified,TUint& aFileAtt) const
	{
	aFileModified=iFileModified->At(aIndex);
	aFileAtt=iFileAtt->At(aIndex);

	}


void CEikIrMultipleFileReceiver::CTimeStampAndFileAttributes::ConstructL()
	{
	iFileModified=new(ELeave) CArrayFixSeg<TTime>(16);
	iFileAtt=new(ELeave) CArrayFixSeg<TUint>(16);
	}

