// Copyright (C) 2000
//          by the Massachusetts Institute of Technology
//
//    Export of this software from the United States of America may
//    require a specific license from the United States Government.  It
//    is the responsibility of any person or organization contemplating
//    export to obtain such a license before exporting.
//
// WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
// distribute this software and its documentation for any purpose and
// without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both that copyright notice and
// this permission notice appear in supporting documentation, and that
// the name of M.I.T. not be used in advertising or publicity pertaining
// to distribution of the software without specific, written prior
// permission.  M.I.T. makes no representations about the suitability of
// this software for any purpose.  It is provided "as is" without express
// or implied warranty.

// winsshDoc.cpp : implementation of the CWinsshDoc class
//

#include "winsshDoc.h"
#include "MainFrm.h"
#include "Errors.h"
//#include <memory.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWinsshDoc

IMPLEMENT_DYNCREATE(CWinsshDoc, CDocument)

BEGIN_MESSAGE_MAP(CWinsshDoc, CDocument)
	//{{AFX_MSG_MAP(CWinsshDoc)
	ON_COMMAND(IDM_FILE_NEWCONNECT, OnNewConnection)
	ON_COMMAND(IDM_FILE_CONNECT, OnFileConnect)
	ON_COMMAND(IDM_FILE_DISCONNECT, OnFileDisconnect)
	ON_UPDATE_COMMAND_UI(IDM_FILE_NEWCONNECT, OnUpdateFileNewconnect)
	ON_UPDATE_COMMAND_UI(IDM_FILE_CONNECT, OnUpdateFileConnect)
	ON_UPDATE_COMMAND_UI(IDM_FILE_DISCONNECT, OnUpdateFileDisconnect)
	ON_COMMAND(ID_OVERRIDE_CUT, OnOverrideCut)
	//}}AFX_MSG_MAP
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_CIPHER, OnUpdateCipher)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_TERM_AND_SIZE, OnUpdateTermAndSize)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWinsshDoc construction/destruction

//override
//CRichEditCntrItem* CWinsshDoc::CreateClientItem(REOBJECT* preo) const
//{
	// cast away constness of this
//	return new CWinsshCntrItem(preo, (CWinsshDoc*)this);
//}

CWinsshDoc::CWinsshDoc()
{
	m_pSocket = NULL;
	m_iPort	= 0;
	m_iRecvBufBusy = 0;
	m_iAppState = APP_STATE_START;
	m_bTermModify = FALSE;
	m_szTermType = "VT100";
	m_pTC = NULL;


	if ((m_pucNetworkReceiveBuffer = new UCHAR[SSH_MAX_PACKET_LENGTH * 2]) ==
		NULL) {
		// TODO: Fatal Memory
	}
	if ((m_pucReceivePacketBuffer = new UCHAR[SSH_MAX_PACKET_LENGTH]) ==
		NULL) {
		// TODO: Fatal Memory
	}
	if ((m_pucSendPacketBuffer = new UCHAR[SSH_MAX_PACKET_LENGTH]) ==
		NULL) {
		// TODO: Fatal Memory
	}

	// Really screwy extern variable for error control.
	g_theDoc = this;

	// Give the SSH Object pointer to send packet buffer
	cSshHandler.SetSendPacketBuffer(m_pucSendPacketBuffer);


} // end CWinsshDoc::CWinsshDoc();

CWinsshDoc::~CWinsshDoc()
{
	// Free Willy (Memory)
#ifdef DEBUG_SSH
	EndSshDebug();
#endif

	delete m_pucNetworkReceiveBuffer;
	delete m_pucReceivePacketBuffer;
	delete m_pucSendPacketBuffer;

	delete m_pSocket;
	delete m_pTC;
}

//////////////////////////////////////////////////////////////////////////////
// Start up Message Handlers
//

BOOL CWinsshDoc::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
	{	
		return FALSE;
	}
	else return TRUE;
}

BOOL CWinsshDoc::SaveModified() 
{
	// Tells App not to ask user to save screen content by default
	
	return TRUE;
}

void CWinsshDoc::DeleteContents() 
{
	if (m_pSocket != NULL)
	{
		BYTE Buffer[50];
		m_pSocket->ShutDown();
		while(m_pSocket->Receive(Buffer,50) > 0);
	}

	delete m_pSocket;
	m_pSocket = NULL;


	// Final parent cleanup
	CDocument::DeleteContents();

	//AfxCheckMemory();

}

void CWinsshDoc::CloseConnection() {

	if (m_pSocket != NULL)
	{
		BYTE Buffer[50];
		m_pSocket->ShutDown();
		while(m_pSocket->Receive(Buffer,50) > 0);
	}
	SetApplicationState(APP_STATE_DISCONNECTED);

	m_iRecvBufBusy = 0;	// Reset Mutex.
	m_iNetBufLength = 0; // Reset Counter.

#ifdef DEBUG_SSH
	Debug("CWinsshDoc::CloseConnection() - Killing Socket Connection");
#endif

	
} // End CloseConnection()

