applayerprotocols/telnetengine/SRC/TELFSM.CPP
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerprotocols/telnetengine/SRC/TELFSM.CPP	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1681 @@
+// Copyright (c) 2003-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:
+// Telnet Protocol API
+// Supported RFC class implementations
+// 
+//
+
+/**
+ @file 
+*/
+
+#include	"TELFSM.H"
+#include	"TELDEBUG.H"
+
+// The option negotiation class
+// Independent of the option 
+// All option classes contain one of these state machine classes
+
+TOptionFSM::TOptionFSM()
+/**
+Constructor
+Set up the defaults
+*/
+	{
+	Allowed = ENoOption;
+	Us = ENo;
+	UsQ = EEmpty;
+	Him = ENo;
+	HimQ = EEmpty;
+	}
+
+void TOptionFSM::Reset()
+/**
+Reset restores the state machine to default but leaves the permissions
+*/
+	{
+	Us = ENo;
+	UsQ = EEmpty;
+	Him = ENo;
+	HimQ = EEmpty;
+	}
+
+TInt32	TOptionFSM::Request(const TInt32 aRequest,TInt32& aAction,TInt32& aEvent)
+/**
+Implementation of RFC 1143 Q Method bidirectional Telnet option state machine
+Can be called from the client side (aRequest = EClientXX) or the line side (aRequest = EServerXX).
+
+errors are returned for illegal requests
+aEvent is set when an option transits to enabled or disabled
+aAction's are the Telnet defined DO, WILL, WONT, DONT
+*/
+	{
+	TInt32 Ret = EErrNone;
+	aAction = ENoAction;
+	aEvent = ENoEvent;
+
+	switch(aRequest)
+		{
+//
+		case	EServerWill	:
+		if(Him == ENo)
+			{
+			if(Allowed & EServerWill)
+				{
+				Him = EYes;				
+				Ret = EErrNone;
+				aEvent = EServerEnabled;
+				aAction = ESendDo;
+				}
+			else
+				{
+				Ret = EErrNone;
+				aEvent = ENoEvent;
+				aAction = ESendDont;
+				}
+			}
+		else if(Him == EYes)
+			{
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EEmpty)
+			{
+			Him = ENo;
+			Ret = EProtocolError;
+			aEvent = EServerDisabled;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EOpposite)
+			{
+			Him = EYes;
+			HimQ = EEmpty;
+			Ret = EProtocolError;
+			aEvent = EServerEnabled;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EEmpty)
+			{
+			Him = EYes;
+			Ret = EErrNone;
+			aEvent = EServerEnabled;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EOpposite)
+			{
+			Him = EWantNo;
+			HimQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendDont;
+			}
+		break;
+//
+		case	EServerDo	:
+		if(Us == ENo)
+			{
+			if(Allowed & EServerDo)
+				{
+				Us = EYes;				
+				Ret = EErrNone;
+				aEvent = EClientEnabled;
+				aAction = ESendWill;
+				}
+			else
+				{
+				Ret = EErrNone;
+				aEvent = ENoEvent;
+				aAction = ESendWont;
+				}
+			}
+		else if(Us == EYes)
+			{
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EEmpty)
+			{
+			Us = ENo;
+			Ret = EProtocolError;
+			aEvent = EClientDisabled;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EOpposite)
+			{
+			Us = EYes;
+			UsQ = EEmpty;
+			Ret = EProtocolError;
+			aEvent = EClientEnabled;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EEmpty)
+			{
+			Us = EYes;
+			Ret = EErrNone;
+			aEvent = EClientEnabled;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EOpposite)
+			{
+			Us = EWantNo;
+			UsQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendWont;
+			}
+		break;
+//
+
+		case	EServerWont	:
+		if(Him == ENo)
+			{
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EYes)
+			{
+			Him = ENo;
+			Ret = EErrNone;
+			aEvent = EServerDisabled;
+			aAction = ESendDont;
+			}
+		else if(Him == EWantNo && HimQ == EEmpty)
+			{
+			Him = ENo;
+			Ret = EErrNone;
+			aEvent = EServerDisabled;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EOpposite)
+			{
+			Him = EWantYes;
+			HimQ = ENone;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendDo;
+			}
+		else if(Him == EWantYes && HimQ == EEmpty)
+			{
+			Him = ENo;
+			Ret = EErrNone;
+			aEvent = EServerDisabled;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EOpposite)
+			{
+			Him = ENo;
+			HimQ = ENone;
+			Ret = EErrNone;
+			aEvent = EServerDisabled;
+			aAction = ENoAction;
+			}
+		break;
+//
+		case	EServerDont	:
+		if(Us == ENo)
+			{
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EYes)
+			{
+			Us = ENo;
+			Ret = EErrNone;
+			aEvent = EClientDisabled;
+			aAction = ESendWont;
+			}
+		else if(Us == EWantNo && UsQ == EEmpty)
+			{
+			Us = ENo;
+			Ret = EErrNone;
+			aEvent = EClientDisabled;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EOpposite)
+			{
+			Us = EWantYes;
+			UsQ = ENone;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendWill;
+			}
+		else if(Us == EWantYes && UsQ == EEmpty)
+			{
+			Us = ENo;
+			Ret = EErrNone;
+			aEvent = EClientDisabled;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EOpposite)
+			{
+			Us = ENo;
+			UsQ = ENone;
+			Ret = EErrNone;
+			aEvent = EClientDisabled;
+			aAction = ENoAction;
+			}
+		break;
+//
+		case	EClientDo	:
+		if(!(Allowed & EClientDo))
+			{
+			Ret = EPermissionsError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == ENo)
+			{
+			Him = EWantYes;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendDo;
+			}
+		else if(Him == EYes)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EEmpty)
+			{
+			if(UsQ == EOpposite)
+				{
+				Ret = EErrNone;
+				HimQ = EOpposite;
+				}
+			else
+				Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EOpposite)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EEmpty)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EOpposite)
+			{
+			HimQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		break;
+//
+		case	EClientWill	:
+		if(!(Allowed & EClientWill))
+			{
+			Ret = EPermissionsError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == ENo)
+			{
+			Us = EWantYes;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendWill;
+			}
+		else if(Us == EYes)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EEmpty)
+			{
+			if(HimQ == EOpposite)
+				{
+				Ret = EErrNone;
+				UsQ = EOpposite;
+				}
+			else
+				Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EOpposite)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EEmpty)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EOpposite)
+			{
+			UsQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		break;
+//
+		case	EClientDont	:
+		if(Him == ENo)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EYes)
+			{
+			Him = EWantNo;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendDont;
+			}
+		else if(Him == EWantNo && HimQ == EEmpty)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantNo && HimQ == EOpposite)
+			{
+			HimQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EEmpty)
+			{
+			if(UsQ == EOpposite)
+				{
+				HimQ = EOpposite;
+				Ret = EErrNone;
+				}
+			else
+				Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Him == EWantYes && HimQ == EOpposite)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		break;
+//
+		case	EClientWont	:
+		if(Us == ENo)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EYes)
+			{
+			Us = EWantNo;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ESendWont;
+			}
+		else if(Us == EWantNo && UsQ == EEmpty)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantNo && UsQ == EOpposite)
+			{
+			UsQ = EEmpty;
+			Ret = EErrNone;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EEmpty)
+			{
+			if(HimQ == EOpposite)
+				{
+				UsQ = EOpposite;
+				Ret = EErrNone;
+				}
+			else
+				Ret = EProtocolError;
+
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		else if(Us == EWantYes && UsQ == EOpposite)
+			{
+			Ret = EProtocolError;
+			aEvent = ENoEvent;
+			aAction = ENoAction;
+			}
+		break;
+
+		default	:
+		break;
+
+	
+		}
+	return(Ret);
+	}
+
+// The base class for all RFC option classes 
+
+CRFCOptionBase::~CRFCOptionBase()
+/**
+Destructor
+*/
+	{
+	__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CRFCOptionBase::D'Tor"));
+	}
+
+void CRFCOptionBase::CreateOptionResponse(TInt8 aOption,TInt32 aAction,TDes8& aBuffer)
+/**
+Create a Telnet protocol request or response
+*/
+	{
+	aBuffer.Append(KTelnetIAC);
+
+	// MAP our local codes to the genuine telnet ones
+	if(aAction == TOptionFSM::ESendWill)
+		aBuffer.Append(KTelnetWILL);
+	else if(aAction == TOptionFSM::ESendDo)
+		aBuffer.Append(KTelnetDO);
+	else if(aAction == TOptionFSM::ESendDont)
+		aBuffer.Append(KTelnetDONT);
+	else if(aAction == TOptionFSM::ESendWont)
+		aBuffer.Append(KTelnetWONT);
+
+	aBuffer.Append(aOption);
+	};
+
+
+void CUnknownOption::RequestUnknown(TInt8 aOption,const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+For tidy code we keep a class that handles unknown option requests from the server.
+The result of a call into this object will always be WONT or DONT
+*/
+	{
+	TInt32 action;
+	iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CRFCOptionBase::CreateOptionResponse(aOption,action,aAction);
+	}
+
+CUnknownOption* CUnknownOption::NewL()
+	{
+	CUnknownOption* self = new(ELeave) CUnknownOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+void CUnknownOption::ConstructL()
+	{
+	}
+
+CUnknownOption::~CUnknownOption()
+/**
+Destructor
+*/
+	{
+	__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CUnknownOption::D'Tor"));
+	}
+
+CSuppressGAOption* CSuppressGAOption::NewL()
+	{
+	CSuppressGAOption* self = new(ELeave) CSuppressGAOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CSuppressGAOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+Virtual overide
+Should always be a request for DO or WILL as we always suppress the Go Ahead signal
+*/
+	{
+	TInt32 action;
+
+	iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionSuppressGA,action,aAction);
+	return(KErrNone);
+	}
+
+void CSuppressGAOption::ConstructL()
+	{
+	}
+
+void CSuppressGAOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetDO);
+		aCurrentStatus.Append(KTelnetProtOptionSuppressGA);
+		}
+	}
+
+
+CStatusOption* CStatusOption::NewL()
+	{
+	CStatusOption* self = new(ELeave) CStatusOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CStatusOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+Virtual overide
+*/
+	{
+	TInt32 action;
+
+	iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionStatus,action,aAction);
+
+	return(KErrNone);
+	}
+
+void CStatusOption::ConstructL()
+	{
+	}
+
+void CStatusOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionStatus);
+		}
+	if(iFSM.ServerEnabled())
+		{
+		aCurrentStatus.Append(KTelnetDO);
+		aCurrentStatus.Append(KTelnetProtOptionStatus);
+		}
+	}
+
+CSpeedOption* CSpeedOption::NewL()
+	{
+	CSpeedOption* self = new(ELeave) CSpeedOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CSpeedOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+Virtual overide
+*/
+	{
+	TInt32 action;
+	TInt32 err;
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionTerminalSpeed,action,aAction);
+	return(err);
+	}
+
+void CSpeedOption::GetTelnetSubOption(TDes8& aOutBuffer)
+/**
+Creates the sub option for telling the server our speed
+*/
+	{
+	aOutBuffer.Append(KTelnetIAC); // Interpret As Command
+	aOutBuffer.Append(KTelnetSB);  // Suboption start
+	aOutBuffer.Append(KTelnetProtOptionTerminalSpeed); //Terminal Speed
+	aOutBuffer.Append(KTelnetCommandIS); // IS
+
+	aOutBuffer.Append(iTermSpeed); // Receive Speed
+	aOutBuffer.Append(',');
+	aOutBuffer.Append(iTermSpeed); // Send Speed
+
+	aOutBuffer.Append(KTelnetIAC); // Interpret As Command
+	aOutBuffer.Append(KTelnetSE);  // Suboption End
+	}
+
+void CSpeedOption::ConstructL()
+	{
+	iTermSpeed = _L8("38400");
+	}
+
+void CSpeedOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionTerminalSpeed);
+		}
+	}
+
+TBool CSpeedOption::Set(const TDesC8& aSpeed)
+/**
+Sets the speed option, return TRUE if it's altered
+*/
+	{
+	TBool ret;
+	(iTermSpeed == aSpeed) ? (ret = FALSE) : (ret = TRUE);
+	iTermSpeed = aSpeed;
+	return ret;
+	}
+
+CLogoutOption* CLogoutOption::NewL()
+	{
+	CLogoutOption* self = new(ELeave) CLogoutOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CLogoutOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+Virtual overide
+*/
+	{
+	TInt32 action;
+	TInt32 err;
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionLogoff,action,aAction);
+	return(err);
+	}
+
+void CLogoutOption::ConstructL()
+	{
+	}
+
+void CLogoutOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+I don't think this method is relevant for status reporting but for consistency and is
+never called to report it's status.
+*/
+	{
+	if(iFSM.ServerEnabled())
+		{
+		aCurrentStatus.Append(KTelnetDO);
+		aCurrentStatus.Append(KTelnetProtOptionLogoff);
+		}
+	}
+
+CTerminalTypeOption* CTerminalTypeOption::NewL()
+	{
+	CTerminalTypeOption* self = new(ELeave) CTerminalTypeOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CTerminalTypeOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+/**
+Virtual overide
+*/
+	{
+	TInt32 action;
+	TInt32 err;
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionTerminalType,action,aAction);
+	return(err);
+	}
+
+void CTerminalTypeOption::ConstructL()
+	{
+	iTermType  = _L8("dumb");
+	}
+
+void CTerminalTypeOption::GetTelnetSubOption(TDes8& aOutBuffer)
+	{
+	aOutBuffer.Append(KTelnetIAC); // Interpret As Command
+	aOutBuffer.Append(KTelnetSB);  // Suboption Start
+	aOutBuffer.Append(KTelnetProtOptionTerminalType); // Terminal Type
+	aOutBuffer.Append(KTelnetCommandIS); // IS
+	aOutBuffer.Append(iTermType); // Terminal Type eg "dumb"
+	aOutBuffer.Append(KTelnetIAC);// Interpret As Command
+	aOutBuffer.Append(KTelnetSE); // Suboption End
+	}
+
+void CTerminalTypeOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionTerminalType);
+		}
+	}
+
+TBool CTerminalTypeOption::Set(const TDesC8& aType)
+/**
+Sets the terminal type, returns TRUE if the type has altered
+*/
+	{
+	TBool ret;
+	(aType == iTermType) ? (ret = FALSE) : (ret = TRUE);
+	iTermType = aType;
+	return ret;
+	}
+
+CWindowSizeOption* CWindowSizeOption::NewL()
+	{
+	CWindowSizeOption* self = new(ELeave) CWindowSizeOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CWindowSizeOption::RequestOption(const TInt32 aRequest, TDes8& aAction,TInt32& aEvent)
+	{
+	TInt32 action;
+	TInt32 err;
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionWindowSize,action,aAction);
+	return(err);
+	}
+
+void CWindowSizeOption::ConstructL()
+	{
+	iWindowSize.x = 80;
+	iWindowSize.y = 24;
+	}
+
+TBool CWindowSizeOption::Set(const TTelnetConfig::TWindowSize& aSize)
+/**
+Sets the window size, returns TRUE if it's altered
+*/
+	{
+	TBool ret;
+	(iWindowSize.x == aSize.x && iWindowSize.y == aSize.y) ? (ret = FALSE) : (ret = TRUE);
+	iWindowSize.x = aSize.x;
+	iWindowSize.y = aSize.y;
+	return ret;
+	}
+
+void CWindowSizeOption::GetTelnetSubOption(TDes8& aOutBuffer)
+/**
+Creates the window size suboption in short's
+Make sure we escape any Interpret As Command 255's (unlikely to occur)
+*/
+	{
+	aOutBuffer.Append(KTelnetIAC);                  // Interpret As Command
+	aOutBuffer.Append(KTelnetSB);                   // Suboption Start
+	aOutBuffer.Append(KTelnetProtOptionWindowSize); // Window Size (NAWS)
+
+	TUint8 nibble;
+
+	nibble = (TUint8)(iWindowSize.x >> 8);  // High Byte of x
+	aOutBuffer.Append(nibble);              
+	if(nibble == KTelnetIAC)                // Escape check
+		aOutBuffer.Append(KTelnetIAC);
+	nibble = (TUint8)iWindowSize.x;         // Low byte of x
+	aOutBuffer.Append(nibble); 
+	if(nibble == KTelnetIAC)                // Escape check          
+		aOutBuffer.Append(KTelnetIAC);
+
+	nibble = (TUint8)(iWindowSize.y >> 8);  // High byte of y
+	aOutBuffer.Append(nibble);
+	if(nibble == KTelnetIAC)                // Escape check
+		aOutBuffer.Append(KTelnetIAC);
+	nibble = (TUint8)iWindowSize.y;         // Low byte of y
+	aOutBuffer.Append(nibble);
+	if(nibble == KTelnetIAC)                // Escape check
+		aOutBuffer.Append(KTelnetIAC);
+
+	aOutBuffer.Append(KTelnetIAC);          // Interpret As Command
+	aOutBuffer.Append(KTelnetSE);           // Suboption End
+	}
+
+void CWindowSizeOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionWindowSize);
+		}
+	}
+
+
+CEchoOption* CEchoOption::NewL()
+	{
+	CEchoOption* self = new(ELeave) CEchoOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CEchoOption::RequestOption(const TInt32 aRequest,TDes8& aAction,TInt32& aEvent)
+	{
+	TInt32 action;
+	TInt32 err;
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionEcho,action,aAction);
+	return(err);
+	}
+
+void CEchoOption::ConstructL()
+	{
+	}
+
+void CEchoOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionEcho);
+		}
+	}
+
+
+CBinaryOption* CBinaryOption::NewL()
+	{
+	CBinaryOption* self = new(ELeave) CBinaryOption;
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+
+TInt32 CBinaryOption::RequestOption(const TInt32 aRequest,TDes8& aAction,TInt32& aEvent)
+	{
+	TInt32 action;
+	TInt32 err;
+
+	err = iFSM.Request(aRequest,action,aEvent);
+	if(action != TOptionFSM::ENoAction)
+		CreateOptionResponse(KTelnetProtOptionBinary,action,aAction);
+	return(err);
+	}
+
+void CBinaryOption::ConstructL()
+	{
+	}
+
+void CBinaryOption::GetTelnetOptionStatus(TDes8& aCurrentStatus)
+/**
+Virtual overide
+Returns a Telnet Protocol option status , see RFC 859
+*/
+	{
+	if(iFSM.ClientEnabled())
+		{
+		aCurrentStatus.Append(KTelnetWILL);
+		aCurrentStatus.Append(KTelnetProtOptionBinary);
+		}
+	}
+
+CProto* CProto::NewL(const TTelnetConfig& aConfig,MProtoEvent* aNotifier)
+	{
+	CProto* self = new(ELeave) CProto;
+	CleanupStack::PushL(self);
+	self->ConstructL(aConfig,aNotifier);
+	CleanupStack::Pop();
+	return self;
+	}
+
+void CProto::ConstructL(const TTelnetConfig& aConfig,MProtoEvent* aNotifier)
+/**
+Initialises the protocol object and creates the RFC objects
+*/
+	{
+	iNotifier =				aNotifier;
+
+	iReceiveState =			ENormal;
+	iUrgentFlag =			FALSE;
+
+	iBinary =				CBinaryOption::NewL();
+	iEcho =					CEchoOption::NewL();
+	iWindowSize =			CWindowSizeOption::NewL();
+	iTerminalType =			CTerminalTypeOption::NewL();
+	iLogout =				CLogoutOption::NewL();
+	iSpeed =				CSpeedOption::NewL();
+	iGA =					CSuppressGAOption::NewL();
+	iStatus =				CStatusOption::NewL();
+	iUnknown =				CUnknownOption::NewL();
+
+	// Set the permitted requests for all the RFC objects
+	// Logout permissions are set from the aConfig
+	// Echo permissions are set from aConfig
+	iWindowSize->SetRequestPermission
+					(
+					TOptionFSM::EServerDo | TOptionFSM::EClientWill
+					);
+
+	iStatus->SetRequestPermission
+					(
+					TOptionFSM::EServerWill | TOptionFSM::EClientDo |
+					TOptionFSM::EClientWill | TOptionFSM::EServerDo
+					);
+	
+	iSpeed->SetRequestPermission
+					(
+					TOptionFSM::EServerDo | TOptionFSM::EClientWill
+					);
+	
+	iTerminalType->SetRequestPermission
+					(
+					TOptionFSM::EServerDo | TOptionFSM::EClientWill 
+					);
+	
+	iBinary->SetRequestPermission
+					(
+					TOptionFSM::EClientDo | TOptionFSM::EServerWill |
+					TOptionFSM::EServerDo | TOptionFSM::EClientWill
+					);
+
+	iGA->SetRequestPermission
+					(
+					TOptionFSM::EClientDo | TOptionFSM::EServerWill |
+					TOptionFSM::EServerDo | TOptionFSM::EClientWill
+					);
+	// NULL means we don't want a protocol string returned to send to the server
+	ModifyConfig(aConfig,NULL);
+	}
+
+void CProto::ReceiveUrgent(TInt aUrgentData)
+/**
+We've received an urgent notification
+aUrgent will be the urgent data byte, currently we're not interested in what it is 
+*/
+	{
+	iUrgentFlag = TRUE;
+	iUrgentData = aUrgentData;
+	}
+
+void CProto::GetOptionStatus(TDes8& aOutBuffer)\
+/**
+Called when the server wants to know our perceived state of the Telnet option RFC's
+RFC member methods add WILL/DO KTelnetProtOptionXXXX, if its currently enabled for the client
+*/
+	{
+	aOutBuffer.Append(KTelnetIAC);                    // Interpret As Command
+	aOutBuffer.Append(KTelnetSB);                     // Suboption Start
+ 	aOutBuffer.Append(KTelnetProtOptionStatus);       // KTelnetProtOptionStatus
+	aOutBuffer.Append(KTelnetCommandIS);              // IS
+
+	iBinary->GetTelnetOptionStatus(aOutBuffer);       // ? WILL Binary
+	iWindowSize->GetTelnetOptionStatus(aOutBuffer);   // ? WILL NAWS
+	iTerminalType->GetTelnetOptionStatus(aOutBuffer); // ? WILL Terminal Type
+	iSpeed->GetTelnetOptionStatus(aOutBuffer);        // ? WILL Terminal Speed
+	iGA->GetTelnetOptionStatus(aOutBuffer);           // ? DO Suppress Go Ahead
+	iStatus->GetTelnetOptionStatus(aOutBuffer);       // ? WILL and/or DO Status  
+
+	aOutBuffer.Append(KTelnetIAC);                    // Interpret As Command
+	aOutBuffer.Append(KTelnetSE);                     // Suboption End
+	}
+
+void CProto::OptionStatus(TOptionStatus& aStatus)
+/**
+Client has requested the state of the RFC options
+*/
+	{
+	(iBinary->ReceiveBinary() == TRUE) ?			(aStatus.iServerBinary = TRUE)	: (aStatus.iServerBinary = FALSE);
+	(iBinary->SendBinary() == TRUE) ?				(aStatus.iClientBinary = TRUE)	: (aStatus.iClientBinary = FALSE);
+	(iEcho->ReceiveEcho() == TRUE) ?				(aStatus.iEcho = TRUE)			: (aStatus.iEcho = FALSE);
+	(iWindowSize->SendWindowSize()) ?				(aStatus.iNAWS = TRUE)			: (aStatus.iNAWS = FALSE);
+	(iSpeed->SendSpeed() == TRUE) ?					(aStatus.iTerminalSpeed = TRUE) : (aStatus.iTerminalSpeed = FALSE);
+	(iTerminalType->SendTerminalType() == TRUE) ?	(aStatus.iTerminalType = TRUE)	: (aStatus.iTerminalType = FALSE);
+	(iStatus->ReceiveStatus() == TRUE) ?			(aStatus.iServerStatus = TRUE)	: (aStatus.iServerStatus = FALSE);
+	(iStatus->SendStatus() == TRUE) ?				(aStatus.iClientStatus = TRUE)	: (aStatus.iClientStatus = FALSE);
+	}
+
+void CProto::ServerOptionStatus(TDes8& aActionBuffer)
+	{
+	if(iStatus->ReceiveStatus())
+		{
+		aActionBuffer.Append(KTelnetIAC);             // Interpret As Command
+		aActionBuffer.Append(KTelnetSB);              // Suboption Start
+		aActionBuffer.Append(KTelnetProtOptionStatus);// Status
+		aActionBuffer.Append(KTelnetCommandSEND);     // Send 
+		aActionBuffer.Append(KTelnetIAC);             // Interpret As Command
+		aActionBuffer.Append(KTelnetSE);              // Suboption End    
+		}
+	}
+
+void CProto::GetInitOptions(TDes8& aActionBuffer)
+/**
+Should be called following connection
+Place here any calls to enable options at connection time
+*/
+	{
+	TInt32 event;
+	// Switch on binary if the terminal is not "dumb"
+	if(iTerminalType->TerminalType() != _L8("dumb"))
+		ClientRequestOption(KTelnetProtOptionBinary,aActionBuffer,event);					
+	// Always suppress the Go Ahead signal
+	ClientRequestOption(KTelnetProtOptionSuppressGA,aActionBuffer,event);					
+	}
+
+
+void CProto::ModifyConfig(const TTelnetConfig& aConfig,TDes8 * aActionBuffer)
+/**
+Modify the configurable options
+aActionBuffer is set to NULL if the caller does not want to notify the server of option changes
+*/
+	{
+	// If the speed has changed
+	if(iSpeed->Set(aConfig.iTermSpeed))
+		{
+		// If speed option is not enabled and caller wants to notify server
+		if(!iSpeed->SendSpeed() && aActionBuffer)
+			{
+			TInt32 event;
+			// If caller wants to notify server
+			// Switch the option on
+			ClientRequestOption(KTelnetProtOptionTerminalSpeed,*aActionBuffer,event);
+			}
+		}
+	// Terminal Type
+	// We don't currently support lists of terminal types.
+	// All telnet servers request the terminal type first thing so, just modify the terminal type
+	// If the connection is up, then the client will have to close the connection
+	iTerminalType->Set(aConfig.iTermType);
+	// NAWS
+	// If window size has changed
+	if(iWindowSize->Set(aConfig.iWindowSize))
+		{
+		// If NAWS is not enabled and the user wants to notify the server
+		if(!iWindowSize->SendWindowSize() && aActionBuffer)
+			{
+			TInt32 event;
+			// Switch on NAWS
+			ClientRequestOption(KTelnetProtOptionWindowSize,*aActionBuffer,event);
+			}
+		else
+			// Window size is already enabled
+			// If the caller wants to notify the server
+			if(aActionBuffer)
+				// return IAC SB NAWS IS X Y IAC SE
+				iWindowSize->GetTelnetSubOption(*aActionBuffer);
+		}
+
+	// Enable/disable server logout is a passive set action
+	// If the Client wants to force a Telnet defined logout then it should call DoForceLogout()
+	if(aConfig.iAllowLogout)
+		iLogout->SetRequestPermission
+					(
+					TOptionFSM::EServerWill | TOptionFSM::EClientDo
+					);
+	else
+		iLogout->SetRequestPermission
+					(
+					TOptionFSM::ENoOption
+					);
+	// Echo
+	// First set the permissions for the Server depending on the member boolean
+	if(aConfig.iServerEcho)
+		iEcho->SetRequestPermission(TOptionFSM::EServerWill | TOptionFSM::EClientDo);
+	else
+		iEcho->SetRequestPermission(TOptionFSM::ENoOption);
+
+	// Echo is a "toggler"
+	// If we are required to supply an action and the client wants to change the echo state
+	if(aActionBuffer && iEcho->ReceiveEcho() != aConfig.iServerEcho)
+		{
+		TInt32 event;
+		// Call the routine to create the request as the client is attempting to toggle
+		// Server echo
+		ClientRequestOption(KTelnetProtOptionEcho,*aActionBuffer,event);
+		}
+	}
+
+
+TInt CProto::ClientRequestOption(TInt32 aOptionRequest,TDes8& aAction,TInt32& aEvent)
+/**
+Creates client side option requests
+Currently we only switch on options
+Ignore the errors as we can pre-check the state of the option before we call this.
+*/
+	{
+	TInt32 err = KErrNone;
+	TInt32 event;
+	TBuf8<10> action;
+
+	aEvent = TOptionFSM::ENoEvent;
+	
+	// If binary
+	if(aOptionRequest == KTelnetProtOptionBinary)
+		{
+		// Request the server to transmit in Binary
+		err = iBinary->RequestOption(TOptionFSM::EClientDo,action,event);
+		aEvent |= event;
+		aAction.Append(action);
+
+		// Tell the server we are prepared to transmit in binary
+		action.SetLength(0);
+		err = iBinary->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent |= event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionSuppressGA)
+		{
+		// Request the server to suppress the go ahead signal
+		err = iGA->RequestOption(TOptionFSM::EClientDo,action,event);
+		aEvent |= event;
+		aAction.Append(action);
+
+		// Tell the server we are prepared to suppress the go ahead signal
+		action.SetLength(0);
+		err = iGA->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent |= event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionStatus)
+		{
+		// Tell the server we are prepared to send status information
+		err = iStatus->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionLogoff)
+		{
+		// Client is forcing a logout
+		iLogout->SetRequestPermission
+					(
+					TOptionFSM::EServerWill | TOptionFSM::EClientDo
+					);
+		err = iLogout->RequestOption(TOptionFSM::EClientDo,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionWindowSize)
+		{
+		// Tell the Server we are prepared to send window size information
+		err = iWindowSize->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionTerminalType)
+		{
+		// Tell the server we are prepared to send terminal type information
+		err = iTerminalType->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionTerminalSpeed)
+		{
+		// Tell the server we are prepared to send terminal speed information
+		err = iSpeed->RequestOption(TOptionFSM::EClientWill,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+	else if(aOptionRequest == KTelnetProtOptionEcho)
+		{
+		// Echo is a special case where we toggle
+		if(iEcho->ReceiveEcho())
+			// Server Echo is currently enabled on so tell server DONT
+			err = iEcho->RequestOption(TOptionFSM::EClientDont,action,event);
+		else
+			// Server Echo is currently disabled so tell server DO 
+			err = iEcho->RequestOption(TOptionFSM::EClientDo,action,event);
+		aEvent = event;
+		aAction.Append(action);
+		}
+
+
+	return(err);
+	}
+
+TInt CProto::ProtoWrite(const TDesC8& aInBuffer,TDes8& aOutBuffer)
+/**
+Modifies the output stream :-
+Escape EIAC in Binary mode.
+PAD CR to CR NULL when not in binary mode
+*/
+	{
+	if(iBinary->SendBinary())
+		{
+		for(TInt i=0;i<aInBuffer.Length();i++)
+			{
+			if(aInBuffer[i] == KTelnetIAC)
+				aOutBuffer.Append(KTelnetIAC);
+			aOutBuffer.Append(aInBuffer[i]);
+			}
+		}
+	else
+		{
+		for(TInt i=0;i<aInBuffer.Length();i++)
+			{
+			aOutBuffer.Append(aInBuffer[i]);
+			if(aInBuffer[i] == KTelnetCR && (i == (aInBuffer.Length() - 1) || aInBuffer[i+1] != KTelnetLF))
+				{
+				aOutBuffer.Append(KTelnetNULL);
+				}
+			}
+		}
+	return(KErrNone);
+	}
+
+TInt CProto::ProtoWrite(const TTelnetUserControl& aControlCode,TDes8& aBuffer,TDes8& aUrgentBuffer)
+/**
+Writes a Telnet 854 defined control code to the server
+In the case of an Interrupt Process we need to send urgent data 
+*/
+	{
+	TInt err;
+	if(aControlCode == KTelnetIP)
+		// Interrupt Process, supported in Binary and NVT mode
+		{
+		aUrgentBuffer.Append(KTelnetIAC); // Interpret As Command *** URGENT ***
+		aUrgentBuffer.Append(KTelnetIP);  // Interrupt Process    *** URGENT ***
+		aUrgentBuffer.Append(KTelnetIAC); // Interpret As Command *** URGENT ***
+		aBuffer.Append(KTelnetDM);        // Data Mark
+		err = KErrNone;
+		}
+	else
+		{
+		// All codes valid when not in binary
+		if(!iBinary->SendBinary())
+			{
+			__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoWrite() Normal Send Control = %d"),aControlCode);
+			aBuffer.Append(KTelnetIAC);           // Interpret As Command
+			aBuffer.Append((TInt8)aControlCode);  // Code 
+			err = KErrNone;
+			}
+		else if(aControlCode == KTelnetAYT || aControlCode == KTelnetAO)
+			// In binary Are You There and Abort Output only
+			{
+			__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoWrite() Binary Send Control = %d"),aControlCode);
+			aBuffer.Append(KTelnetIAC);          // Interpret As Command
+			aBuffer.Append((TInt8)aControlCode); // Code
+			err = KErrNone;
+			}
+		else
+			{
+			err = KErrGeneral;
+			__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoWrite() ERROR Illegal Code in Binary"));
+			}
+		}
+	return(err);
+	}
+
+
+TInt CProto::ProtoRead(const TDesC8& aInBuffer,TDes8& aClientOutBuffer,TDes8& aProtoOutBuffer)
+/**
+Process data received from the Telnet Server
+Contains receive state machine plus sub-state machine for suboptions
+*/
+	{
+//	TBuf8<64> action;
+	TBuf8<128> subOptions;
+	TInt32 numEvents = 0;
+	// Loop through the input buffer byte by byte
+	for(TInt i=0;i<aInBuffer.Length();i++)
+		{
+		switch(iReceiveState)
+			{
+		case	ENormal	:
+			// Check for Interpret as command
+			if(aInBuffer[i] == KTelnetIAC)
+				{
+				// Reset the object protocol receive buffer
+				iProtReadBuffer.SetLength(0);
+				// Set the state
+				iReceiveState = EIAC;
+				__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() IAC"));
+				}
+			// Check for first character of a RFC 854 Sync
+			else if(aInBuffer[i] == KTelnetIP)
+				{
+				__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() IP"));
+				}
+			// Check for Data Mark that follows Sync and ends urgent processing
+			else if(aInBuffer[i] == KTelnetDM && iUrgentFlag)
+				{
+				iUrgentFlag = FALSE;
+				__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead()  DM"));
+				}
+			// Check we are not discarding during urgent receive waiting for DM
+			else if(!iUrgentFlag)
+				{
+				// Pass to client
+				// Normal data received, just write it to the output buffer
+				aClientOutBuffer.Append(aInBuffer[i]);
+				}
+			else
+				{
+				// Discard
+				__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Discard During Urgent"));
+				}
+			break;
+
+		case	EIAC	:
+			// Last character received was an Interpret As Command
+			// Check for DO WILL WONT DONT
+			if(aInBuffer[i] == KTelnetDO || aInBuffer[i] == KTelnetWILL || aInBuffer[i] == KTelnetDONT || aInBuffer[i] == KTelnetWONT)
+				{
+				// Add it to the protocol buffer
+				iProtReadBuffer.Append(aInBuffer[i]);
+				iReceiveState = ECommand;
+				}
+			else if(aInBuffer[i] == KTelnetSB)
+				{
+				// Suboption Start
+				iReceiveState = ESubOpt;
+				iSubOptState = ESB;
+				}
+			else
+				{
+				// Interpret As Command followed by Interpret As Command
+				if(aInBuffer[i] == KTelnetIAC)
+					{
+					// Previous Interpret As Command is Escaping a genuine 255 , legal in Binary Mode
+					if(iBinary->ReceiveBinary() && !iUrgentFlag)
+						{
+						// Pass to client
+						aClientOutBuffer.Append(aInBuffer[i]);
+						}
+					else
+						{
+						// Discard whilst in urgent or illegal in NVT mode
+						__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Illegal IAC Urgent = %d"),iUrgentFlag);
+						}
+					}
+				else
+					{
+					// Unsupported command
+					__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Unsupported Command = %d"),aInBuffer[i]);
+					}
+				// Back to normal state
+				iReceiveState = ENormal;
+				}
+			break;
+
+		case	ESubOpt		:
+			// Receiving a Suboption sequence
+			if(iSubOptState == ESB)
+				{
+				// TERMINAL-TYPE,TERMINAL-SPEED,STATUS
+				iProtReadBuffer.Append(aInBuffer[i]);
+				iSubOptState = EOption;
+				}
+			else if(iSubOptState == EOption)
+				{
+				// SEND or IS
+				iProtReadBuffer.Append(aInBuffer[i]);
+				iSubOptState = ERequest;
+				}
+			else if(iSubOptState == ERequest)
+				{
+				// IAC or STATUS info from the server
+				if(aInBuffer[i] == KTelnetIAC)
+					{
+					iSubOptState = EEndIAC;
+					}
+				iProtReadBuffer.Append(aInBuffer[i]);
+				}
+			else if(iSubOptState == EEndIAC && aInBuffer[i] == KTelnetSE)
+				{
+				// SE Suboption End
+				// Scan through the sequence we have just received
+				// Currently we support sending Window Size ,Terminal Speed and Status Information
+				if(iProtReadBuffer[0] == KTelnetProtOptionTerminalType)
+					{
+					// Get our window size
+					iTerminalType->GetTelnetSubOption(aProtoOutBuffer);
+					__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() SE Term Type"));
+					}
+				else if(iProtReadBuffer[0] == KTelnetProtOptionTerminalSpeed)
+					{
+					// Get our speed
+					iSpeed->GetTelnetSubOption(aProtoOutBuffer);
+					__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() SE Term Speed"));
+					}
+				else if(iProtReadBuffer[0] == KTelnetProtOptionStatus)
+					{
+					// Check whether it's a request for us to send or it's status info from the server
+					if(iProtReadBuffer[1] == KTelnetCommandSEND)
+						{
+						// Server wants our status
+						GetOptionStatus(aProtoOutBuffer);
+						__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() SE STATUS SEND"));
+						}
+					else if(iProtReadBuffer[1] == KTelnetCommandIS)
+						{
+						// Status info from the server
+						// We are currently doing nothing with it but in case
+						// the client wants to know the server's perceived state of the connection
+						// store it in the object
+						iServerStatus = iProtReadBuffer;
+						__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() SE STATUS IS"));
+						}
+					else
+						{
+						// Corruption
+						__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() STATUS SubOption ERROR"));
+						}
+					}
+					iReceiveState = ENormal;
+				}
+			else
+				{
+				iReceiveState = ENormal;
+				__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() SubOption Unexpected Receive ERROR"));
+				}
+
+			break;
+
+		case	ECommand	:
+			// Last byte received was a DO, WILL, WONT or DONT
+			{
+			TInt32 event = TOptionFSM::ENoEvent;
+			TOptionFSM::TRequests	request = TOptionFSM::ENoOption;
+			TBuf8<64> action;
+
+			// Map to our internal command code
+			if(iProtReadBuffer[0] == KTelnetDO)
+				request = TOptionFSM::EServerDo;
+			else if(iProtReadBuffer[0] == KTelnetWILL)
+				request = TOptionFSM::EServerWill;
+			else if(iProtReadBuffer[0] == KTelnetWONT)
+				request = TOptionFSM::EServerWont;
+			else if(iProtReadBuffer[0] == KTelnetDONT)
+				request = TOptionFSM::EServerDont;
+
+			// switch on the RFC option
+			// Most cases get a possible action and a possible event
+			switch(aInBuffer[i])
+				{
+			case	KTelnetProtOptionEcho			:
+				iEcho->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC2(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Echo Event = %d Request = %d"),event,request);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			case	KTelnetProtOptionSuppressGA		:
+				iGA->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() GA Event = %d"),event);
+				break;
+
+			case	KTelnetProtOptionBinary			:
+				iBinary->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC2(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Binary Event = %d request = %d"),event,request);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			case	KTelnetProtOptionTerminalSpeed	:
+				iSpeed->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Terminal Speed Event = %d"),event);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			case	KTelnetProtOptionTerminalType	:
+				iTerminalType->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Terminal Type Event = %d"),event);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			case	KTelnetProtOptionWindowSize		:
+				// Winbdow size, send our window size if it's enabled
+				iWindowSize->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				if(event == TOptionFSM::EClientEnabled)
+					{
+					iWindowSize->GetTelnetSubOption(subOptions);
+					}
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Window Size Event = %d"),event);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			case	KTelnetProtOptionLogoff			:
+				// Server may want to log us out
+				iLogout->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Logout Event = %d"),event);
+				break;
+
+			case	KTelnetProtOptionStatus			:
+				iStatus->RequestOption(request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC2(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Status Event = %d request = %d"),event,request);
+				if(event != TOptionFSM::ENoEvent)
+					numEvents++;
+				break;
+
+			default									:
+				iUnknown->RequestUnknown(aInBuffer[i],request,action,event);
+				if(action.Length())
+					aProtoOutBuffer.Append(action);
+				__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Unsupported Option = %d"),aInBuffer[i]);
+				break;
+
+				}
+			}
+			iReceiveState = ENormal;
+			break;
+
+		default			:
+			break;
+			}
+		}
+	// If any option events have occured then tell the client side so it can retrieve the status
+	// if it's interested
+	if(numEvents)
+		iNotifier->ProtoEvent();
+
+	if(subOptions.Length())
+		{
+		aProtoOutBuffer.Append(subOptions);
+		__FLOG_STATIC1(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::ProtoRead() Add SubOpt Length = %d"),subOptions.Length());
+		}
+	return(KErrNone);
+	}
+
+void CProto::Reset()
+/**
+Clear states
+Don't bother to zero buffers as state machine does them
+*/
+	{
+	iReceiveState =			ENormal;
+	iUrgentFlag =			FALSE;
+
+	iBinary->Reset();
+	iLogout->Reset();
+	iWindowSize->Reset();
+	iSpeed->Reset();
+	iStatus->Reset();
+	iTerminalType->Reset();
+	iEcho->Reset();
+	iGA->Reset();
+	}
+
+
+CProto::CProto()
+/**
+Constructor
+*/
+	{
+	}
+
+
+CProto::~CProto()
+/**
+Destructor
+*/
+	{
+	__FLOG_STATIC(KTelnetLoggingCompnt(),KTelnetLoggingTag(),_L("CProto::D'Tor"));
+
+	delete iUnknown;
+	delete iStatus;
+	delete iGA;
+	delete iSpeed;
+	delete iLogout;
+	delete iTerminalType;
+	delete iWindowSize;
+	delete iEcho;
+	delete iBinary;
+	}