/////////////////////////////////////////////////////////////////////////////
// CWinsshDoc Operations

BOOL CWinsshDoc::ConnectSocket(LPCTSTR lpszHandle, LPCTSTR lpszAddress, UINT iPort)
{
	m_szSockHandle	= lpszHandle;
	m_iPort			= iPort;
	m_pSocket		= new CSshSocket(this);
	m_pTC				= new CTermCap();

	if (!m_pSocket->Create())
	{
		delete m_pSocket;
		m_pSocket = NULL;
		AfxMessageBox(IDS_CREATEFAILED);
		SetApplicationState(APP_STATE_DISCONNECTED);
		return FALSE;
	}

	while (!m_pSocket->Connect(lpszAddress, iPort))
	{
		if (AfxMessageBox(IDS_RETRYCONNECT,MB_YESNO) == IDNO)
		{
			delete m_pSocket;
			m_pSocket = NULL;
			SetApplicationState(APP_STATE_DISCONNECTED);
			return FALSE;
		}
	}

	CString szSendBuf;
	if (iPort == 23)  // Telnet Negotiation purposes only
	{
		szSendBuf = "\x0ff\x0fd\x003\x0ff\x0fb\x018";
		SocketSend((unsigned char *)(LPCTSTR)szSendBuf, szSendBuf.GetLength());
	}

	// VT100 starts
	m_pTC->Initialize(TC_VT100);


	SetApplicationState(APP_STATE_CONNECTING);

#ifdef DEBUG_SSH
	Debug("CWinsshDoc::ConnectSocket() - Socket Connected.");
#endif

	return TRUE;
}


void CWinsshDoc::SocketEncryptSend(CString& strText)
{

	// Only allow data to be sent if in interactive mode.
	if ((m_iProtocolState == PROTOCOL_SSH) && (m_iSshState == SSH_STATE_INTERACTIVE)) {
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SocketEncryptSend() - Encrypting STDIN Data and sending.");
		Debug("CWinsshDoc::SocketEncryptSend() - TextLength = %d.", strText.GetLength());
#endif
		cSshHandler.CmsgStdinData((unsigned char *)(LPCSTR)strText, strText.GetLength());
		m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
		if (m_pSocket == NULL) {
			// Lost our connection
			ErrorApplication(ERROR_SSH_MSG_DISCONNECT);
			return;
		}

		m_pSocket->Send(m_pucSendPacketBuffer, m_iSendBufferLength);
	}
	else if (m_iProtocolState == PROTOCOL_TELNET) {
		if (m_pSocket == NULL) {
			// Lost our connection
			ErrorApplication(ERROR_SSH_MSG_DISCONNECT);
			return;
		}

		m_pSocket->Send((LPCTSTR)strText, strText.GetLength());
	}

}  //end SocketEncryptSend()

void CWinsshDoc::SocketEncryptSend(unsigned char* pucSendBuf, int iCount)
{
	if ((m_iProtocolState == PROTOCOL_SSH) && (m_iSshState == SSH_STATE_INTERACTIVE)) {
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SocketEncryptSend() - Encrypting STDIN Data and sending.");
		Debug("CWinsshDoc::SocketEncryptSend() - TextLength = %d.", iCount);
#endif
		cSshHandler.CmsgStdinData(pucSendBuf, iCount);
		m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
		if (m_pSocket == NULL) {
			// Lost our connection
			ErrorApplication(ERROR_SSH_MSG_DISCONNECT);
			return;
		}
		m_pSocket->Send(m_pucSendPacketBuffer, m_iSendBufferLength);
	}
	else if (m_iProtocolState == PROTOCOL_TELNET) {
		if (m_pSocket == NULL) {
			// Lost our connection
			ErrorApplication(ERROR_SSH_MSG_DISCONNECT);
			return;
		}
		m_pSocket->Send(pucSendBuf, iCount);
	}
}

// This is for the direct sends for the SSH negotiation..
void CWinsshDoc::SocketSend(unsigned char * pucSend, int iLength) {

	if (m_pSocket == NULL) {
		// Lost our connection
		ErrorApplication(ERROR_SSH_MSG_DISCONNECT);
		return;
	}

	m_pSocket->Send(pucSend, iLength);
	return;

} // end CWinsshDoc::SocketSend()

void CWinsshDoc::ProcessPendingRead() 
{
	SocketReceive();
}

void CWinsshDoc::ProcessPendingClose(int nError) 
{
	CString szAnnouncement;
	switch (nError)
	{
	case 0:
		szAnnouncement = "Connection Closed."; break;
	case WSAENETDOWN:
		szAnnouncement = "Unexpected Connection Closed"; break;
	case WSAECONNRESET:
		szAnnouncement = "Connection reset by remote Host"; break;
	case WSAECONNABORTED:
		szAnnouncement = "Connected Timed Out"; break;
	default:
		szAnnouncement = "Connected Closed!"; break;
	}

	SetApplicationState(APP_STATE_DISCONNECTED);

#ifdef SSH_DEBUG
	Debug("CWinsshDoc::ProcessPendingClose() - %s", szAnnouncement);
#endif

	if (m_pSocket != NULL)
	{
		BYTE Buffer[50];
		m_pSocket->ShutDown();
		while(m_pSocket->Receive(Buffer,50) > 0);
		m_pSocket->Close();
	}
	
	delete m_pSocket;
	m_pSocket = NULL;
	delete m_pTC;
	m_pTC = NULL;

}

void CWinsshDoc::SocketReceive()
{
	int iError;
	int iReceivedBytes;
	int iMessageLength;
	int iPacketLength;

	if (m_iRecvBufBusy > 0) {
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SocketReceive() - Sleeping!");
#endif
		while (m_iRecvBufBusy++ < 30) 
		// Sleep when the still working with the network buffers.
		Sleep(500);
		//TODO: Error here - Socket Timed out / Possible exception
	}

	m_iRecvBufBusy = 1;		// set mutex to block mode until we're clear

	//-----------------------------------------------------
	// Handle SSH Protocol Here.
	if (m_iProtocolState == PROTOCOL_SSH) {

		// Append incoming data to m_pucNetworkReceiveBuffer.  Check for a full packet at the beginning
		// of m_szRecvBuffer.  If there is a full packet, send it on to be processed via
		// buffer and clear our m_szRecvBuffer of packet. Otherwise return.
		// iMessageLength = iPacketLength + (8 - (iPacketLength % 8)) + 4;
		if (m_iSshState != SSH_STATE_INIT) {
			
			// Fill the buffer
			iReceivedBytes = m_pSocket->Receive(m_pucNetworkReceiveBuffer +
				m_iNetBufLength, MAX_PACKET_LENGTH);
			m_iNetBufLength += iReceivedBytes;

			iPacketLength =
				cSshHandler.iGetPacketLength(m_pucNetworkReceiveBuffer);
			iMessageLength = iPacketLength + (8 - (iPacketLength % 8)) + 4;

#ifdef DEBUG_SSH
			Debug("CWinsshDoc::SocketReceive() - Received %d iReceivedBytes", iReceivedBytes);
#endif
				
			// If MessageLength larger than what is in buffer, return 
			// and wait for a full packet
			if (iMessageLength > m_iNetBufLength) {
				m_iRecvBufBusy = 0;
				return;
			}
			else {
				// Else get Packet out of m_pucNetworkReceiveBuffer
				memcpy (m_pucReceivePacketBuffer, m_pucNetworkReceiveBuffer, iMessageLength);


				// Move the bytes in NetworkReceiveBuffer down if there is more data.
				m_iNetBufLength -= iMessageLength;
				if (m_iNetBufLength != 0) {
					memmove (m_pucNetworkReceiveBuffer, m_pucNetworkReceiveBuffer + iMessageLength, m_iNetBufLength);
				}
			}
		}  // end if (m_iSshState!=INIT)
		else {
	
			// Fill the buffer
			iReceivedBytes =
			m_pSocket->Receive((void*)m_pucNetworkReceiveBuffer, 
				MAX_PACKET_LENGTH);
				
		} // else SSH_STATE_INIT

		do 
		{
			switch (m_iSshState) {
				
				case SSH_STATE_INIT:
					// TODO:
					// Should check the major protocol version number here.

#ifdef DEBUG_SSH
					Debug("CWinsshDoc::SocketReceive() - SSH_STATE_INIT");
					Debug("CWinsshDoc::SocketReceive() - Sending Protocol Version: %s", 
						cSshHandler.pszGetProtocolVersion());
#endif
					// Send our version number and jump to next state.
					SocketSend((unsigned char *)cSshHandler.pszGetProtocolVersion(), 
						strlen(cSshHandler.pszGetProtocolVersion()));
					m_iSshState = SSH_STATE_GET_PUBLIC_KEY;
					break;

				case SSH_STATE_GET_PUBLIC_KEY:

#ifdef DEBUG_SSH
					Debug("CWinsshDoc::SocketReceive() - SSH_STATE_GET_PUBLIC_KEY");
					Debug("CWinsshDoc::SocketReceive() - Receiving first message from server");
#endif
					iError = cSshHandler.iProcessIncomingPacket(SSH_SMSG_PUBLIC_KEY, m_pucReceivePacketBuffer);

					if (iError == ERROR_SSH_MSG_DEBUG) {
						m_iRecvBufBusy = 0;	// Finished processing, 
						break;
					}
					if (iError > ERROR_SSH_START) {
						ErrorApplication(iError);
					}

					// Get the CMSG_CMSG_SESSION_KEY packet
					m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
					SocketSend(m_pucSendPacketBuffer, m_iSendBufferLength);
					
					m_iSshState = SSH_STATE_SEND_USERNAME;
					
					break;

				case SSH_STATE_SEND_USERNAME:
#ifdef DEBUG_SSH
					Debug("CWinsshDoc::SocketReceive() - SSH_STATE_SEND_USERNAME");
#endif

					iError = cSshHandler.iProcessIncomingPacket(SSH_SMSG_SUCCESS, m_pucReceivePacketBuffer);
					if (iError == ERROR_SSH_MSG_DEBUG) {
						m_iRecvBufBusy = 0;	// Finished processing, 
						break;
					}
					else if (iError == SSH_SMSG_SUCCESS) {
#ifdef DEBUG_SSH
						Debug("CWinsshDoc::SocketReceive() - Server reports succesful key exchange");
#endif
						// Create and send username packet.
						cSshHandler.CmsgUser();
						m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
						SocketSend(m_pucSendPacketBuffer, m_iSendBufferLength);
#ifdef DEBUG_SSH
						Debug("CWinsshDoc::SocketReceive() - Sent username packet");		
#endif
						m_iSshState = SSH_STATE_AUTHENTICATE;
					} // end if (CSshHandler)
					else {
						ErrorApplication(iError);
					} // end else


					break;

				case SSH_STATE_AUTHENTICATE:
				case SSH_STATE_AUTHENTICATE_PASSWORD:

					iError = cSshHandler.iProcessIncomingPacket(SSH_SMSG_FAILURE, 
							m_pucReceivePacketBuffer);
#ifdef DEBUG_SSH
					if (m_iSshState == SSH_STATE_AUTHENTICATE)
						Debug("CWinsshDoc::SocketReceive() - SSH_STATE_AUTHENTICATE");
					else if (m_iSshState == SSH_STATE_AUTHENTICATE_PASSWORD)
						Debug("CWinsshDoc::SocketReceive() - SSH_STATE_AUTHENTICATE_PASSWORD");
#endif
					if (iError == SSH_MSG_DEBUG) {
						break;
					}
					if (iError == SSH_SMSG_FAILURE) {
						// Either username / password unknown or we start authentication.
						if (m_iSshState == SSH_STATE_AUTHENTICATE) {
							m_iSshState = SSH_STATE_AUTHENTICATE_PASSWORD;

#ifdef DEBUG_SSH
							Debug("CWinsshDoc::SocketReceive() - Password Authentication");
							Debug("CWinsshDoc::SocketReceive() - Building Password packet and sending.");
#endif
							cSshHandler.CmsgAuthPassword();
							m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
							SocketSend(m_pucSendPacketBuffer, m_iSendBufferLength);
						}
						else if (m_iSshState == SSH_STATE_AUTHENTICATE_PASSWORD) {

							ErrorApplication(ERROR_WINSSH_USERNAME_PASSWORD_NOT_RECOGNIZED);
							return;
						}
					} 
					else if (iError == SSH_SMSG_SUCCESS) {
#ifdef DEBUG_SSH
						Debug("CWinsshDoc::SocketReceive() - Succesful Authentication!");
						Debug("CWinsshDoc::SocketReceive() - Sending Request Pty Packet");
#endif
						
						for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
						{
							CView* pView = GetNextView(pos);
							CWinsshView* pWinsshView = DYNAMIC_DOWNCAST(CWinsshView, pView);
							cSshHandler.CmsgRequestPty("vt100",pWinsshView->m_usTermY, pWinsshView->m_usTermX);
							m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
							SocketSend(m_pucSendPacketBuffer, m_iSendBufferLength);
							m_iSshState = SSH_STATE_PREPATORY;
						}
					}
					else if (iError > ERROR_SSH_START) {
						ErrorApplication(iError);
						return;
					}
					break;

				case SSH_STATE_PREPATORY:
				case SSH_STATE_PREPATORY_SHELL_REQUESTED:

					iError = cSshHandler.iProcessIncomingPacket(SSH_SMSG_FAILURE, 
							m_pucReceivePacketBuffer);

					if (iError == ERROR_SSH_MSG_DEBUG) {
						m_iRecvBufBusy = 0;	// Finished processing, 
						break;
					}
					else if (iError == SSH_SMSG_SUCCESS) {
						if (m_iSshState == SSH_STATE_PREPATORY) {
#ifdef DEBUG_SSH
							Debug("CWinsshDoc::SocketReceive() - SSH_STATE_PREPATORY");
							Debug("CWinsshDoc::SocketReceive() - Sending request to server to Execute Shell");
							Debug("CWinsshDoc::SocketReceive() - Entering Interactive Session Mode.");
#endif
							cSshHandler.CmsgExecShell();
							m_iSendBufferLength = cSshHandler.iGetPacketBufferLength();
							SocketSend(m_pucSendPacketBuffer, m_iSendBufferLength);
							m_iSshState = SSH_STATE_INTERACTIVE;
							SetApplicationState(APP_STATE_CONNECTED);

						} // end if SSH_STATE_PREPATORY
					} // end if iError()
					else if (iError == SSH_SMSG_FAILURE) {
						if (m_iSshState == SSH_STATE_PREPATORY) {
							// TODO: Invalid Terminal?
#ifdef DEBUG_SSH
							Debug("Invalid Terminal?.");
#endif
							CloseConnection();	
							return;
						}					
						else if (m_iSshState == SSH_STATE_PREPATORY_SHELL_REQUESTED) {
							// TODO: Not able to execute shell.
#ifdef DEBUG_SSH
							Debug("Not able to execute shell.");
#endif
							CloseConnection();
							return;
						}
					}
					else if (iError > ERROR_SSH_START) {
						ErrorApplication(iError);
					} 
					break;

				case SSH_STATE_INTERACTIVE:

					// Asynchronus Data Exchange from now on.
					iError = cSshHandler.iProcessIncomingPacket(SSH_SMSG_FAILURE, 
						m_pucReceivePacketBuffer);

#ifdef DEBUG_SSH
					Debug("CWinsshDoc::SocketReceive() - SSH_STATE_INTERACTIVE");
#endif

					if (iError == ERROR_SSH_MSG_DEBUG) {
						// do nothing
						break;
					}
					else if (iError == SSH_SMSG_EXITSTATUS) {
						CloseConnection();
					}
					else if (iError > ERROR_SSH_START) {
						ErrorApplication(iError);
						break;
					}
					else if (iError == SSH_SMSG_STDOUT_DATA) {
#ifdef DEBUG_SSH
						Debug("CWinsshDoc::SocketReceive() - Displaying STDOUT Data");
#endif
						DisplayMsg( cSshHandler.iGetStdoutLength(), (void *)cSshHandler.pucGetStdoutString());
					} // end else if STDOUT_DATA
					break;
			} // end switch (m_iSshState)

			// See if there are any more packets in receive buffer to process.
			if (m_iNetBufLength < 5)
				break;
						
			iPacketLength = cSshHandler.iGetPacketLength(m_pucNetworkReceiveBuffer);
			iMessageLength = iPacketLength + (8 - (iPacketLength % 8)) + 4;

#ifdef DEBUG_SSH
			Debug("CWinsshDoc::SocketReceive() - iMessageLength = %d, m_iNetBufLength = %d", 
				iMessageLength, m_iNetBufLength);
#endif
					
			// If MessageLength larger than what is in buffer, return 
			// and wait for a full packet
			if (iMessageLength > m_iNetBufLength)
				break;
			else {
				// Else get Packet out of m_pucNetworkReceiveBuffer
				memcpy (m_pucReceivePacketBuffer, m_pucNetworkReceiveBuffer, iMessageLength);
					// Move the bytes in NetworkReceiveBuffer down if there is more data.
					m_iNetBufLength -= iMessageLength;
				if (m_iNetBufLength != 0) 
					memmove (m_pucNetworkReceiveBuffer, m_pucNetworkReceiveBuffer + 
						iMessageLength, m_iNetBufLength);			
			}  // end else

		} while (1); // keep handling till the ReceiveBuffer is Empty.
	} // end if (m_iProtocolState)
	// end Handling SSH Protocol
	//-----------------------------------------------------

	//-----------------------------------------------------
	// Handle Telnet Protocol Here.
	else if (m_iProtocolState == PROTOCOL_TELNET) {
			// Fill the buffer
//			iReceivedBytes = m_pSocket->Receive(m_pucNetworkReceiveBuffer +
//				m_iNetBufLength, 65536);
//			m_iNetBufLength += iReceivedBytes;

		m_iNetBufLength = m_pSocket->Receive((void*)m_pucNetworkReceiveBuffer, MAX_PACKET_LENGTH);

		LPCSTR pTemp = m_szTempBuf.GetBuffer(m_iNetBufLength);
		memcpy((void *)pTemp, m_pucNetworkReceiveBuffer, m_iNetBufLength);
		m_szTempBuf.ReleaseBuffer(m_iNetBufLength);

#ifdef DEBUG_SSH
		// HexDump Network Content 
		DebugPlain("Received TELNET Packet:");
		DebugPacketWithASCII((unsigned char *)(LPCSTR)m_pucNetworkReceiveBuffer, m_iNetBufLength);
#endif 
		// Filter all the (IAC) commands (Telnet only)
		if (m_szTempBuf.GetAt(0) == '\x0ff') {
			while (m_szTempBuf.GetLength() >= 3)
			{
				if ((m_szTempBuf.GetAt(0) == '\x0ff') && (m_szTempBuf.GetAt(1) == '\x0fa'))
					NegotiateTerminal();
				else if (m_szTempBuf.GetAt(0) == '\x0ff')
					NegotiateOthers();
				else {
					break;
				}
			}//end while
			SetApplicationState(APP_STATE_CONNECTED);
		}  // end if()
		DisplayMsg(m_szTempBuf);

	} // end else if (PROTOCOL_TELNET)
	// end Handling Telnet Protocol
	//-----------------------------------------------------

	m_iRecvBufBusy = 0;	// Finished processing, 
								// can process incoming packets.

#ifdef DEBUG_SSH
	// Clear out between receives (makes debugging memory easier)
	if (m_iProtocolState == PROTOCOL_SSH)
		memset (m_pucReceivePacketBuffer, 0, 5000);
	Debug("CWinsshDoc::SocketReceive() - Leaving");
#endif

} // end CWinsshDoc::SocketReceive()

//----------------------------------//
// Negotiation functions for Telnet //
//----------------------------------//

// Sends the message "My Terminal is VT100"
void CWinsshDoc::NegotiateTerminal()
{	// Sends the message "My Terminal is VT100"
	CString szSendBuf;
	// ff fa 18 01 ff f0
	m_szTempBuf = m_szTempBuf.Right(m_szTempBuf.GetLength()-6);
	szSendBuf = "\x0ff\x0fa\x018";
	szSendBuf += '\0';									// braindead CString workaround
	szSendBuf += "\x056\x054\x031\x030\x030\x0ff\x0f0";	// I said "VT100" =)
	SocketSend((unsigned char *)(LPCTSTR)szSendBuf, szSendBuf.GetLength());
	//DisplayMsg(_T("Server Requests for Terminal Type\n"));
#ifdef DEBUG_SSH
	Debug("CWinsshDoc::NegotiateTerminal() - Server Requests for Terminal Type");
#endif
}
	

void CWinsshDoc::NegotiateOthers()
{
	CString szSendBuf;
	char	uszHexStorage[5];

	if (m_szTempBuf.Left(2) == "\x0ff\x0fd")  // DO's
	{
		if (m_szTempBuf.GetAt(2) != '\x018')
		{
			szSendBuf = "\x0ff\x0fc";
			szSendBuf += m_szTempBuf.GetAt(2);
			SocketSend((unsigned char *)(LPCTSTR)szSendBuf, szSendBuf.GetLength());
		}
		// Format Negotation status string
		sprintf(uszHexStorage, "%2.2X\n", (BYTE)m_szTempBuf.GetAt(2));
		szSendBuf = "Server asks: (D0) 0x";
		szSendBuf += uszHexStorage;
		// Delete the processed content
		m_szTempBuf = m_szTempBuf.Right(m_szTempBuf.GetLength()-3);
	}
	else if (m_szTempBuf.Left(2) == "\x0ff\x0fb") // Will's
	{
		if (m_szTempBuf.GetAt(2) == '\x005')		//(STATUS)
		{
			szSendBuf = "\x0ff\x0fe\x005";			// reply (DONT) (STATUS)
			SocketSend((unsigned char *)(LPCTSTR)szSendBuf, szSendBuf.GetLength());
		}
		else if (m_szTempBuf.GetAt(2) == '\x001')//(ECHO)
		{
			szSendBuf = "\x0ff\x0fd\x001";			// reply (DO) (ECHO)
			SocketSend((unsigned char *)(LPCTSTR)szSendBuf, szSendBuf.GetLength());
		}
		// Format Negotation status string
		sprintf(uszHexStorage, "%2.2X\n", (BYTE)m_szTempBuf.GetAt(2));
		szSendBuf = "Server offers: (WILL) 0x";
		szSendBuf += uszHexStorage;
		// Delete the processed content
		m_szTempBuf = m_szTempBuf.Right(m_szTempBuf.GetLength()-3);
	}
	else if (m_szTempBuf.Left(2) == "\x0ff\x0fe")
	{
		// Format Negotation status string
		sprintf(uszHexStorage, "%2.2X", (BYTE)m_szTempBuf.GetAt(2));
		szSendBuf = "Server replies: (DONT) 0x";
		szSendBuf += uszHexStorage;
		// Delete the processed content
		m_szTempBuf = m_szTempBuf.Right(m_szTempBuf.GetLength()-3);
	}

#ifdef DEBUG_SSH
	Debug("CWinsshDoc::NegotiateOthers() - %s", szSendBuf);
#endif
	//DisplayMsg(szSendBuf);
}


///////////////////////////////////////////////////////////////////
// All sorts of display functions lies here
//

void CWinsshDoc::DisplayMsg(CString& szText)
{
	for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
	{
		CView* pView = GetNextView(pos);
		CWinsshView* pWinsshView = DYNAMIC_DOWNCAST(CWinsshView, pView);
		pWinsshView->DisplayVtText(szText);
	} // end for (pos)
} // end CWinsshDoc::DisplayMsg()


void CWinsshDoc::DisplayMsg(int Length, void* Text)
{
	CString szText;
	memcpy(szText.GetBuffer(Length), Text, Length);
	szText.ReleaseBuffer(Length);
	DisplayMsg(szText);
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshDoc diagnostics

#ifdef _DEBUG
void CWinsshDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CWinsshDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

//----------//
// Handlers //
//----------//

void CWinsshDoc::SetTitle(LPCTSTR lpszTitle) 
{
	// intercepts the ugly "untitled" tag
	if (strcmp(lpszTitle,"Untitled") == 0)
		CDocument::SetTitle("Disconnected");
	else
		CDocument::SetTitle(lpszTitle);

} // end CWinsshDoc::SetTitle()

//-------------------//
// Menu Bar Handlers //
//-------------------//

//void CWinsshDoc::OnNego() // SSH VERSION
//{
//	// TODO: Add your command handler code here
//	if (m_pSocket != NULL)
//	{
//		SocketSend((unsigned char *)"\0\0\0", 3);
//	}
//}


//void CWinsshDoc::OnTestSessionmain() 
//{
//	CSessionMain mySession("Winssh pre-Beta Property Sheet");
//	mySession.DoModal();
//}


void CWinsshDoc::SetApplicationState(int iState) {

	CString szMessage;
	CString szNewTitle;
	
	m_iAppState = iState;

	HICON hIcon;

	POSITION pos = GetFirstViewPosition();
	CView* pFirstView = GetNextView( pos );
	
	switch (iState) {

		case APP_STATE_START:
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SetApplicationState() - APP_STATE_START");
#endif 


			return;
	
		case APP_STATE_CONNECTING:
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SetApplicationState() - APP_STATE_CONNECTING");
#endif 
			szNewTitle = "Connecting...";
			SetTitle(szNewTitle);
			pFirstView->GetParentFrame()->SetMessageText((LPCSTR)szNewTitle);
			return;

		case APP_STATE_CONNECTED:
#ifdef DEBUG_SSH
			Debug("CWinsshDoc::SetApplicationState() - APP_STATE_CONNECTED");
#endif
			if (m_iProtocolState == PROTOCOL_SSH) {
				szNewTitle = m_szHostname + " [ssh] ";
				hIcon=AfxGetApp()->LoadIcon(IDI_BLUEFISSH);
				AfxGetApp()->m_pMainWnd->SetIcon(hIcon, TRUE);
			}
			else if (m_iProtocolState == PROTOCOL_TELNET) {
				hIcon=AfxGetApp()->LoadIcon(IDI_REDFISSH);
				AfxGetApp()->m_pMainWnd->SetIcon(hIcon, TRUE);
				szNewTitle = m_szHostname + " [telnet] ";
			}
			SetTitle(szNewTitle);

			szMessage = "Connected to host " + m_szHostname;
			pFirstView->GetParentFrame()->SetMessageText((LPCSTR)szMessage);
			DestroyCaret();
			return;

		case APP_STATE_DISCONNECTED:
#ifdef DEBUG_SSH
			Debug("CWinsshDoc::SetApplicationState() - APP_STATE_DISCONNECTED");
#endif 
			szNewTitle = "Disconnected";
			SetTitle(szNewTitle);
			hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
			AfxGetApp()->m_pMainWnd->SetIcon(hIcon, TRUE);

			szMessage = ("\r\nConnection Closed.\r\n");
			DisplayMsg(szMessage);
			szMessage = "Disconnected";
			pFirstView->GetParentFrame()->SetMessageText((LPCSTR)szMessage);

			return;

		case APP_STATE_END:
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::SetApplicationState() - APP_STATE_DISCONNECTED");
#endif 
			// Presumbably save stuff into the registry here.


			return;
	} // end switch(iState)

} // end CWinsshDoc::SetApplicationState(*pItem)



void CWinsshDoc::OnNewConnection() 
{
	NewConnect Dialog;

	// Clean out network buffers.
	m_szTempBuf.Empty();
	m_szTempBuf.FreeExtra();

	// Clear network receive length
	m_iNetBufLength = 0;

	// Clean Session Data.
	cSshHandler.CleanSessionData();

#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnTestc() - Starting up NewConnect Dialog Box");
#endif 

	while(TRUE) {
		if (IDOK != Dialog.DoModal()) // got CANCEL
			return;

		if (Dialog.szProtocol.Compare("ssh") == 0)  {

			m_iProtocolState = PROTOCOL_SSH;
			m_iSshState = SSH_STATE_INIT;
			cSshHandler.SetOptions(Dialog.iCipher, Dialog.iCompression,
				(char *)(LPCSTR)Dialog.szUsername, (char *)(LPCSTR)Dialog.szPassword);

		}
		else if (Dialog.szProtocol.Compare("telnet") == 0) {
			m_iProtocolState = PROTOCOL_TELNET;
		}
		//--------

		m_szHostname = Dialog.szHostname;
		m_iPort		 = Dialog.iPort;

#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnTestc() - Opening connection to %s on port %d.", 
			Dialog.szHostname, Dialog.iPort);
#endif 

		if (ConnectSocket("SshitSocket", Dialog.szHostname, Dialog.iPort))
			return;
		if (AfxMessageBox(IDS_CHANGEADDRESS,MB_YESNO) == IDNO)
			return;
	}

	return;	
}

void CWinsshDoc::OnFileConnect() 
{

	// Note previous session must be Telnet in order for this to be enabled
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnFileConnect() - Trying to connect to previous host");
		Debug("CWinsshDoc::OnFileConnect() - Opening connection to %s on port %d.", 
			m_szHostname, m_iPort);
#endif 
		if (ConnectSocket("SshitSocket", m_szHostname, m_iPort))
			return;
		if (AfxMessageBox(IDS_CHANGEADDRESS,MB_YESNO) == IDNO)
			return;

	
}

void CWinsshDoc::OnFileDisconnect() 
{
	CloseConnection();
}

void CWinsshDoc::OnUpdateFileNewconnect(CCmdUI* pCmdUI) 
{
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnUpdateFileNewconnect()");
#endif 

	if ((m_iAppState == APP_STATE_CONNECTED) || (m_iAppState == APP_STATE_CONNECTING))
		pCmdUI->Enable(FALSE);
	else
		pCmdUI->Enable(TRUE);
}

void CWinsshDoc::OnUpdateFileConnect(CCmdUI* pCmdUI) 
{
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnUpdateFileConnect()");
#endif 

	if ((m_iAppState == APP_STATE_DISCONNECTED) && (m_iProtocolState == PROTOCOL_TELNET)) 
		pCmdUI->Enable(TRUE);
	else	
		pCmdUI->Enable(FALSE);

}

void CWinsshDoc::OnUpdateFileDisconnect(CCmdUI* pCmdUI) 
{
#ifdef DEBUG_SSH
		Debug("CWinsshDoc::OnUpdateFileDisconnect()");
#endif 

	if ((m_iAppState == APP_STATE_CONNECTED) || (m_iAppState == APP_STATE_CONNECTING))
		pCmdUI->Enable(TRUE);
	else 
		pCmdUI->Enable(FALSE);
} // end CWinsshDoc::OnUpdateFileDisconnect()


void CWinsshDoc::OnUpdateCipher(CCmdUI* pCmdItem) {

//#ifdef DEBUG_SSH
//		Debug("CWinsshDoc::OnUpdateCipher()");
//#endif 

	CString szPanel;

	if (m_iAppState==APP_STATE_CONNECTED) {
		if (m_iProtocolState == PROTOCOL_SSH) {
			if (cSshHandler.m_uiEncryptionMethod == SSH_CIPHER_IDEA)	
				szPanel = "IDEA";
			if (cSshHandler.m_uiEncryptionMethod == SSH_CIPHER_DES)	
				szPanel = "DES";
			if (cSshHandler.m_uiEncryptionMethod == SSH_CIPHER_3DES)	
				szPanel = "3DES";
		}
		else if (m_iProtocolState == PROTOCOL_TELNET) {
			szPanel = "No Encryption";
		}
	}
	else {
			szPanel="";
	}
	pCmdItem->SetText(szPanel);

} // end CWinsshDoc::OnUpdateCipher()


void CWinsshDoc::OnUpdateTermAndSize(CCmdUI* pCmdItem) {
//#ifdef DEBUG_SSH
//		Debug("CWinsshDoc::OnUpdateTermAndSize()");
//#endif 
	
	if (!m_bTermModify)
		return;

	CString szPanel;

	for(POSITION pos=GetFirstViewPosition();pos!=NULL;)
	{
		CView* pView = GetNextView(pos);
		CWinsshView* pWinsshView = DYNAMIC_DOWNCAST(CWinsshView, pView);
		szPanel.Format("%s - %d Rows %d Cols (%d, %d)",
			m_szTermType,
			pWinsshView->m_usTermY,
			pWinsshView->m_usTermX,
			pWinsshView->m_usCursorY,
			pWinsshView->m_usCursorX);
			pCmdItem->SetText(szPanel);
	} 
	m_bTermModify = FALSE;

} // end CWinsshDoc::OnUpdateTermAndSize()

void CWinsshDoc::OnOverrideCut() 
{
	UCHAR ucCtrlX = '\x018';

	SocketEncryptSend(&ucCtrlX, 1);
}


