Click here to Skip to main content
15,892,809 members
Articles / Desktop Programming / MFC

TJFTP - A Visual C++ FTP Client

Rate me:
Please Sign up or sign in to vote.
4.91/5 (29 votes)
14 Nov 2005CPOL4 min read 164.6K   26.2K   115  
A Windows FTP client written without CInternetSession or CFtpConnection classes. Demonstrates manual manipulation of Winsock sockets, FTP principles, and GUI concepts such as List controls with in-place label-editing and column sorting, progress indicators, and reading and writing to the Registry.
//***********************************************************
//* TJFTP - A Win 95/NT FTP client							*
//* Written February, 1997 in Visual C++ 5.0				*
//*	Recompiled March, 2005 in Visual Studio .NET			*
//*															*
//* Copyright � Jim Dunne 1997 - 2005.  All Rights Reserved	*
//*															*
//* Written by Jim Dunne									*
//* PO Box 33632											*
//* Dayton, OH 45433										*
//*															*
//* topjimmy@topjimmy.net									*
//* www.topjimmy.net/tjs									*
//***********************************************************
// mainview.cpp : implementation of the CMainView class
//

#include "stdafx.h"
#include "TJFTP.h"
#include "mainfrm.h"
#include "TJFTPDoc.h"
#include "mainview.h"
#include "csock.h"
#include "ftpconnect.h"
#include <stdio.h>
#include <direct.h>

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

/////////////////////////////////////////////////////////////////////////////
// CMainView

IMPLEMENT_DYNCREATE(CMainView, CFormView)

BEGIN_MESSAGE_MAP(CMainView, CFormView)
	//{{AFX_MSG_MAP(CMainView)
	ON_MESSAGE(WM_USER_ASYNC_HOST_LOOKUP, OnAsyncHost)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_CONNECT, OnClickedButtonMainConnect)
	ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST_LOCAL, OnGetDispInfo)
	ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST_REMOTE, OnGetDispInfoRem)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_LOCAL, OnColumnClick)
	ON_NOTIFY(NM_DBLCLK, IDC_LIST_LOCAL, OnDblclkListLocal)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REMOTE, OnColumnclickListRemote)
	ON_NOTIFY(NM_DBLCLK, IDC_LIST_REMOTE, OnDblclkListRemote)
	ON_CBN_SELCHANGE(IDC_COMBO_LOCDIR, OnSelchangeComboLocdir)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_LOCREFRESH, OnClickedButtonMainLocrefresh)
	ON_CBN_SELCHANGE(IDC_COMBO_REMDIR, OnSelchangeComboRemdir)
	ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_LOCAL, OnBeginlabeleditListLocal)
	ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_LOCAL, OnEndlabeleditListLocal)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_REMREFRESH, OnClickedButtonMainRemrefresh)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_LOCMKDIR, OnClickedButtonMainLocmkdir)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_LOCDEL, OnClickedButtonMainLocdel)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_REMMKDIR, OnClickedButtonMainRemmkdir)
	ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REMOTE, OnBeginlabeleditListRemote)
	ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REMOTE, OnEndlabeleditListRemote)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_REMDEL, OnClickedButtonMainRemdel)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON_MAIN_LOCSEND, OnClickedButtonMainLocsend)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_REMRET, OnClickedButtonMainRemret)
	ON_BN_CLICKED(IDC_RADIO_MAIN_BINARY, OnClickedRadioMainBinary)
	ON_BN_CLICKED(IDC_RADIO_MAIN_Ascii, OnClickedRadioMainAscii)
	ON_BN_CLICKED(IDC_RADIO_MAIN_AUTO, OnClickedRadioMainAuto)
	ON_BN_CLICKED(IDC_BUTTON_MAIN_EXIT, OnButtonMainExit)
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_USER_STREAM, OnStream)
	ON_MESSAGE(WM_USER_LISTEN, OnListenSocket)
	ON_MESSAGE(WM_USER_ACCEPT, OnAccept)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_STATUS, OnUpdateStatus)
	ON_UPDATE_COMMAND_UI(ID_INDICATOR_UTILITY, OnUpdateUtility)

	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMainView::CMainView() : CFormView(CMainView::IDD)
//
// Sample Call: Not called by the user.
//
// Description: Called when the CMainView Class is created.  Initializes   
// class member variables.
//
// Input/Output data elements:
//	- Input: Argument CMainView::IDD.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
CMainView::CMainView()
	: CFormView(CMainView::IDD)
{
	m_bWinSockOK = FALSE;
	m_nPlaceOnList = 0;
	m_pStream = NULL;

	//{{AFX_DATA_INIT(CMainView)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// TODO: add construction code here

}

/////////////////////////////////////////////////////////////////////////////
// CMainView::~CMainView()
//
// Sample Call: Not called by the user.
//
// Description: The destructor for CMainView.
//	- Destroys and deletes any listening, data, or stream sockets.
//	- Shuts down Winsock.
//	- Writes flag values to the Registry.
//
// Input/Output data elements: None.
//
// Error handling:
//	- If Winsock ShutDown() fails, an error message box is displayed.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
CMainView::~CMainView()
{
	// Free the stream and WinSock objects
	if (m_pListenSocket)
	{	//If a listening socket is open, shut it down
		m_pListenSocket->DestroySocket();
		delete m_pListenSocket;
		m_pListenSocket = NULL;
	}
	if (m_pDataSocket)
	{	//If a data socket is open, shut it down
		m_pDataSocket->DestroySocket();
		delete m_pDataSocket;
		m_pDataSocket = NULL;
	}

	if (m_pStream)
	{	//If a stream socket exists, shut it down
		m_pStream->DestroySocket();
		delete m_pStream;
		m_pStream = NULL;
	}
	if(m_pWinsock)
	{	//If m_pWinsock Cwinsock object exists, shut it down
		m_pWinsock->Shutdown();
		if(m_pWinsock->m_nStatus == CWINSOCK_WINSOCK_ERROR)
		{
			wsprintf(m_lpszMessage, "WSACleanup Failure: ( %s)",
				m_pWinsock->ErrorBox(WSAGetLastError(), ""));
			MessageBox(m_lpszMessage, "TJLookUp", 
				MB_OK | MB_ICONEXCLAMATION);
		}						
		delete m_pWinsock;
		m_pWinsock = NULL;
	}

	//Write flag settings to the Registry 
	AfxGetApp()->WriteProfileInt("Options","Binary", m_bBinary); 
	AfxGetApp()->WriteProfileInt("Options","Auto", m_bAuto);
}

void CMainView::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMainView)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BOOL CMainView::PreCreateWindow(CREATESTRUCT& cs)
{

	return CFormView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CMainView diagnostics

#ifdef _DEBUG
void CMainView::AssertValid() const
{
	CFormView::AssertValid();
}

void CMainView::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}

CTJFTPDoc* CMainView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTJFTPDoc)));
	return (CTJFTPDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainView message handlers

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnInitialUpdate() 
//
// Sample Call: Not called by the user.
//
// Description: Handles all the initialization when the main dialog box 
// is first started.
//	- Initializes variables.
//	- Disables buttons.
//	- Get a pointer to the main frame window.
//	- Creates the CProgressCtrl control.
//	- Gets the current year in FILETIME format.
//	- Changes the main window title.
//	- Get the settings for the "Binary" and "Auto" buttons, and sets 
//		their states.
//	- Initializes the local list path and the local and remote 
//		list controls.
//	- Starts the Winsock services.
//	- Displays the "Connect" dialog box.
//
// Input/Output data elements: None.
//
// Error handling:
//	- If the local host lookup fails, an error message is printed 
//		to the screen.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnInitialUpdate() 
{
	CFormView::OnInitialUpdate();

	//Initialize variables
	m_nBytesReceived = 0;
	m_bLocMkDir = FALSE;
	m_bRemMkDir = FALSE;
	m_bSendFile = FALSE;
	m_bReadReady = FALSE;
	m_bConClose = FALSE;
	m_csBytes = "";
	m_bConnect = FALSE; 

	//Disable non-applicable buttons
	DisableFunctions();

	//Get a pointer to the main frame window
	CMainFrame *pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainWnd;

	RECT rc;	//Declare a RECT object

	//Get the rectangle for the progress indicator on the status bar
	pMainFrame->m_wndStatusBar.GetItemRect(1, &rc);
	//Create the CProgressCtrl control
    VERIFY (m_wndProgress.Create(WS_CHILD | WS_VISIBLE, rc,
		&pMainFrame->m_wndStatusBar, 1));
    m_wndProgress.SetRange(0, 100);
    m_wndProgress.SetStep(2);

	//Get the current year in FILETIME format
	SYSTEMTIME stTemp;
	::GetLocalTime(&stTemp);
	//Set the time to beginning of this year 
	stTemp.wMilliseconds = 0;
	stTemp.wSecond = 0;
	stTemp.wDay = 1;
	stTemp.wMonth = 1;
	stTemp.wHour = 0;
	stTemp.wMinute = 0;

	if(!::SystemTimeToFileTime(&stTemp, &m_ftYear))
	{
		AfxMessageBox("Couldn't get FILETIME time");
	}

	//Change the main window title
	AfxGetMainWnd()->SetWindowText(TJFTP);

	//Get the settings for the "Binary" and "Auto" buttons
	m_bBinary = AfxGetApp()->GetProfileInt("Options","Binary", 1); 
	m_bAuto = AfxGetApp()->GetProfileInt("Options","Auto", 1);

	if(m_bBinary)
	{
		//Press the "Binary" radio button
		CButton *pRadBin = (CButton *)GetDlgItem(IDC_RADIO_MAIN_BINARY);
		pRadBin->SetCheck(TRUE);
	}
	else
	{
		//Press the "Ascii" radio button
		CButton *pRadAsc = (CButton *)GetDlgItem(IDC_RADIO_MAIN_Ascii);
		pRadAsc->SetCheck(TRUE);
	}

	CButton *pRadAuto = (CButton *)GetDlgItem(IDC_RADIO_MAIN_AUTO);
	if(m_bAuto)
		//Press the "Auto" radio button
		pRadAuto->SetCheck(TRUE);

	//Initialize the local list path and list control
	m_csLocPath = "C:\\";
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	m_imglDrives.Create(IDR_DRIVEIMAGES, 16, 1, RGB(255, 0, 255));

	pList->SetImageList(&m_imglDrives, LVSIL_SMALL);
	pList->InsertColumn (0, "File Name", LVCFMT_LEFT, 115);
	pList->InsertColumn (1, "Size", LVCFMT_RIGHT, 60);
	pList->InsertColumn (2, "Last Modified", LVCFMT_CENTER, 93);

	InitLocList(m_csLocPath);
	pList->SortItems(CompareFunc, 0);

	//Initialize the remote list control
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	pRemList->SetImageList(&m_imglDrives, LVSIL_SMALL);
	pRemList->InsertColumn (0, "File Name", LVCFMT_LEFT, 115);
	pRemList->InsertColumn (1, "Size", LVCFMT_RIGHT, 60);
	pRemList->InsertColumn (2, "Last Modified", LVCFMT_CENTER, 93);

	//Start the Winsock services
	m_nWinSockError = 0;
	m_pWinsock = new CWinsock;
	if (m_pWinsock->Startup() == CWINSOCK_NOERROR && 
		(m_bWinSockOK == FALSE))
	{
		m_bWinSockOK = TRUE;

		//Change the "Status" pane
		m_csStatus = "Winsock started OK";
		PrintToStatus("%s    Started OK", 
			m_pWinsock->m_wsaData.szDescription);
		if((gethostname((LPSTR)szString, MAXHOSTNAMELEN))
			==SOCKET_ERROR)
		{
			m_csStatus = "Local Host lookup failed";
			PrintToStatus("Local Host lookup failed ( %s)",
				m_pWinsock->ErrorBox(WSAGetLastError(), ""));
		}
		else
  			lstrcpy(m_lpszHost, szString);

		m_bLocalFlag = 1;
		m_hAsyncHost = WSAAsyncGetHostByName(m_hWnd, WM_USER_ASYNC_HOST_LOOKUP,
			m_lpszHost, m_achHostEnt, MAXGETHOSTSTRUCT);
	}
		else
		m_nWinSockError = WSAGetLastError();
	
	//Display the "Connect" dialog
	SetTimer(1,0, NULL);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainConnect() 
//
// Sample Call: OnClickedButtonMainConnect();
//
// Description: Called when the "Connect" or "Close" button is pressed.
//	- If button in "Close" mode, begins closing actions.
//	- Sets button text to "Connect", disables "Cancel" button.
//	- Gets pointers to the remote list control and combobox.
//	- Clears the remote list control and combobox.
//	- Disables operations buttons.
//	- Cleans up any open file or sockets, and returns.
//	- If button in "Connect" mode, gets pointers to the local list 
//		control and combobox.
//	- Opens the "Connect" dialog box.
//	- If the user presses the "Cancel" button, returns.
//	- If the user presses the "OK" button, changes button text to "Close".
//	- Gets the current host settings, including the local path.
//	- Draws the local directory in the local list control.
//	- Sets the main window caption to reflect the current host name.
//	- If no m_pStream object exists,  creates new CStreamSocket object.
//	- Creates a new stream socket.
//	- Attempts to connect to the server.
//
// Input/Output data elements: None.
//
// Error handling:
//	- If the stream socket creation or connection attempt fail, an error 
//		message is printed to the screen, and the 
//		CStreamSocket object is destroyed.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainConnect() 
{
	if(m_bConClose)
	{	//If the button is active (showing "Close")
		m_bConClose = FALSE;
		CButton *pbCon = (CButton *)GetDlgItem(IDC_BUTTON_MAIN_CONNECT);
		pbCon->SetWindowText("Connect");

		//Get pointers to the remote list control and combobox
		CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
		CComboBox *pcbRem = (CComboBox *)GetDlgItem(IDC_COMBO_REMDIR);

		//Clear the remote list control and combobox
		pRemList->DeleteAllItems();
		pcbRem->ResetContent();

		m_nBytesReceived = 0;
		m_nTotalSent = 0;
		DisableFunctions();

		if(m_cfUserFile)
		{	//If a file is open, close it
			m_cfUserFile->Close();
			delete m_cfUserFile;
			m_cfUserFile = NULL;
		}
		if (m_pStream)
		{	//If a stream socket is open
			AfxGetMainWnd()->SetWindowText(TJFTP);
			if(m_bConnect)
			{
				SendCommand("QUIT", "");
				ReadResponse();
			}
			//Destroy the socket
			m_pStream->DestroySocket();
			delete m_pStream;
			m_pStream = NULL;
			m_csStatus = "Disconnected";
		}
		if (m_pListenSocket)
		{	//If the listening socket is open
			m_pListenSocket->DestroyDataSocket();
			delete m_pListenSocket;
			m_pListenSocket = NULL;
		}
		if (m_pDataSocket)
		{	//If a data socket is open
			m_pDataSocket->DestroySocket();
			delete m_pDataSocket;
			m_pDataSocket = NULL;
		}
		 return;
	}

	//Get pointers to local list control and combobox
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	CComboBox *pcbLoc = (CComboBox *)GetDlgItem(IDC_COMBO_LOCDIR);
	
	m_csStatus = "Starting new session...";
	//Letting the idle processing mechanism update the status bar
	UpdateControls();

	//Open the "Connect" dialog box
	CFtpConnect dlg;
	m_nConDialStatus = dlg.DoModal();
	
	if(m_nConDialStatus == IDCANCEL)
	{	//If the user pressed the "Cancel" button
		m_csStatus = "Idle";
		return ;
	}

	if(m_nConDialStatus == IDOK)
	{	//If the user presed the "OK" button
		//Set the first button's text to "Close"
		CButton *pbCon = (CButton *)GetDlgItem(IDC_BUTTON_MAIN_CONNECT);
		pbCon->SetWindowText("Close");

		m_csStatus = "Attempting to connect...";

		//Set the flag in case the user presses the "Close" button
		m_bConClose = TRUE;
		//Get the Current host settings 
		csHost = AfxGetApp()->GetProfileString("Hosts\\Current",
			"Host Address"); 
		csUser = AfxGetApp()->GetProfileString("Hosts\\Current",
			"User"); 
		csPass = AfxGetApp()->GetProfileString("Hosts\\Current",
			"Pass"); 
		m_csRemDir = AfxGetApp()->GetProfileString("Hosts\\Current",
			"Remote Directory"); 
		m_csLocDir = AfxGetApp()->GetProfileString("Hosts\\Current",
			"Local Directory"); 
		m_csLocPath = m_csLocDir;

		//Draw the local directory in the list control
		DrawLocList();
		//Perform idle processing
		UpdateControls();

		//Set the main window caption to reflect the current host
		wsprintf(m_lpszMessage,TJFTP);
		strcat(m_lpszMessage, " - "); 
		strcat(m_lpszMessage, AfxGetApp()->GetProfileString("Hosts\\Current", "Profile")); 
		AfxGetMainWnd()->SetWindowText(m_lpszMessage);

		if(m_pStream == NULL)
		{	//If no m_pStream object exists,
			// initialize the stream socket object
			m_pStream = new CStreamSocket(this, WM_USER_STREAM);
			//Create the stream socket
			if (m_pStream->CreateSocket() != CWINSOCK_NOERROR)
			{	//If socket creation fails...
				m_csStatus = "Couldn't create the stream socket";
				PrintToStatus("Couldn't create the stream socket", NULL);
				CButton *pbCon = (CButton *)GetDlgItem(IDC_BUTTON_MAIN_CONNECT);
				pbCon->SetWindowText("Connect");
				m_bConClose = FALSE;
				delete m_pStream;
				m_pStream = NULL;
				return;
			}
		}
		//Copy the Current host address to a string pointer
		wsprintf(m_pszServer,csHost);
		
		//Connect the client to the server
		if (m_pStream->Connect(m_pszServer, 21) != CWINSOCK_NOERROR)
		{	//If the connection failed
			m_csStatus = "Couldn't connect to the server";
			PrintToStatus("Couldn't connect to the server", NULL);
			CButton *pbCon = (CButton *)GetDlgItem(IDC_BUTTON_MAIN_CONNECT);
			pbCon->SetWindowText("Connect");
	   		m_bConClose = FALSE;
			m_pStream->DestroySocket();
			delete m_pStream;
			m_pStream = NULL;
			return;
		}
	
	}
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::SendCommand(CString csString1, CString csString2)
//
// Sample Call: SendCommand("USER ", "Jim");
//
// Description: Sets the "wait" cursor and sends the command out 
// on the stream socket.
//	- Sets the "wait" cursor.
//	- Combines csString1 and csString2 and adds a CR/LF to the end.
//	- Sends the string out on the stream socket using the "Write" command.
//
// Input/Output data elements:
//	- Input: CString arguments csString1 and csString2.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::SendCommand(CString csString1, CString csString2)
{
	CWaitCursor cWait;	//Set the "wait" cursor
	//Combine csString1 and csString2 and add a carriage return to the end
	wsprintf(m_pszBuf,"%s%s\r\n", csString1, csString2);
	//Send the string out on the stream socket
	m_pStream->Write(strlen(m_pszBuf)/*+ 1*/, m_pszBuf);
	return;
}

/////////////////////////////////////////////////////////////////////////////
// LONG CMainView::OnAsyncHost(WPARAM wParam, LPARAM lParam)
//
// Sample Call: WSAAsyncGetHostByName(m_hWnd, WM_USER_ASYNC_HOST_LOOKUP,
//			m_stringHostName, m_achHostEnt, MAXGETHOSTSTRUCT);
//
// Description: Called when the asynchronous lookup is done.  The message 
// and the function are linked by the ON_MESSAGE(WM_USER_ASYNC_HOST_LOOKUP, 
// OnAsyncHost) entry in the message maps. It contains either the requested 
// lookup information, or an error message if the lookup failed.
//	- Typecasts lParam to WSAGETASYNCERROR to get any error messages.
//	- Assigns a HOSTENT host entry pointer to the buffer m_achHostEnt.
//	- Copies the four-byte IP address into an Internet address structure.
//	- Formats the results, converting the IP address into a string 
//		and an unsigned long variable.
//	- Cleans up and returns.
//
// Input/Output data elements:
//	- Input: Arguments WPARAM wParam and LPARAM lParam.
//	- Output: Returns zero.
//
// Error handling:
//	-If the host lookup fails, prints an error message to the screen.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
LONG CMainView::OnAsyncHost(WPARAM wParam, LPARAM lParam)
{	//If there is an error message
	if (WSAGETASYNCERROR(lParam) != 0)
	{	//If this is a local lookup 
		if (m_bLocalFlag)
		{
			m_csStatus = "Local Host lookup failed";
			PrintToStatus("Local Host lookup failed ( %s)",
				m_pWinsock->ErrorBox(WSAGETASYNCERROR(lParam), ""));
		}
		else
		{	//This is a normal lookup
			m_csStatus = "Host lookup failed";
			PrintToStatus("Host lookup failed ( %s)",
				m_pWinsock->ErrorBox(WSAGETASYNCERROR(lParam), ""));
		}
	}
	else
	{	
		//Assign a hostent host entry pointer to the buffer
		m_pHostEnt = (PHOSTENT)m_achHostEnt;

		//Copy the four byte IP address into
		//an Internet address structure
		memcpy(&m_in, m_pHostEnt->h_addr, 4);

		//Format the results, converting the 
		//IP address into a string
		if (m_bLocalFlag)
		{	//If this is for the local lookup
			csMain.LoadString(IDS_STRING61205);
			wsprintf(m_lpszMessage, csMain,
				m_pHostEnt->h_name, inet_ntoa(m_in));
			m_csStatus.Format("  %s   %s",
				m_pHostEnt->h_name, inet_ntoa(m_in));
			PrintToStatus(m_lpszMessage, NULL);
			//This is the local IP address
			m_ulIPLocal = inet_addr(inet_ntoa(m_in));
		}
		else
		{
			//Put the resolved IP address into m_ulIPAddress 
			m_ulIPAddress = inet_addr(inet_ntoa(m_in));
			wsprintf(m_lpszMessage, "Host %s has IP address %s",
					m_pHostEnt->h_name, inet_ntoa(m_in));
			PrintToStatus(m_lpszMessage, NULL);
		}
	}
//Cleanup
m_hAsyncHost = 0;
m_bLocalFlag = 0;
return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// LONG CMainView::OnStream(WPARAM wParam, LPARAM lParam)
//
// Sample Call: WSAAsyncSelect(m_s, m_hWnd, 
//	CWINSOCK_EVENT_NOTIFICATION, lEvent) where
//			lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE | FD_CONNECT;
//
// Description: Receives messages from the stream socket.
//	- Switch statement handles cases based on value of wParam.
//	- If the socket is done writing:
//		- Gets the written data.
//		- Strips the CR/LF from the end, and prints the data to the screen, 
//		hiding passwords, if a PASS command message was sent.
//	- If there was an error writing, shows an error message to the screen 
//		and status bar with the data that was meant to be sent.
//	- If the socket is done reading, sets Timer 2 if m_bReadReady is TRUE.
//		- If m_bReadReady is FALSE, sets it TRUE.
//	- If there was an error reading, shows an error message box.
//	- If a successful connection was made:
//		- Reads the connection request response.
//		- Starts the login sequence.
//		- Shows the login directory in the remote list if the 
//			login was successful.
//	- If the connection was lost, sets m_bConnect to False .
//
// Input/Output data elements:
//	- Input: Arguments WPARAM wParam and LPARAM lParam.
//	- Output: Returns zero.
//
// Error handling:
//	- If any of the ReadResponse() calls fails, the stream socket is killed.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
LONG CMainView::OnStream(WPARAM wParam, LPARAM lParam)
{
	CWaitCursor cWait;		//Set the wait cursor
	LPVOID pDataWritten;	//Pointer to data that is completely written
	CString csTemp,
			csDoneWrite;

	switch (wParam)
	{
	case CWINSOCK_READY_TO_ACCEPT_CONNECTION:
	break;	//Don't use this here

    case CWINSOCK_DONE_WRITING:
		//The socket tells us that it has written out all data
		//lParam = pointer to data that was sent
		//Typecast lParam as LPVOID to get at the data
		pDataWritten = (LPVOID)lParam;	//Put data into LPVOID object
		csDoneWrite.Format("%s",pDataWritten);	//Put data into a string
		//Parse individual lines and show them
		//Strip the CR/LF from the line
		csTemp = csDoneWrite.Left(csDoneWrite.Find("\r\n"));
		//If the password was sent, don't show it to the screen 
		if(csTemp.Left(4) == "PASS")
			PrintToStatus("PASS *****", NULL);	//Show asterisks instead
		else	//otherwise, print the line to the status window
			PrintToStatus(csTemp, NULL);
		free(pDataWritten);	//Free the memory for the pDataWritten
		pDataWritten = NULL;
		break;

    case CWINSOCK_ERROR_WRITING:
		//If there was an error writing the data to the socket
		//lParam = pointer to data that generated error sending
		pDataWritten = (LPVOID)lParam;
		csTemp.Format("%s",pDataWritten);
		csTemp = csTemp.Left(csTemp.Find("\r\n"));
		//Show which data didn't get sent
		csDoneWrite.Format("Error sending data (%s)", csTemp);
		PrintToStatus(csDoneWrite, NULL);
		m_csStatus = csDoneWrite;	//Show error on status bar
		UpdateControls();
		free(pDataWritten);
		pDataWritten = NULL;
		break;

	case CWINSOCK_DONE_READING:
		//The socket is finished reading data
		if(m_bReadReady)	//If m_bReadReady = TRUE
			SetTimer(2,100, NULL);	//Set timer 2 to 100 ms
		else	//If m_bReadReady = FALSE
			m_bReadReady = TRUE;
		break;

	case CWINSOCK_ERROR_READING:
		AfxMessageBox("Error Reading");
		break;

	case CWINSOCK_YOU_ARE_CONNECTED:
		//Successful connection - show message on status bar
		m_bConnect = TRUE; 
		m_csStatus = "Connected to server";
		UpdateControls();
		//Read the connection request response
		if(!ReadResponse())
		{
			//If an error, kill the stream object
			KillStream();
		}
		else
		{	//Send login commands and read the responses
			//Clean-up if errors are received
			SendCommand("USER ", csUser);
			if(!ReadResponse())
				KillStream();
			else
			{
				SendCommand("PASS ", csPass);
				if(!ReadResponse())
					KillStream();
				else
				{
					SendCommand("CWD ", m_csRemDir);
					if(!ReadResponse())
						KillStream();
					else
					{	//If successful login, show the login directory
						//in the Remote list control
						if(PostDirectory())
							EnableFunctions();
					}
				}
			}
		}
		break;

	case CWINSOCK_LOST_CONNECTION:
		// server closed the connection
		m_bConnect = FALSE; 
		break;

    default:
      break;
  }
  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// LONG CMainView::OnListenSocket(WPARAM wParam, LPARAM lParam)
//
// Sample Call: WSAAsyncSelect(m_s, m_hWnd, 
//	CWINSOCK_EVENT_NOTIFICATION, lEvent) where
//			lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE;
//
// Description: Receives messages from the listening socket.
//	- Handles the CWINSOCK_READY_TO_ACCEPT_CONNECTION message, indicating 
//		a connection request from the FTP server.
//	- Checks to make sure a data socket is not already open.
//	- Accepts the connection request by creating a new CStreamSocket object, 
//		and then calling the Winsock function "accept()" with the object.
//	- If the connection is accepted, destroys the listening socket.
//
// Input/Output data elements:
//	- Input: Arguments WPARAM wParam and LPARAM lParam. 
//	- Output: Returns zero.
//
// Error handling:
//	- If the Accept() command fails, deletes the new CStreamSocket object.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
LONG CMainView::OnListenSocket(WPARAM wParam, LPARAM lParam)
{	
	CWaitCursor cWait;

	switch (wParam)
	{
    case CWINSOCK_READY_TO_ACCEPT_CONNECTION:
		//The FTP server wants to connect to us
		//Make sure the server is not already servicing a client
		if (m_pDataSocket != NULL)	
			{	//If a data socket already exists
				PrintToStatus("Already servicing a client", NULL);
				m_csStatus = "Already servicing a client";
				UpdateControls();
				break;
			}

		int nStatus;
		//Accept the client connection by creating a new socket...
		m_pDataSocket = new CStreamSocket(this, WM_USER_ACCEPT);
		//And creating a data socket from the new socket  
		nStatus = m_pListenSocket->Accept(m_pDataSocket);

		if (nStatus != CWINSOCK_NOERROR)
		{	//If the socket creation failed
			delete m_pDataSocket;
			m_pDataSocket = NULL;
			PrintToStatus("Error accepting connection from server", NULL);
			m_csStatus = "Error accepting connection from server";
			UpdateControls();
			break;
		}
		else
		{	//If the Accept socket creation was successful
			m_csStatus = "Accepted server connection";
			UpdateControls();
			//Don't need the listening socket anymore
			m_pListenSocket->DestroyDataSocket();
			delete m_pListenSocket;
			m_pListenSocket = NULL;
		}
		break;

	default:
		break;
	}
	return (0L);
}

/////////////////////////////////////////////////////////////////////////////
// LONG CMainView::OnAccept(WPARAM wParam, LPARAM lParam)
//
// Sample Call: WSAAsyncSelect(m_s, m_hWnd, 
//	CWINSOCK_EVENT_NOTIFICATION, lEvent) where
//			lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE;
//
// Description: Receives messages from the data socket.
//	- Initializes variables.
//	- Switch statement handles cases based on value of wParam.
//	- If the socket has finished writing:
//		- Updates the progress control and statistics on the status bar
//		- If the complete file has been sent:
//			- Sets the progress control to 0.
//			- Destroys and deletes the data socket.
//			- Closes and deletes the file handle.
//	- If there is an error writing data - prints the data that could not 
//		be written to the screen.
//	- If the socket is done reading:
//		- Reads the data from the socket and puts into a character string.
//		- If a file is being received:
//			- Updates the progress control and status bar satistics.
//			- If receiving in ASCII mode, finds every line of data and 
//				writes to the user file.
//			- If receiving in binary, writes data to the user file.
//		- If a file listing is being received:
//			- Copies the data to a string.
//			- Updates the "Bytes Received:" stat on the status bar.
//			- Finds each line of text and adds to m_csRemFileList.
//	- If the server connected to the client successfully, gets the server's 
//		name and the port it connected on and shows this information on 
//		the status bar.
//	- If the connection is lost:
//		- If sending or receiving a file, set the progress control to 0, 
//			close and delete the user file handle, and refresh the local list.
//		- If receiving a file list:
//			- Deletes all items from the remote list.
//			- Parses each line of m_csRemFileList for file/directory information.
//			- Adds items to the remote list, sorts them, and redraws the list.
//		- Cleans up the data and listening sockets.
//
// Input/Output data elements:
//	- Input: Arguments WPARAM wParam and LPARAM lParam.
//	- Output: Returns zero.
//
// Error handling:
//	- If there is an error getting the connecting server's information, 
//		prints an error message to the screen and updates the status bar.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
LONG CMainView::OnAccept(WPARAM wParam, LPARAM lParam)
{	
	CWaitCursor cWait;
	int nStrCount = 0,		//Position in string
		nLen = 0,			//Length
		nStatus = 0,		//Error status
		nCrLfPos = 0,		//CR/LF position		
		ncsLineLen = 0;		//Line length
	char pszData[READ_BUF_LEN + 1];		//Informational message
	char pszMessage[READ_BUF_LEN + 1];	//Informational message
	CString csLine,
			csTemp,
			csDoneWrite;
	LPVOID pDataWritten;  //Pointer to data that is completely written
	LPVOID pDataRead;     //Pointer to data just read
	SOCKADDR_IN sin;      //Internet address of client
	IN_ADDR in;           //IP address of client

  switch (wParam)
  {
    case CWINSOCK_DONE_WRITING:
		//The data socket has finished writing
		//lParam = pointer to data that was sent
		//Update the progress indicator on the status bar
		m_wndProgress.SetPos((m_nTotalSent * 100)/m_nFileSize);
		//And the status message on the status bar
		m_csStatus.Format("%u of %u bytes sent, %u%% done",
			m_nTotalSent, m_nFileSize, (m_nTotalSent * 100)
			/m_nFileSize);

		m_bPieceDone = TRUE;	//This piece of data is done
		if(m_bWriteDone)
		{	//If the whole file has been sent
			m_csStatus = "Transfer complete.";
			m_wndProgress.SetPos(0);
			UpdateControls();	//Make sure status bar gets updated
			m_bWriteDone = FALSE;
			//Clean up the socket and file handle
			m_pDataSocket->DestroySocket();
			delete m_pDataSocket;
			m_pDataSocket = NULL;

			m_cfUserFile->Close();
			delete m_cfUserFile;
			m_cfUserFile = NULL;
			//Indicate that there is no file transfer going on
			m_csListOrFile == "";
		}
		break;

	case CWINSOCK_ERROR_WRITING:
		//There was an error writing the data
		//lParam = pointer to data that generated error sending
		pDataWritten = (LPVOID)lParam;
		wsprintf(pszMessage, "Error sending data (%s)", pDataWritten);
		PrintToStatus(pszMessage, NULL);	//Show the data 
		free(pDataWritten);
		pDataWritten = NULL;
		break;

	case CWINSOCK_DONE_READING:
		//Socket is done reading
		//lParam = # data chunks in queue
		//Clear string buffers
		memset(pszData, 0, READ_BUF_LEN + 1);
		*pszData = '\0';	//Clear pszData
		//Read socket data and copy to buffer
		pDataRead = m_pDataSocket->Read(&nLen);
		wsprintf(pszData,"%s",pDataRead);
		//Append terminator to string
		pszData[nLen] = '\0';
		//For the progress control
		m_nBytesReceived += nLen;
		//If we are receiving a file
		if(m_csListOrFile == "Receiving File")
		{	//Update the progress indicator and status bar message
			m_wndProgress.SetPos((m_nBytesReceived * 100)/m_nFileSize);
			m_csStatus.Format("%u of %u bytes received, %u%% done",
				m_nBytesReceived, m_nFileSize, (m_nBytesReceived * 100)
				/m_nFileSize);
			UpdateControls();	//Make sure status bar gets updated

			//If this is an ASCII transfer  
			if(!m_bBinary ||(m_bAuto && 
				m_csLocalFile.Right(3)== "txt"))
			{	//Copy the data to cur
				char* cur = (char*)pszData;
				char* cr = strchr(cur, '\r'); //Location of the CR
				while (cr)	//While there is another CR
				{	//Terminate the string
					*cr++ = 0;
					//Will go from cur to the null termination we just put in
					m_cfUserFile->Write((char*) cur,strlen(cur));
					cur = cr;	//Skip over the null
					cr = strchr(cur, '\r');	//Search again
				}
				//Write out any leftover amount
				m_cfUserFile->Write((char*) cur,strlen(cur));
			}
			else	//This is binary data, just write it 
				m_cfUserFile->Write((char*)pDataRead, nLen);
		}
		//If we are receiving a file list
		if(m_csListOrFile == "Receiving List")
		{	//Copy the data to pszData
			wsprintf(pszData,"%s",pDataRead);
			//Show bytes recieved in third status pane
			m_csBytes.Format("Bytes received: %u", m_nBytesReceived);
			UpdateControls();	//Make sure status bar gets updated
			//Append terminator to string
			pszData[nLen] = '\0';
			csLine.Format(pszData);	//Convert data to a CString
			//Add the new data to any leftover data from before
			csLine = m_csLeftovers + csLine;
			//While a CR/LF exists
			while((nCrLfPos = csLine.Find("\r\n")) != -1)
			{	//Get the line length
				ncsLineLen = csLine.GetLength();
				//csTemp = everything before the first CR/LF
				csTemp = csLine.Left(nCrLfPos);
				//Add the whole line, including CR/LF, to m_csRemFileList
				m_csRemFileList += csLine.Left(nCrLfPos + 2);
				//csLine = the data minus the current line (including
				//the CR/LF)
				csLine = csLine.Right(ncsLineLen - nCrLfPos - 2);
			}
			//What's left are leftovers with no CR/LF
			m_csLeftovers = csLine; 
		}
		free(pDataRead);
		pDataRead = NULL;
		break;

	case CWINSOCK_ERROR_READING:
		break;

	case CWINSOCK_YOU_ARE_CONNECTED:
		//We've connected to the server
		//Print out the client information
		nStatus = m_pStream->GetPeerName(&sin);
		if (nStatus == CWINSOCK_NOERROR)
		{	//If no errors, show connection info
			memcpy(&in, &sin.sin_addr.s_addr, 4);
			//Show which port the server connected to
			m_csStatus.Format("Server %s connected to data port %d",
				inet_ntoa(in), ntohs(m_sinaddrDataPort.sin_port));
			UpdateControls();	//Make sure status bar gets updated

		}
		else
		{	//Couldn't get server name
			PrintToStatus("Error getting server name", NULL);
			m_csStatus = "Error getting server name";
		}
		break;

    case CWINSOCK_LOST_CONNECTION:
		//Client closed the connection
		if((m_csListOrFile == "Receiving File") ||
			(m_csListOrFile == "Sending File"))
		{	//Set the progress indicator to 0 and clean up
			m_wndProgress.SetPos(0);				
			m_cfUserFile->Close();
			delete m_cfUserFile;
			m_cfUserFile = NULL;
			m_csListOrFile == "";
			OnClickedButtonMainLocrefresh();
		}

		if(m_csListOrFile == "Receiving List")
		{	//Ready to draw the remote list - get a pointer to the control 
			CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
			CWaitCursor cWait;
			//Free all item memory and clear the list
			FreeItemMemoryRem();
			pRemList->DeleteAllItems();
			//Don't redraw it till we're done
			SetRedraw(FALSE);
			//Add the ".." item to the list
			AddItemRemList(0, "..", "0", m_ftYear, 0);
			//Parse the list for individual lines
			while((nCrLfPos = m_csRemFileList.Find("\r\n")) != -1)
			{	//Find data up to the first CR/LF
				ncsLineLen = m_csRemFileList.GetLength();
				//csTemp = left side of data before CR/LF 
				csTemp = m_csRemFileList.Left(nCrLfPos);
				ParseInfo(csTemp);	//Parse lines and display
				//Strip off the left side of the data
				m_csRemFileList = m_csRemFileList.Right(ncsLineLen
					- nCrLfPos - 2);
			}
			//Sort the items in the list
			pRemList->SortItems(CompareFuncRem, 0);
			//Redraw the list once all items are there 
			SetRedraw(TRUE);
			InvalidateRect(NULL,FALSE);
			m_csListOrFile == "";
		}
		//Clean up sockets
		if(m_pDataSocket)
		{
			m_pDataSocket->DestroySocket();
			delete m_pDataSocket;
			m_pDataSocket = NULL;
			m_csStatus = "Data connection closed";
			UpdateControls();	//Make sure status bar gets updated
		}
		if(m_pListenSocket)
		{
			m_pListenSocket->DestroySocket();
			delete m_pListenSocket;
			m_pListenSocket = NULL;
		}
		break;

    default:
      break;
  }
 	return (0L);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::ParseInfo(CString csLine)
//
// Sample Call: ParseInfo(csLine);
//
// Description: Parses the file list and adds the item to the 
// remote list control.
//	- Initializes variables.
//	- Finds the beginning of the file name.
//	- Determines if item is a file, directory, or link, and assigns the 
//	appropriate value to Dirlist.
//	- Parses the file name, date and time, and the file size from the string.
//	- Calls AddItemRemList to add the item to the list.
//
// Input/Output data elements:
//	- Input: Argument CString csLine.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::ParseInfo(CString csLine)
{
	FILETIME FileTime;	//Create a FILETIME object
	int DirList = 0,
		position = 0,
		nLineLen = csLine.GetLength(),
		nSkips = 0;
	CString tmp,
			csFileName,
			csRemFileSize;

	//Find the beginning of the filename - looks for a space in the line
	for(int i=1; i<nLineLen; i++)
	{	//If a space is found that is not preceded by a space
		if((csLine[i] == ' ') && (csLine[i -1] != ' '))
			nSkips++;
		//If we are at the 9th character
		if(nSkips == 8)
		{
			position = i;
			break;
		}
	}
	//If the first character is a ...
	if (csLine[0] == 'd')
		DirList = 0;	//It is a directory
	else 
		if(csLine[0] == 'l')
			DirList = 1;	//It is a link
	else
		if((csLine[0] == '-') || (csLine[0] == 's'))
			DirList = 2;	//It is a file or a system file
	else return;
	//The filename is at the right end of the string
	csFileName = csLine.Right(nLineLen - position - 1);
	//Get the left side of the string, minus the filename
	tmp = csLine.Left(position);
	//The file's date and time is the last 12 chars of the new string  
	FileTime = TimeConversion(tmp.Right(12));	//Convert it
	//Get the left side of the string, minus the date/time
	tmp = tmp.Left(tmp.GetLength() - 13);
	//Find the first space, beginning at the right side of the string
	position = tmp.ReverseFind(' ');
	//The filesize is now at the end of the string
	csRemFileSize = tmp.Right(tmp.GetLength() - position - 1);
	//Add the entry to the remote list control, if it is not "." or ".."
	if((csFileName != ".") && (csFileName != ".."))
		AddItemRemList(0, csFileName, csRemFileSize,
			FileTime, DirList);	
}

/////////////////////////////////////////////////////////////////////
// CMainView Remote list control functions

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnDblclkListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: OnDblclkListRemote(pNMHDR, pResult);
//
// Description: Called when the user double-clicks the mouse on the 
// remote list  control.
//	- Initializes variables.
//	- Gets pointers to the remote list control and combobox.
//	- Determines if the mouse pointer was over a list item when clicked. 
//	- Get the text of the item's label.
//	- Sets the attributes for the LV_ITEM object.
//	- Gets the item info for the selected item.
//	- If selection is ".." (up arrow icon), sends a "CDUP" command.
//	- If selection is a directory or link:
//		- If a link, parses the directory name from it.
//		- Gets the directory path from the combobox.
//		- Adds the item's text to the end of the path.
//		- Sends the CWD command with the new path.
//		- Reads response and updates the remote list control
//	- If the selection is a file, calls OnClickedButtonMainRemret().
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pNMHDR and LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnDblclkListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
{
	CString csText,
			csRemCBTxt;
	int nIndex = 0,
		nLen = 0,
		nDir = 0,
		nPosition=0;
	//Get pointers to the remote list control and combobox
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	CComboBox* pcbRem = (CComboBox*)GetDlgItem(IDC_COMBO_REMDIR);
	LV_ITEM lvRem;

	m_bReadReady = FALSE;	//Set the read flag
	m_nBytesReceived = 0;	//Reset bytes counter 

	if((nIndex = pRemList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{	//If the mouse pointer was over a list item
		CWaitCursor cWait;
		//Get the text of the item's label
		csText = pRemList->GetItemText(nIndex, 0);
		//Set attributes for the LV_ITEM object
		lvRem.mask = LVIF_IMAGE;	//Image bit is valid
		lvRem.iItem = nIndex;	//Get info for the clicked item
		lvRem.iSubItem = 0;	//Don't want info on subitems 
		pRemList->GetItem(&lvRem);	//Get the info

		//If selection is ".." (up arrow)
		if(lvRem.iImage == 6)
		{	//Change directory up
			SendCommand("CDUP", "");
			if(ReadResponse())
				PostDirectory();
		}
		//If selection is a directory or link
		if((lvRem.iImage == 3) || (lvRem.iImage == 4))
		{	//If selection is a link
			if(lvRem.iImage == 4)
			{	//Get the directory path from the link path
				csText = csText.Right(csText.GetLength() - 
					csText.Find(" ->") - 4);
				if(csText.Left(1) == "/")
					csText = csText.Right(csText.GetLength() - 1);
			}		
			
			//Get the directory path from the combobox
			pcbRem->GetLBText(pcbRem->GetCurSel(), csRemCBTxt);
			//If the end of the path is not a "/"
			if(csRemCBTxt.Right(1) != '/')
				//Add a "/", and then the new directory
				csRemCBTxt += "/" + csText;
			else	//"/" is already there, just add new directory
				csRemCBTxt += csText + "/";
			//Change the remote host to the new directory
			SendCommand("CWD ", csRemCBTxt);
			if(ReadResponse())
				PostDirectory();
			else if(lvRem.iImage == 4)
				OnClickedButtonMainRemret();
		}
		//If selection is a file
		if(lvRem.iImage == 5)
			OnClickedButtonMainRemret();
	}
	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnColumnclickListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: OnColumnclickListRemote(pNMHDR, pResult);
//
// Description: Called when the user left-clicks the mouse on a column 
// header  on the remote list control.
//	- Creates an NM_LISTVIEW* pointer by typecasting pNMHDR.
//	- Gets a pointer to the remote list control.
//	- Sorts the list based on which column header was clicked. 
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pNMHDR and LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnColumnclickListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	//Get a pointer to the remote list control
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	//Sort the list based on which column header was clicked (iSubItem)
    (pRemList->SortItems(CompareFuncRem, pNMListView->iSubItem));
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::AddItemRemList (int nIndex, LPCTSTR lpszRemFile,
//		CString csFileSize, FILETIME FileTime, int DirFile)
//
// Sample Call: AddItemRemList(0, csFileName, csRemFileSize,
//			FileTime, DirList);
//
// Description: Adds an item to the remote list control.
//	- Gets a pointer to the control.
//	- Initializes a new ITEMINFO object, pItem, and sets its member's variables.
//	- Initializes a new LV_ITEM object, lvi, and sets its member's variables.
//	- Sets the icon next to the item based on what the item's DirFile 
//		value is.  DirFile = the bitmap's 0-based index in the bitmap image list.
//	- Inserts the item with InsertItem(). 
//
// Input/Output data elements:
//	- Input: Arguments int nIndex, LPCTSTR lpszRemFile, CString csFileSize, 
//		FILETIME FileTime, int DirFile.
//	- Output:  Boolean return value.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
BOOL CMainView::AddItemRemList (int nIndex, LPCTSTR lpszRemFile,
		CString csFileSize, FILETIME FileTime, int DirFile)
{	//Get a pointer to the control
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
    ITEMINFO* pItem;
	pItem = new ITEMINFO;  //Initialize a new ITEMINFO object
	//Set the pItem variables
    pItem->dwFileAttributes = DirFile;
	pItem->strFileName = lpszRemFile;
	pItem->nFileSizeLow =atoi(csFileSize);
	pItem->ftLastWriteTime = FileTime;

    LV_ITEM lvi;
	//Set the masks for the LV_ITEM lvi
    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
	lvi.state = 0;
    lvi.iItem = nIndex; 
    lvi.iSubItem = 0;

	//Set the icon next to the item based on what the item is
	//If it's a directory
	if (DirFile == 0)
		lvi.iImage = 3;		//Use the third image in the bitmap 
	else	//If it's a link
		if (DirFile == 1)
			lvi.iImage = 4;
	else	//If it's a file
		if(DirFile == 2)
			lvi.iImage = 5;
	//If it's the ".." directory, use the "UP" arrow graphic
	if (!(lstrcmp(lpszRemFile, "..")))
		lvi.iImage = 6;
	//The text is CALLBACK, i.e., the application handles it, not us
	lvi.pszText = LPSTR_TEXTCALLBACK;
	//Set the lvi.lParam by typecasting pItem to LPARAM
    lvi.lParam = (LPARAM) pItem;
	//Insert the item, now that all parameters are specified
    if (pRemList->InsertItem (&lvi) == -1)
        return FALSE;

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnGetDispInfoRem (NMHDR* pnmh, LRESULT* pResult)
//
// Sample Call: Not called by the user.
//
// Description: Assigns values to the remote list control's items and subitems.
//	- Places info into LV_DISPINFO* structure plvdi by typecasting 
//		pnmh as LV_DISPINFO*.
//	- Creates ITEMINFO* structure pItem and puts the info into pItem by 
//		typecasting plvdi as ITEMINFO*.
//	- Switch statement uses plvdi->item.iSubItem for cases:
//		- case 0: SubItem 0 is the file name - copies the file name 
//			to the item structure.
//		- case 1: File size - formats the file size into a string and copies 
//			to plvdi->item.pszText.
//		- case 2: Date and time:
//			- Creates a CTime object from the file's FILETIME descriptor.
//			- Formats a string to show the date and time.
//			- Copies the date/time info to the list control.
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pnmh, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnGetDispInfoRem (NMHDR* pnmh, LRESULT* pResult)
{	
    CString string;
	//Place info into plvdi by typecasting pnmh as LV_DISPINFO*
    LV_DISPINFO* plvdi = (LV_DISPINFO*) pnmh;
	//If the mask is set to text
    if (plvdi->item.mask & LVIF_TEXT)
	{
 		//Put the info into pItem by typecasting
       ITEMINFO* pItem = (ITEMINFO*) plvdi->item.lParam;

	   //Which item are we dealing with?
		switch (plvdi->item.iSubItem)
		{

        case 0: //SubItem 0 is the file name
			//Copy the file name to the item structure
			::lstrcpy (plvdi->item.pszText, (LPCTSTR) pItem->strFileName);
      	      break;

        case 1: // File size
			//Don't show file size for ".."
			if(pItem->strFileName == "..")
				::lstrcpy (plvdi->item.pszText, "");
			else
			{	//Format and show the filesize
				string.Format ("%u", pItem->nFileSizeLow);
				::lstrcpy (plvdi->item.pszText, (LPCTSTR) string);
			}
			break;

		case 2: // Date and time
			//Don't show for ".." directory
			if(pItem->strFileName == "..")
				::lstrcpy (plvdi->item.pszText, "");
			else
			{	//Create a CTime object from the file's FILETIME
				// descriptor
				CTime time (pItem->ftLastWriteTime);
				int nHour = time.GetHour();
				//Format to show the date and time
				string.Format ("%0.2d/%0.2d/%0.2d  %0.2d:%0.2d",
				time.GetMonth(), time.GetDay(), time.GetYear() % 100,
					nHour, time.GetMinute());
				//Copy the date/time info to the list control
				::lstrcpy (plvdi->item.pszText, (LPCTSTR)string);
			}
			break;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// int CALLBACK CMainView::CompareFuncRem (LPARAM lParam1, LPARAM lParam2,
//	LPARAM lParamSort)
//
// Sample Call: SortItems(CompareFuncRem, 0);
//
// Description: A callback function - sorts the remote list control.
//	- Fills in ITEMINFO* structures by typecasting lParams to ITEMINFO*.
//	- Determines return value nResult based on whether items are files 
//		or directories.  nResult tells function which item precedes which. 
//	- Switch statement uses lParamSort for cases - lParamSort is 
//		the subitem to sort on:
//		- case 0: SubItem 0 is the file name - sorts by file name.
//		- case 1: SubItem 1 is the file size - sorts by file size.
//		- case 2: SubItem 2 is the file date and time - sorts by 
//			date and time:
//
// Input/Output data elements:
//	- Input: Arguments LPARAM lParam1, LPARAM lParam2, lPARAM lParamSort. 
//	- Output: Returns integer value nResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
int CALLBACK CMainView::CompareFuncRem (LPARAM lParam1, LPARAM lParam2,
	LPARAM lParamSort)
{	//Fill in ITEMINFO structures by typecasting lParams to ITEMINFO*
    ITEMINFO* pItem1 = (ITEMINFO*) lParam1;
    ITEMINFO* pItem2 = (ITEMINFO*) lParam2;
    int nResult;

	//If item 1 is the hidden directory "..", return -1, means that 
	//item 1 precedes item 2
	if(pItem1->strFileName == "..") 
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 2 is the hidden directory "..", return 1, means that 
	//item 2 precedes item 1
	if(pItem2->strFileName == "..") 
	{
	   nResult = 1;
	   return nResult;
	}
	//If comparing drive letters, item 1 always preceds item 2
	if((pItem2->strFileName.GetAt(1) == ':') && 
		!(pItem1->strFileName.GetAt(1) == ':'))
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 1 is a directory and item 2 is not, item 1 precedes
	if ((pItem1->dwFileAttributes == 0) &&
	   !(pItem2->dwFileAttributes == 0))
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 2 is a directory and item 1 is not, item 2 precedes
	if ((pItem2->dwFileAttributes == 0) &&
	   !(pItem1->dwFileAttributes == 0))
	{
	   nResult = 1;
	   return nResult;
	}

    switch (lParamSort)
	{	//lParamSort is the subitem to sort on 
		case 0: //Sorting by file name
			nResult = pItem1->strFileName.CompareNoCase
				(pItem2->strFileName);
			break;

		case 1: //Sorting by file size
			nResult = pItem1->nFileSizeLow - pItem2->nFileSizeLow;
			break;

		case 2: //Sort by date and time
			nResult = ::CompareFileTime (&pItem1->ftLastWriteTime,
            &pItem2->ftLastWriteTime);
			break;
    }
    return nResult;
}

/////////////////////////////////////////////////////////////////////
// CMainView local list control functions
//

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnDblclkListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: OnDblclkListLocal(pNMHDR, pResult);
//
// Description: Called when the user double-clicks on the local list.
//	- Gets a pointer to the local list control.
//	- Initializes variables.
//	- Determines if the mouse pointer was over a list item when clicked. 
//	- Sets the attributes for the LV_ITEM object.
//	- Gets the item's information using GetItem().
//	- If the item being double-clicked is a file, sends the file 
//		using OnClickedButtonMainLocsend() and returns.
//	- Gets the text of the item's label.
//	- If the item is a drive, sets the drive as the local path.
//	- If the item's text is "..", truncates the local path list 
//		to the parent directory.
//	- If the item is a normal directory, modifies the local path string.
//	- Gets the directory's attributes using FindFirstFile() .
//	- Adds the new list and redraws it using DrawLocList().
//
// Input/Output data elements:
//	- Input arguments NMHDR* pNMHDR and LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnDblclkListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
{	//Get a pointer to the list control
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);

	CString csTemp;
	int nIndex = 0;

    HANDLE hFind;
    WIN32_FIND_DATA fddc;

	LV_ITEM lvLoc;

	if((nIndex = pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{	//If a list item was under the mouse pointer
		CWaitCursor cWait;

		//Set attributes for the LV_ITEM object
		lvLoc.mask = LVIF_IMAGE;	//Image bit is valid
		lvLoc.iItem = nIndex;	//Get info for the clicked item
		lvLoc.iSubItem = 0;	//Don't want info on subitems 
		pList->GetItem(&lvLoc);	//Get the info
		
		//If selection is a file
		if(lvLoc.iImage == 5)
		{
			//Send the file
			OnClickedButtonMainLocsend();
			return;
		}

		//Get that item's text
		csTemp = pList->GetItemText(nIndex, 0);
		//If the item is a drive, list it
		if(csTemp[1] == ':')	
			m_csLocPath = csTemp;
		//If "..", truncate path list to parent directory
		else if(csTemp == "..")	
		{	
			m_csLocPath = m_csLocPath.Left(m_csLocPath.ReverseFind('\\'));
			if(m_csLocPath.GetLength() < 3)
				m_csLocPath += '\\';
		}
		else	//Should be a normal directory 
		{	//Check if the string ends in"\"
			if(m_csLocPath.Right(1) == '\\')
				//If so, just add the name to the end
				csTemp = m_csLocPath + csTemp;
			else
				//If not, add "\" and then the name
				csTemp = m_csLocPath + "\\" + csTemp;
			//If the item is a directory, list it
			if ((hFind = ::FindFirstFile((LPCTSTR)csTemp,
				&fddc)) != INVALID_HANDLE_VALUE)
			{
				if (fddc.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
					m_csLocPath = csTemp;
				CloseHandle (hFind);
			}
		}
	//Add the new list and redraw it
	DrawLocList();
	}
	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnGetDispInfo (NMHDR* pnmh, LRESULT* pResult)
//
// Sample Call: Not called by the user.
//
// Description: Assigns values to the local list control's items and subitems.
//	- Places info into LV_DISPINFO* structure plvdi by 
//		typecasting pnmh as LV_DISPINFO*.
//	- Creates ITEMINFO* structure pItem and puts the info into pItem by 
//		typecasting plvdi as ITEMINFO*.
//	- Switch statement uses plvdi->item.iSubItem for cases:
//		- case 0: SubItem 0 is the file name - copies the file name 
//			to the item structure.
//		- case 1: File size - formats the file size into a string and copies 
//			to plvdi->item.pszText.
//		- case 2: Date and time:
//			- Creates a CTime object from the file's FILETIME descriptor.
//			- Formats a string to show the date and time.
//			- Copies the date/time info to the list control.
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pnmh, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnGetDispInfo (NMHDR* pnmh, LRESULT* pResult)
{	
	CString string;
	//Place info into plvdi by typecasting pnmh as LV_DISPINFO*
	LV_DISPINFO* plvdi = (LV_DISPINFO*) pnmh;
	//If the mask is set to text
    if (plvdi->item.mask & LVIF_TEXT)
	{
 		//Put the info into pItem by typecasting
		ITEMINFO* pItem = (ITEMINFO*) plvdi->item.lParam;

	   //Which item are we dealing with?
        switch (plvdi->item.iSubItem)
		{

        case 0: //File name
			::lstrcpy (plvdi->item.pszText, (LPCTSTR) pItem->strFileName);
      	      break;

        case 1: //File size
			//Don't show file size for drives and directories
			if((pItem->strFileName.GetAt(1)== ':') || 
				(pItem->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			::lstrcpy (plvdi->item.pszText, "");
			else
			{
			string.Format ("%u", pItem->nFileSizeLow);
			::lstrcpy (plvdi->item.pszText, (LPCTSTR) string);
			}
			break;

        case 2: //Date and time
			//Don't show for drives or ".." directory
			if((pItem->strFileName.GetAt(1) == ':')
				|| (pItem->strFileName == ".."))
            ::lstrcpy (plvdi->item.pszText, "");
			else
			{
				CTime time(pItem->ftLastWriteTime);
				int nHour = time.GetHour();
				string.Format ("%0.2d/%0.2d/%0.2d  %0.2d:%0.2d",
				time.GetMonth(), time.GetDay(), time.GetYear() % 100,
					nHour, time.GetMinute());
				::lstrcpy (plvdi->item.pszText, (LPCTSTR)string);
			}
			break;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnColumnClick (NMHDR* pnmh, LRESULT* pResult)
//
// Sample Call: OnColumnClick (pnmh, pResult);
//
// Description: Called when the user left-clicks the mouse on a column 
// header  on the local list control.
//	- Creates an NM_LISTVIEW* pointer by typecasting pNMHDR.
//	- Gets a pointer to the local list control.
//	- Sorts the list based on which column header was clicked. 
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pNMHDR and LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
void CMainView::OnColumnClick (NMHDR* pnmh, LRESULT* pResult)
{
	NM_LISTVIEW* pnmlv = (NM_LISTVIEW*) pnmh;
	//Get a pointer to the local list control
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	//Sort the list based on which column header was clicked
	(pList->SortItems(CompareFunc, pnmlv->iSubItem));
}


/////////////////////////////////////////////////////////////////////////////
// int CALLBACK CMainView::CompareFunc (LPARAM lParam1, LPARAM lParam2,
//	LPARAM lParamSort)
//
// Sample Call: SortItems(CompareFunc, 0);
//
// Description: A callback function - sorts the local list control.
//	- Fills in ITEMINFO* structures by typecasting lParams to ITEMINFO*.
//	- Determines return value nResult based on whether items are files, 
//		directories, or drives.  nResult tells function which item 
//		precedes which. 
//	- Switch statement uses lParamSort for cases - lParamSort is the 
//		subitem to sort on:
//		- case 0: SubItem 0 is the file name - sorts by file name.
//		- case 1: SubItem 1 is the file size - sorts by file size.
//		- case 2: SubItem 2 is the file date and time - sorts by date and time:
//
// Input/Output data elements:
//	- Input: Arguments LPARAM lParam1, LPARAM lParam2, lPARAM lParamSort.
//	- Output: Returns integer value nResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
int CALLBACK CMainView::CompareFunc (LPARAM lParam1, LPARAM lParam2,
	LPARAM lParamSort)
{
    ITEMINFO* pItem1 = (ITEMINFO*) lParam1;
    ITEMINFO* pItem2 = (ITEMINFO*) lParam2;
    int nResult;

	//Check to see if new folder is being added, put below ".."	
	if((pItem1->strFileName == "") && 
		(pItem2->strFileName == ".."))
	{
	   nResult = 1;
	   return nResult;
	}
	//Check to see if new folder is being added, put below ".."	
	if((pItem1->strFileName == "..") && 
		(pItem2->strFileName == ""))
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 1 is a drive letter and item 2 is not, item 2 precedes
	if((pItem1->strFileName.GetAt(1) == ':') && 
		!(pItem2->strFileName.GetAt(1) == ':'))
	{
	   nResult = 1;
	   return nResult;
	}
	//If item 2 is a drive letter and item 1 is not, item 2 precedes
	if((pItem2->strFileName.GetAt(1) == ':') && 
		!(pItem1->strFileName.GetAt(1) == ':'))
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 1 is a directory and item 2 is not, item 1 precedes
	if ((pItem1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
	   !(pItem2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
	{
	   nResult = -1;
	   return nResult;
	}
	//If item 2 is a directory and item 1 is not, item 2 precedes
	if ((pItem2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
	   !(pItem1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
	{
	   nResult = 1;
	   return nResult;
	}

    switch (lParamSort)
	{	//lParamSort is the subitem to sort on 
    case 0: //File name
        nResult = pItem1->strFileName.CompareNoCase (pItem2->strFileName);
        break;

    case 1: //File size
        nResult = pItem1->nFileSizeLow - pItem2->nFileSizeLow;
        break;

    case 2: //Date and time
        nResult = ::CompareFileTime (&pItem1->ftLastWriteTime,
            &pItem2->ftLastWriteTime);
        break;
    }
    return nResult;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::InitLocList(LPCTSTR pszPath)
//
// Sample Call: InitLocList("C:\");
//
// Description: Initializes the local list control - gets the list's 
// items and draws list.
//	- Get a pointer to the local list control.
//	- Checks to see if the directory specified by pszPath is valid. 
//	- If the directory path already exists in the local combo box, 
//		select it; else, add it to the combobox and select it.
//	- Begins adding files and subdirectories to the list control using 
//		FindFirstFile(), FindNextFile(), and AddItem)().
//	- Adds all local drives to the list using GetLogicalDrive().
//
// Input/Output data elements:
//	- Input: Arguments LPCTSTR pszPath.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
void CMainView::InitLocList(LPCTSTR pszPath)
{	//pszPath is the directory to be shown
    CString strPath = pszPath;
	//Get a pointer to the local list control
	CComboBox *pcbLoc = (CComboBox *)GetDlgItem(IDC_COMBO_LOCDIR);
	//Check to see if the directory is valid
	if(_chdir(strPath))
	{	//If not, show an error box and default to the root
		AfxMessageBox("Couldn't change directory - defaulting to C:\\");
		strPath = "C:\\";
		m_csLocPath = strPath;
	}

	//If the selection exists in combo box, select it; else, add
	if(pcbLoc->FindStringExact(-1, m_csLocPath) == CB_ERR)
	{				
		pcbLoc->AddString(m_csLocPath);	//Add the string
		//Show it in the edit box portion of the combobox
		pcbLoc->SetCurSel(pcbLoc->FindStringExact(-1, m_csLocPath));
	}
	else	//Already exists, so just select and show it
		pcbLoc->SetCurSel(pcbLoc->FindStringExact(-1, m_csLocPath));

	//If the last character of the path is not "\", add it 
    if (strPath.Right(1) != "\\")
        strPath += "\\";
    strPath += "*.*";	//Select all items in the directory

    HANDLE hFind;
    WIN32_FIND_DATA fd;
	//Begin adding files to the list - file info is put into fd
    if ((hFind = ::FindFirstFile ((LPCTSTR) strPath, &fd)) !=
        INVALID_HANDLE_VALUE)
	{
		//Don't show the "." directory
		if(lstrcmp(".", fd.cFileName))
            AddItem (&fd);
		//Find and add the rest of the files and directories
       while (::FindNextFile (hFind, &fd))
		{			
              if (!AddItem (&fd))
                  break;
		}
        CloseHandle (hFind);
    }

    int nPos = 0;
    CString strDrive = "?:\\";
	//Put the drives into an integer - drive A: is bit 0, B is 1, etc
    DWORD dwDriveList = ::GetLogicalDrives ();

    while (dwDriveList)	//While drives remain to be listed
	{
        if (dwDriveList & 1)
		{	//Set the drive letter ? in strDrive
            strDrive.SetAt(0, 0x41 + nPos);
			//Add the drive to the list
			AddDriveNode (strDrive);
        }
		//Shift the bits one bit right in dwDriveList
        dwDriveList >>= 1;
        nPos++;
    }
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::AddItem(WIN32_FIND_DATA* pfd)
//
// Sample Call: AddItem(&pfd);
//
// Description: Adds an item to the local list control.
//	- Get a pointer to the local list control.
//	- Initializes ITEMINFO* structure pItem and fills in member variables 
//		from from pfd's member variables. 
//	- Creates LV_ITEM structure lvi and sets its the mask flags.
//	- Specifies the bitmap image for the item depending on what kind 
//		of item it is.
//	- Inserts the item into the list.
//
// Input/Output data elements:
//	- Input: Argument WIN32_FIND_DATA* pfd.
//	- Output: Boolean return value.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
BOOL CMainView::AddItem(WIN32_FIND_DATA* pfd)
{	//Get a pointer to the control
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
    ITEMINFO* pItem;
	pItem = new ITEMINFO;
	//Initialize pItem from pfd
    pItem->dwFileAttributes = pfd->dwFileAttributes;
	pItem->strFileName = pfd->cFileName;
	pItem->nFileSizeLow = pfd->nFileSizeLow;
	pItem->ftLastWriteTime = pfd->ftLastWriteTime;

    LV_ITEM lvi;
	//Set the mask flags for lvi
    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
	lvi.state = 0;
    lvi.iItem = 0; 
    lvi.iSubItem = 0;
	//If item is not a directory
	if (!(pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
	{
		//Specify the file bitmap
		lvi.iImage = 5;
	}
		else	//Directory bitmap
		lvi.iImage = 3;
	//Specify the "UP" arrow bitmap for ".."	
	if (!(lstrcmp(pfd->cFileName, "..")))
		lvi.iImage = 6;

	lvi.pszText = LPSTR_TEXTCALLBACK; 
    lvi.lParam = (LPARAM) pItem;
	//Insert the item into the list
    if (pList->InsertItem (&lvi) == -1)
        return FALSE;

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::AddDriveNode (CString& strDrive)
//
// Sample Call: AddDriveNode(strDrive);
//
// Description: Adds a drive item to the local list control.
//	- Get a pointer to the local list control.
//	- Initializes ITEMINFO* structure pItem and fills in the drive name 
//		and time from the local drive time. 
//	- Creates LV_ITEM structure lvi and sets its mask flags and other
//		 member variables.
//	- Finds out what kind of drive it is, and assigns a bitmap image 
//		depending on the drive type.
//	- Inserts the item into the list.
//
// Input/Output data elements:
//	- Input: Argument CString& strDrive.
//	- Output: Boolean return value.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
BOOL CMainView::AddDriveNode (CString& strDrive)
{
	//Get a pointer to the control
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
    CString string;

	FILETIME dwLocDriveTime; 
    ITEMINFO* pItem;

	pItem = new ITEMINFO;

    pItem->dwFileAttributes = 0;
	pItem->strFileName = strDrive;
	pItem->nFileSizeLow = 0;
	pItem->ftLastWriteTime = dwLocDriveTime;

    LV_ITEM lvi;
    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
	lvi.state = 0;
    lvi.iItem = 0; 
    lvi.iSubItem = 0;
	lvi.pszText = LPSTR_TEXTCALLBACK; 
    lvi.lParam = (LPARAM) pItem;

	//Find out what kid of drive it is
    UINT nType = ::GetDriveType ((LPCTSTR) strDrive);

    switch (nType)
	{
	//Specify bitmap image based on drive type
    case DRIVE_REMOVABLE:
		lvi.iImage = 1;
        pList->InsertItem (&lvi);
        break;

    case DRIVE_FIXED:
		lvi.iImage = 0;
        pList->InsertItem(&lvi);
        break;

    case DRIVE_REMOTE:
		lvi.iImage = 0;
        pList->InsertItem(&lvi);
        break;

    case DRIVE_CDROM:
		lvi.iImage = 2;
        pList->InsertItem(&lvi);
        break;

    case DRIVE_RAMDISK:
		lvi.iImage = 0;
        pList->InsertItem(&lvi);
        break;

    default:
        return FALSE;
    }
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::FreeItemMemory ()
//
// Sample Call: FreeItemMemory ();
//
// Description: Frees memory used by pItems in the local list.
//	- Gets a pointer to the local list control.
//	- Finds out how many items are in the list, and deletes 
//		the item data for each. 
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
void CMainView::FreeItemMemory ()
{	//Get a pointer to the control
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	//How many items are there?
    int nCount = pList->GetItemCount();
    if(nCount)
	{	//Delete the item data
        for(int i=0; i<nCount; i++)
            delete (ITEMINFO*) pList->GetItemData(i);
    }
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::FreeItemMemoryRem()
//
// Sample Call: FreeItemMemoryRem();
//
// Description: Frees memory used by pItems in the remote list.
//	- Gets a pointer to the remote list control.
//	- Finds out how many items are in the list, and deletes 
//		the item data for each.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
void CMainView::FreeItemMemoryRem()
{
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);

    int nCount = pRemList->GetItemCount();
    if(nCount)
	{
        for(int i=0; i<nCount; i++)
            delete (ITEMINFO*) pRemList->GetItemData(i);
    }
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::PrintToStatus(LPCSTR lpStr, LPCSTR lpStr1)
//
// Sample Call: PrintToStatus("Hi There", NULL);
//
// Description: Prints a line to the list box and scrolls, if necessary.
//	- If lpStr1 is not NULL, adds lpStr1 to the end of lpStr.
//	- Gets a pointer to the list box control.
//	- Adds the string to the list box, and scrolls to 
//		the bottom, if necessary. 
//
// Input/Output data elements:
//- Input: Arguments LPCSTR lpStr, LPCSTR lpStr1.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
void CMainView::PrintToStatus(LPCSTR lpStr, LPCSTR lpStr1)
{	//Write the string to the status box
	if(lpStr1 == NULL)
		wsprintf(m_lpszMessage, lpStr);
	else
		wsprintf(m_lpszMessage, lpStr, lpStr1);
	//Insert the text into the status box
	CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
	plb->SetTopIndex(plb->AddString(m_lpszMessage));
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::PrintToStatusSpace()
//
// Sample Call: PrintToStatusSpace();
//
// Description: Prints a blank line to the list box.
//	- Gets a pointer to the list box control.
//	- Adds the string to the list box, and scrolls to 
//		the bottom, if necessary. 
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::PrintToStatusSpace()
{
	//Write an empty new line to the status box
	CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
	plb->SetTopIndex(plb->AddString(m_lpszMessage));
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnSelchangeComboLocdir() 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user changes the local combobox selection.
//	- Gets pointers to the local list control and combobox.
//	- Gets index of new combobox selection and it's text.
//	- Resets the official local path based on the selection's text.
//	- Adds the new list to the local list control and redraws it. 
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnSelchangeComboLocdir() 
{	
	CWaitCursor cWait;
	char SelTemp[1000];
	//Get pointers to the local list and combobox
	CComboBox *pcbLoc = (CComboBox *)GetDlgItem(IDC_COMBO_LOCDIR);
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	// Get index of new combobox selection and then it's text
    int index = pcbLoc->SendMessage(CB_GETCURSEL, (WORD)0, 0L);
    pcbLoc->SendMessage(CB_GETLBTEXT, (WORD)index, (LONG)SelTemp);
	//Reset the official local path
	m_csLocPath.Format(SelTemp);
	//Add the new list and redraw it
	DrawLocList();
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnSelchangeComboRemdir() 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user changes the remote combobox selection.
//	- Gets pointers to the remote list control and combobox.
//	- Gets index of new combobox selection and it's text.
//	- Changes the remote directory listing by sending a CWD command.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnSelchangeComboRemdir() 
{
	CWaitCursor cWait;
	char SelTempRem[1000];

	//Get pointers to the remote list and combobox
	CComboBox *pcbRem = (CComboBox *)GetDlgItem(IDC_COMBO_REMDIR);
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	m_nBytesReceived = 0;

	//Get index of new combobox selection and then it's text
    int index = pcbRem->SendMessage(CB_GETCURSEL, (WORD)0, 0L);
    pcbRem->SendMessage(CB_GETLBTEXT, (WORD)index, (LONG)SelTempRem);
	//Change the remote directory listing
	SendCommand("CWD ", SelTempRem);
	if(ReadResponse())
		PostDirectory();
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainLocrefresh() 
//
// Sample Call: OnClickedButtonMainLocrefresh();
//
// Description: Called when the user pushes the local "Refresh" button.
//	- Redraws the local list, showing the m_csLocPath *.* directory.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainLocrefresh() 
{	//Redraw the local list, showing the m_csLocPath *.* directory
	DrawLocList();
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainRemrefresh() 
//
// Sample Call: OnClickedButtonMainRemrefresh();
//
// Description: : Called when the user pushes the remote "Refresh" button.
//	- Reposts the current directory to the remote list by 
//		calling PostDirectory().
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainRemrefresh() 
{	//Re post the current directory to the remote list
	PostDirectory();
	return;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::DrawLocList() 
//
// Sample Call: DrawLocList();
//
// Description: Redraws the local list control.
//	- Gets a pointer to the local list control.
//	- Sets the redraw option to FALSE to keep the list from showing 
//		all the deletion and addition of items.
//	- Deletes all items in the list.
//	- Calls InitLocList() to add all items from the m_csLocPath path.
//	- Sorts the items by calling CompareFunc.
//	- Sets the redraw option to TRUE.
//	- Calls InvalidateRect() to force the list to be redrawn with 
//		the new items.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::DrawLocList() 
{	//Get a pointer to the list
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	//Don't redraw the list yet
	SetRedraw(FALSE);
	pList->DeleteAllItems();	//Delete all list items
	InitLocList(m_csLocPath);	//Add items from m_csLocpath
	//Sort items on directory/file names (subitem 0)
	pList->SortItems(CompareFunc, 0);
	SetRedraw(TRUE);	//Now allow redraws
	InvalidateRect(NULL,FALSE);	//Redraw the list
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnBeginlabeleditListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user initiates a label edit 
// on the local list.
//	- Gets a pointer to the local list control.
//	- Gets the label text of the selected item.
//	- If the item is a drive, disallows label editing by setting *pResult to 1.
//
// Input/Output data elements:
//	- Input: Arguments NMHDR* pNMHDR, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnBeginlabeleditListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
{
	int nBLE,
		nLen = 0;
	//Get a pointer to the list
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);

	//Get the label text of the selected item
	if((nBLE = pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
		pList->GetItemText(nBLE, 0, m_pszLocLE, nLen);

	//If the item is a drive, disallow label editing
	if(m_pszLocLE[1] == ':')
		*pResult = 1;
	else
		*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnBeginlabeleditListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user initiates a label edit on the remote list.
//	- Creates an LV_DISPINFO* pDispInfo structure and sets it equal to 
//		pNMHDR, which is typecast to LV_DISPINFO*.
//	- Gets a pointer to the remote list control.
//	- Gets the label text of the selected item.
//
// Input/Output data elements:
//	- Input arguments NMHDR* pNMHDR, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnBeginlabeleditListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	int nBLER,
		nLen = 0;
	//Get a pointer to the list
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);

	//Get the current label text of the selected item
	if((nBLER = pRemList->GetNextItem(-1, LVNI_SELECTED)) != -1)
		pRemList->GetItemText(nBLER, 0, m_pszRemLE, nLen);
	
	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnEndlabeleditListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user finishes a label edit on the local list.
//	- Gets pointers to the local list and combobox.
//	- Creates an LV_ITEM lvEditLabel structure.
//	- Creates an LV_DISPINFO* plvdi structure and sets it equal to 
//		pNMHDR, which is typecast to LV_DISPINFO*.
//	- Determines if a change was made to the label - if not, refreshes 
//		the local list to remove the new folder, and returns.
//	- Else, gets the label text of the new item.
//	- Gets the index and text of the current combobox selection.
//	- Adds the new name to the end of the current path.
//	- Attempts to add or rename the directory.
//	- Sets the new item label text.
//	- Refreshes the local list whether or not the label change was valid.
//
// Input/Output data elements:
//	- Input arguments NMHDR* pNMHDR, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnEndlabeleditListLocal(NMHDR* pNMHDR, LRESULT* pResult) 
{
	int nELE;
	CString csOldTxt,
			csNewTxt,
			csPath;
	//Get pointers to the local list and combobox
	CComboBox* pcbLoc = (CComboBox*)GetDlgItem(IDC_COMBO_LOCDIR);
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	LV_ITEM lvEditLabel;
	//Get plvdi info by typecasting pNMHDR
	LV_DISPINFO* plvdi = (LV_DISPINFO*) pNMHDR;
	
	//Was a change made to the label at all?
	if(!plvdi->item.pszText)
	{	//No change
		wsprintf(m_pszLocLE,"");
		m_bLocMkDir = FALSE;
		//Refresh the list
		OnClickedButtonMainLocrefresh();
		return;
	}
	else	//Get the new label
		csNewTxt.Format(plvdi->item.pszText);
	
	//If new label is not NULL and isn't the same as the old label
	if((csNewTxt != "") && (csOldTxt != csNewTxt))
	{	//get the index of the current combobox selection
		nELE = pcbLoc->GetCurSel();
		//Get the text of the current combobox selection
		pcbLoc->GetLBText(nELE ,csPath);
		//If the text isn't NULL
		if( csPath != "")
		{	//Add the name to the end of the path
			if(csPath.Right(1) != '\\')	//If path doesn't end in "\"
			{	//Add "\" and the name to the end
				csOldTxt = csPath + "\\" + m_pszLocLE;
				csNewTxt = csPath + "\\" + csNewTxt;
			}
			else
			{	//Add the name to the end
				csOldTxt = csPath + m_pszLocLE;
				csNewTxt = csPath + csNewTxt;
			}
			//Renaming or making a directory?
			if(m_bLocMkDir)	//Making a directory
			{	//If directory is created successfully
				if(!(::_mkdir(csNewTxt)))
					//Change the label text to the new name 
					lvEditLabel.pszText = plvdi->item.pszText;
				else	//Label text is NULL
					lvEditLabel.pszText = m_pszLocLE;
			}
			else
			{
				//Change the label onscreen
				if(!(::rename(csOldTxt, csNewTxt)))
					//If successful, change the label text
					lvEditLabel.pszText = plvdi->item.pszText;
				else	//label text doesn't change
					lvEditLabel.pszText = m_pszLocLE;
			}
		}
		//Set the mask to Text
		lvEditLabel.mask = LVIF_TEXT;
		//Set the same item
		lvEditLabel.iItem = plvdi->item.iItem;
		//Change the label text (subitem 0)
		lvEditLabel.iSubItem = 0;
		//Set the item text
		pList->SetItem(&lvEditLabel);
	}
	else
	{	//The label change wasn't valid - refresh the list
		m_bLocMkDir = FALSE;
		wsprintf(m_pszLocLE, "");
		OnClickedButtonMainLocrefresh();
		return;
	}
	m_bLocMkDir = FALSE;
	wsprintf(m_pszLocLE,"");
	OnClickedButtonMainLocrefresh();
	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnEndlabeleditListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
//
// Sample Call: Not called by the user.
//
// Description: Called when the user finishes a label edit on the remote list.
//	- Creates an LV_DISPINFO* pDispInfo structure and sets it equal to 
//		pNMHDR, which is typecast to LV_DISPINFO*.
//	- Determines if a change was made to the label - if not, returns.
//	- Else, adds directory with MKD command, or renames file 
//		with RNFR and RNTO commands.
//	- Ends mouse capture to release mouse pointer from the screen.
//
// Input/Output data elements:
//	- Input arguments NMHDR* pNMHDR, LRESULT* pResult.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnEndlabeleditListRemote(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	CString csNewTxt;

	//Was a change made to the label at all?
	if(!pDispInfo->item.pszText)	//No
	{
		wsprintf(m_pszRemLE,"");
		m_bRemMkDir = FALSE;
		return;
	}
	else
		csNewTxt.Format(pDispInfo->item.pszText);	//Get the new label

	//If new label is not NULL and isn't the same as the old label
	if((csNewTxt != "") && (csNewTxt != m_pszRemLE))
	{
		//Renaming or making a directory?
		if(m_bRemMkDir)
		{	//Making directory
			SendCommand("MKD ", csNewTxt);
			if(ReadResponse())	//If successful, PWD
				PostDirectory();
		}
		else
		{	//Renaming a file
			SendCommand("RNFR ", m_pszRemLE);
			if(ReadResponse())	//If RNFR successful
			{	//Rename
				SendCommand("RNTO ", csNewTxt);
				if(ReadResponse())	//If successful, PWD
					PostDirectory();
			}
		}
	}
	//End mouse capture - WM_LBUTTONUP message has not been received
	ReleaseCapture();
	m_bRemMkDir = FALSE;
	wsprintf(m_pszRemLE,"");
	*pResult = 0;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainLocmkdir() 
//
// Sample Call: OnClickedButtonMainLocmkdir();
//
// Description: Called when the user clicks the local "Make Directory" button.
//	- Gets a pointer to the local list.
//	- Puts the local time into a SYSTEMTIME object.
//	- Changes the system time to file time.
//	- Sets directory attributes in the WIN32_FIND_DATA structure.
//	- Adds the new item to the list by calling AddItem().
//	- Sorts the list.
//	- Finds the item in the list with FindItem().
//	- Sets the list's focus to that item.
//	- Automatically begins the label edit by calling EditLabel().
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainLocmkdir() 
{	//Initialize and declare variables
	WIN32_FIND_DATA fdMkDir;
	FILETIME ftMkDir;
	SYSTEMTIME stLocTime;
	LV_FINDINFO fiMkDir;

	//Get a pointer to the local list
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	int nMDIndex;

	m_bLocMkDir = TRUE;	//Falg indicates that we are making a directory
	//Put the local time into a SYSTEMTIME object
	::GetLocalTime(&stLocTime);
	//Change sytem time to file time
	::SystemTimeToFileTime(&stLocTime, &ftMkDir);

	//This is a directory
    fdMkDir.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
	wsprintf(fdMkDir.cFileName,"");	//The initial directory name is NULL 
	fdMkDir.nFileSizeLow = 0;
	fdMkDir.ftLastWriteTime = ftMkDir;
	fiMkDir.flags = LVFI_STRING;
	fiMkDir.psz = "";

	//Add the new item to the list
	AddItem(&fdMkDir);
	pList->SortItems(CompareFunc, 0);	//Sort the list
	//Find the item in the list
	nMDIndex = pList->FindItem(&fiMkDir, -1);
	//Set the list's focus to that item
	pList->SetFocus();
	//Begin the label edit automatically
	pList->EditLabel(nMDIndex);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainRemmkdir() 
//
// Sample Call: OnClickedButtonMainRemmkdir();
//
// Description: Called when the user clicks the remote "Make Directory" button.
//	- Gets a pointer to the remote list.
//	- Puts the local time into a SYSTEMTIME object.
//	- Changes the sytem time to file time.
//	- Adds the new item to the list by calling AddItemRemList().
//	- Sorts the list.
//	- Finds the item in the list with FindItem().
//	- Sets the list's focus to that item.
//	- Automatically begins the label edit by calling EditLabel().
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainRemmkdir() 
{
	FILETIME ftMkDir;
	SYSTEMTIME stLocTime;
	LV_FINDINFO fiMkDir;
	//Get a pointer to the remote list
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	int nMDIndex;

	m_bRemMkDir = TRUE;

	::GetLocalTime(&stLocTime);
	::SystemTimeToFileTime(&stLocTime, &ftMkDir);

	fiMkDir.flags = LVFI_STRING;
	fiMkDir.psz = "";

	AddItemRemList (0, "", "0", ftMkDir, 0);
	pRemList->SortItems(CompareFuncRem, 0);
	//Find the item in the list
	nMDIndex = pRemList->FindItem(&fiMkDir, -1);
	//Set the list's focus to that item
	pRemList->SetFocus();
	//Begin the label edit automatically
	pRemList->EditLabel(nMDIndex);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainLocdel() 
//
// Sample Call: OnClickedButtonMainLocdel();
//
// Description: Called when the user clicks the local "Delete" button.
//	- Gets pointers to the local list control and combobox.
//	- Creates an LV_ITEM lvLocDel structure and sets the image mask.
//	- Detects if any items are selected - if, so, loops while 
//		detecting all selected items.
//	- Gets the item's text from the list control.
//	- Gets the item's index number and text from the combobox.
//	- Adds the item's name to the end of the path list .
//	- Gets the item's bitmap image info to determine what type of 
//		item the selection is.
//	- Deletes the file or directory.
//	- Refreshes the local list to show the updated listing.
//
// Input/Output data elements: None.
//
// Error handling:
//	- If the directory or file cannot be removed, shows an error dialog box.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainLocdel() 
{	//Get pointers to the list and combobox controls
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	CComboBox* pcbLoc = (CComboBox*)GetDlgItem(IDC_COMBO_LOCDIR);
	CString csLDDPath,
			csLDDTemp;
	int nLDDIndex = 0,
		nLDD = 0;
	LV_ITEM lvLocDel;
	lvLocDel.mask = LVIF_IMAGE;	//Set the mask to image
	lvLocDel.iSubItem = 0;
	//Any items selected?
	if((nLDDIndex = pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{	//If yes, loop 'til no more items are selected
		while((nLDDIndex = pList->GetNextItem((nLDDIndex - 1), LVNI_SELECTED)) != -1)
		{	
			lvLocDel.iItem = nLDDIndex;	//Set the item's index in the LV_ITEM structure
			//Get the item's text
			csLDDTemp = pList->GetItemText(nLDDIndex, 0);
			//Get the current selection's list number
			nLDD = pcbLoc->GetCurSel();
			//Get the text for that selection - it's the current path
			pcbLoc->GetLBText(nLDD ,csLDDPath);
			//If the path isn't NULL
			if( csLDDPath != "")
			{	//If the path doesn't end in "\", add it and the name to the end
				if(csLDDPath.Right(1) != '\\')
					csLDDPath = csLDDPath + "\\" + csLDDTemp;
			else	//Add just the name to the end
				csLDDPath = csLDDPath + csLDDTemp;
			//Get icon info to determine what type selection is
			pList->GetItem(&lvLocDel);
			//If selection is a directory
			if((lvLocDel.iImage == 3) /*|| (lvRem.iImage == 4)*/)	
			{	//Directory deletion successful?
				if(::_rmdir(csLDDPath))
				{	//If not, show error box
					csLDDTemp.Format("Couldn't delete directory %s! " \
						"Directory must be empty before deleting.",
						csLDDPath);
					AfxMessageBox(csLDDTemp);
				}
			}
			else	//If selection is a file
				if(lvLocDel.iImage == 5)
				{//File deleted?
					if(!DeleteFile(csLDDPath))
					{	//If not, show error box
						csLDDTemp.Format("Couldn't delete file %s!",
							csLDDPath);
						AfxMessageBox(csLDDTemp);
					}
				}
			}	
			nLDDIndex++;
		}
		OnClickedButtonMainLocrefresh();	//Refresh the list
	}
	return;
}	

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainRemdel() 
//
// Sample Call: OnClickedButtonMainRemdel();
//
// Description: Called when the user clicks the remote "Delete" button.
//	- Gets pointers to the remote list control.
//	- Creates an LV_ITEM lvRemDel structure and sets the image mask.
//	- Detects if any items are selected - if, so, loops while 
//		detecting all selected items.
//	- Gets the item's text from the list control.
//	- Gets the item's image information with GetItem().
//	- If the item is a directory, sends the RMD command.
//	- If the item is a file, sends the DELE command.
//	- Refreshes the list by calling PostDirectory().
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainRemdel() 
{	//Get pointers to the list and comboboxes
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	CString csRDDPath,
			csRDDTemp;
	int nRDDIndex = 0,
		nRDD = 0;
	LV_ITEM lvRemDel;
	lvRemDel.mask = LVIF_IMAGE;
	lvRemDel.iSubItem = 0;
	//Any items selected?
	if((nRDDIndex = pRemList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{//If yes, loop 'til no more items are selected
		while((nRDDIndex = pRemList->GetNextItem((nRDDIndex - 1), LVNI_SELECTED)) != -1)
		{
			lvRemDel.iItem = nRDDIndex;
			csRDDTemp = pRemList->GetItemText(nRDDIndex, 0);
			//Get icon info to determine what type selection is
			pRemList->GetItem(&lvRemDel);
			//If selection is a directory
			if((lvRemDel.iImage == 3) /*|| (lvRem.iImage == 4)*/)
			{	//Directory deleted?
				SendCommand("RMD ", csRDDTemp);	//Delete the remote directory
				if(!ReadResponse())	//No, break
					break;
			}
			else
				if(lvRemDel.iImage == 5)//If selection is a file
				{	//File deleted?
					//Delete the remote file
					SendCommand("DELE ", csRDDTemp);
					if(!ReadResponse())	//If failed, break
						break;
				}
			nRDDIndex++;
		}
		PostDirectory();	//PWD
	}
	return;
}	

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainRemret() 
//
// Sample Call: OnClickedButtonMainRemret();
//
// Description: Called when the user clicks the Remote "Retrieve" button.
//	- Gets a pointer to the remote list control.
//	- Detects if any items are selected - if, so, loops while 
//		detecting all selected items and putting their names into 
//		a string list.
//	- If an item is selected:
//		- Starts a message pump to pump messages while waiting for a 
//			previous retrieval to complete.
//		- Parses the string for the next file name.
//		- Gets a new list pointer, as the previous one is now no good.
//		- Finds the item in the list, based on its label text.
//		- Gets the item's info with GetItem();
//		- If the item is not a directory, continues, else, prints 
//			an error message to the screen and returns.
//		- Opens either a binary or ASCII  user file, based on the type 
//			specified, and sends the TYPE command.
//		- If successful, creates a local port and sends the RETR command.
//
// Input/Output data elements: None.
//
// Error handling:
//	- If a directory has been selected, prints an error message to the screen.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainRemret() 
{	//Get the list pointer
	CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
	CString csRRList,
		csTemp;
	int nRRIndex = 0,
		nRR = 0;
	MSG msg;
	//Specify that we're going to recieve a file
	m_csListOrFile = "Receiving File";
	LV_ITEM lvRet;
	LV_FINDINFO fiRet;
	lvRet.iSubItem = 0;
	lvRet.mask = LVIF_IMAGE | LVIF_PARAM;
	m_nTotalSent = 0;

	//Is an item selected?
	if((nRRIndex = pRemList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{	//Yes, find all selected items 
		while((nRRIndex = pRemList->GetNextItem((nRRIndex - 1),
			LVNI_SELECTED)) != -1)
		{	//Get the selected items and put them into a string
			csRRList += pRemList->GetItemText(nRRIndex, 0) + "\r\n";
			nRRIndex++;
		}
		//If a file is selected, retrieve it
		while((csRRList != "\r\n") && 
			(nRR = csRRList.Find("\r\n")) != -1)
		{	//Sit and spin while previous file is retrieved
			while(m_cfUserFile)
			{	//Pump messages while waiting
				while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 
				{ 
					if(!AfxGetApp()->PumpMessage()) 
					{	//If user wants to Quit, break 
						::PostQuitMessage(0); 
						 break; 
					} 
				} 
			}
			//Parse the string for the next file name
			m_csLocalFile = csRRList.Left(nRR);
			csRRList = csRRList.Right(csRRList.GetLength() - nRR - 2);
			//Get a new list pointer - previous one is no good
			CListCtrl* pRemList = (CListCtrl*)GetDlgItem(IDC_LIST_REMOTE);
			fiRet.flags = LVFI_STRING;
			fiRet.psz = m_csLocalFile;
			lvRet.iItem = pRemList->FindItem(&fiRet, -1);
			//Get the selected item's information
			pRemList->GetItem(&lvRet);
			//Put the info into an ITEMINFO structure
			ITEMINFO* pItem = (ITEMINFO*) lvRet.lParam;
			//Get the file size
			m_nFileSize = pItem->nFileSizeLow;

			//It is a link
			if(lvRet.iImage == 4)
			{
				m_csLocalFile = m_csLocalFile.Right(
					m_csLocalFile.GetLength() - 
					m_csLocalFile.ReverseFind('/') - 1);
			}


			//Don't retrieve directories
			if((lvRet.iImage != 3) /*|| (lvRem.iImage == 4)*/)	
			{	
				m_nBytesReceived = 0;	//Counter for the progress control
				if(!m_bBinary || (m_bAuto && m_csLocalFile.Right(3)== "txt"))
				{	//If "Binary" is not specified, and this is a text file,
					//open a text file
					m_cfUserFile = new CStdioFile(m_csLocalFile,
					  CFile::modeWrite | CFile::typeText | CFile::modeCreate);
					//Tell server it's an ASCII transfer
					SendCommand("TYPE A","");
				}
				else	//It's a binery transfer
				{	//Create a binary file
					m_cfUserFile = new CFile(m_csLocalFile, 
						CFile::modeWrite | CFile::typeBinary | CFile::modeCreate);
					//Tell server it's a binary transfer
					SendCommand("TYPE I","");
				}
				//If TYPE command is successful
				if(ReadResponse())
				{	//If "PORT" is successful
					if(Port())
					{	//Retrieve the file
						SendCommand("RETR ", m_csLocalFile);
						if(ReadResponse())	//Read the responses
							ReadResponse();
					}
				}
			}
			else	//We can't retrieve directories
				PrintToStatus("Can't retrieve directory \"%s\""\
				"- Directory transfers not allowed",
					m_csLocalFile);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedButtonMainLocsend() 
//
// Sample Call: OnClickedButtonMainLocsend();
//
// Description: Called when the user clicks the local "Send" button.
//	- Gets a pointer to the local list control.
//	- Detects if any items are selected - if, so, loops while detecting 
//		all selected items and putting their names into a string list.
//	- If there are any files left to send:
//		- Parses the string for the next file name.
//		- Gets a new list pointer, as the previous one is now no good.
//		- Finds the item in the list, based on its label text.
//		- Gets the item's info with GetItem();
//		- Starts a message pump to pump messages while waiting for a previous 
//			send to complete, if any.
//		- If the item is not a directory, continues, else, prints an error 
//			message to the screen and returns.
//		- Opens either a binary or ASCII  user file, based on the type 
//			specified, and sends the TYPE command.
//		- If successful, sends the STOR command.
//		- If successful:
//			- Gets the size of the file to be sent.
//			- Pumps messages if other data is currently being sent.
//			- Pumps messages while waiting for a data socket to be created.
//			- Reads a WRITE_BUF_LEN amount of data from the file.
//			- Writes the data with the data socket Write() command.
//			- Updates the progress control with the total amount sent.
//			- When all data is sent, reads the response from the server.
//		- If a directory was selected, print an error message to the screen.
//		- Refresh the remote list with OnClickedButtonMainRemrefresh(). 
//
// Input/Output data elements: None.
//
// Error handling:
//	- If a directory has been selected, prints an error message to the screen.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedButtonMainLocsend() 
{
	CWaitCursor cWait;
	CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
	char pData[WRITE_BUF_LEN];	//Write buffer binary
	memset(pData, 0, WRITE_BUF_LEN + 1);	//Clear the buffer
	*pData = '\0';
	MSG msg;
	CString csLSList;
	UINT amount = 0;
	int nLSIndex = 0,
		nLS = 0,
		nLength = 0;
	LV_FINDINFO fiSend;
	LV_ITEM lvSend;
	lvSend.mask = LVIF_IMAGE | LVIF_PARAM;
	lvSend.iSubItem = 0;
	m_csListOrFile = "Sending File";	//Specify sending a file
	m_bWriteDone = FALSE;	//Flag to tell us when the write's done
	m_bPieceDone = TRUE;	//Flag to tell when this data has been written

	//Any items selected?
	if((nLSIndex = pList->GetNextItem(-1, LVNI_SELECTED)) != -1)
	{	//If yes, find all selected items
		while((nLSIndex = pList->GetNextItem((nLSIndex - 1),
			LVNI_SELECTED)) != -1)
		{	//Get the selected items and put them into a string
			csLSList += pList->GetItemText(nLSIndex, 0) + "\r\n";
			nLSIndex++;
		}
		//While there is data left
		while((csLSList != "\r\n") && 
			(nLS = csLSList.Find("\r\n")) != -1)
		{	//Get the next file name
			m_csLocalFile = csLSList.Left(nLS);
			//Strip the curent name from the list
			csLSList = csLSList.Right(csLSList.GetLength() - nLS - 2);
			//Get a pointer to the list
			CListCtrl* pList = (CListCtrl*)GetDlgItem(IDC_LIST_LOCAL);
			fiSend.flags = LVFI_STRING;
			fiSend.psz = m_csLocalFile;
			//Find this item in the list
			lvSend.iItem = pList->FindItem(&fiSend, -1);
			pList->GetItem(&lvSend);	//Get the item's info
			ITEMINFO* pItem = (ITEMINFO*) lvSend.lParam;
			//Get the item's file size
			m_nFileSize = pItem->nFileSizeLow;
			m_nBytesReceived = 0;
			m_nTotalSent = 0;

			//Sit and spin while previous file is retrieved
			while(m_cfUserFile)	//While a file handle is open
			{	//Pump messages while waiting
				while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 
				{ 
					if(!AfxGetApp()->PumpMessage()) 
					{	//If user wants to exit, break 
						::PostQuitMessage(0); 
						 break; 
					} 
				} 
			}

			//Don't send directories
			if(lvSend.iImage != 3)
			{	//Open the specified file
				m_cfUserFile = new CFile(m_csLocalFile, CFile::modeRead | 
					CFile::shareDenyWrite | CFile::typeBinary);
				if(m_bBinary)
				{	//If a binary transfer
					if(Port())
						SendCommand("TYPE I","");
				}
				else
				{	//An ASCII transfer
					if(Port())
						SendCommand("TYPE A","");
				}
				//If TYPE command successful
				if(ReadResponse())
				{	//Send the STOR command
					SendCommand("STOR ", m_csLocalFile);
					if(ReadResponse())
					{	//If STOR is successful,
						//Set the amount sent to 0
						m_nTotalSent = 0;
						//nLength initialized as the file size
						nLength = m_cfUserFile->GetLength();
						//While there is still datat to send
						while(nLength > 0)
						{	//If other data is being sent
							while(!m_bPieceDone)
							{	//Pump messages while waiting
								while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 
								{ 
									if(!AfxGetApp()->PumpMessage()) 
									{ 
										::PostQuitMessage(0); 
										 break; 
									} 
								} 
							}
							//While no data socket is available
							while(!m_pDataSocket)
							{	//Pump messages while waiting
								while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) 
								{ 
									if(!AfxGetApp()->PumpMessage()) 
									{ 
										::PostQuitMessage(0); 
										 break; 
									} 
								} 
							}
							//Flag says we are sending data
							m_bPieceDone = FALSE;
							//Read WRITE_BUF_LEN amount of data from the file
							amount = m_cfUserFile->Read(pData, WRITE_BUF_LEN);
							//Terminate the data buffer with NULL
							pData[amount] = '\0';
							//Write the data with the data socket
							m_pDataSocket->Write(amount, pData);
							//Delete the amount sent from the file size
							nLength -= amount;
							//For the progress control
							m_nTotalSent += amount;
							//Update frame items by idle time processing
							UpdateControls();
						}
						//We are done with the write
						m_bWriteDone = TRUE;
					}
					ReadResponse();
				}
			}
			else
			{//Tried to send a directory
				PrintToStatus("Can't send directory \"%s\""\
				"- Directory transfers not allowed", 
					m_csLocalFile);
				return;
			}
		}
		//Refresh the Remote list
		OnClickedButtonMainRemrefresh();
	}
}

/////////////////////////////////////////////////////////////////////////////
// FILETIME CMainView::TimeConversion(CString csString)
//
// Sample Call: TimeConversion(csString);
//
// Description: Converts the time given by the server for remote files to 
// a time  useable by the program.
//	- Converts text month designations to numbers (i.e. "Jan" = 1).
//	- Sets seconds and milliseconds to 0 (not specified by server).
//	- If hours and minutes are separated by ":", get days, hours, and minutes:
//	- Convert system time to file time.
//	- Gets hours, minutes, and days from the string.
//	- Gets year from local time, since year not specified by the server.
//	- Checks the year against the local year to make sure the year is correct.
//	- If hours and minutes were not given, set to 0:
//	- Gets year and day from the string.
//	- Converts the system time to a file time.
//	- Converts the local file time to file time based on the Coordinated 
//		Universal Time (UTC).
//
// Input/Output data elements:
//	- Input: Argument CString csString.
//	- Output: Returns FILETIME structure.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
FILETIME CMainView::TimeConversion(CString csString)
{
	CString Temp;
	int nComp = 0;
	SYSTEMTIME SysTime, LocalTime;
	FILETIME  FileTime, ftLocalTime, ftUTC;

	//Convert months to numbers
	if(csString.Left(3) == "Jan")
		SysTime.wMonth = 1; else
	if(csString.Left(3) == "Feb")
		SysTime.wMonth = 2;else
	if(csString.Left(3) == "Mar")
		SysTime.wMonth = 3;else
	if(csString.Left(3) == "Apr")
		SysTime.wMonth = 4;else
	if(csString.Left(3) == "May")
		SysTime.wMonth = 5;else
	if(csString.Left(3) == "Jun")
		SysTime.wMonth = 6;else
	if(csString.Left(3) == "Jul")
		SysTime.wMonth = 7;else
	if(csString.Left(3) == "Aug")
		SysTime.wMonth = 8;else
	if(csString.Left(3) == "Sep")
		SysTime.wMonth = 9;else
	if(csString.Left(3) == "Oct")
		SysTime.wMonth = 10;else
	if(csString.Left(3) == "Nov")
		SysTime.wMonth = 11;else
	if(csString.Left(3) == "Dec")
		SysTime.wMonth = 12;
	
	//Seconds and milliseconds are 0
	SysTime.wSecond = 0;
	SysTime.wMilliseconds = 0;
	//If hours and minutes are separated by ":" 
	if(csString[9] == ':')
	{
		::GetLocalTime(&LocalTime);
		::SystemTimeToFileTime(&LocalTime, &ftLocalTime);
		//Convert system time to file time 
		Temp = csString.Left(9);
		//Get hours - last 2 digits of Temp
		SysTime.wHour = atoi(Temp.Right(2));
		//Get minutes - last 2 digits of csString
		SysTime.wMinute = atoi(csString.Right(2));
		Temp = csString.Left(6);
		//Get day - last 2 digits of Temp
		SysTime.wDay = atoi(Temp.Right(2));
		//Get year from local time, since year not specified
		//by the server
		SysTime.wYear = LocalTime.wYear;
		//Check to make sure year number is correct
		if(::SystemTimeToFileTime(&SysTime, &FileTime))
		{	//Compare the server's time to our time
			nComp = ::CompareFileTime (&FileTime,&ftLocalTime);
			//Their time is less than ours
			if(nComp == -1)
				//Make their year our year
				SysTime.wYear = LocalTime.wYear;
			//Their time is equal to ours
			if(nComp == 0)
				//Make their year our year
				SysTime.wYear = LocalTime.wYear;
			//Their time is greater than ours
			if(nComp == 1)
				//Their year is ours - 1
				SysTime.wYear = LocalTime.wYear -1;
		}
	}
	else
	{	//Hours and minutes were not given, set to 0
		SysTime.wHour = 0;
		SysTime.wMinute = 0;
		//Year is specified, so get it
		SysTime.wYear = atoi(csString.Right(4));
		//Find the first space from the right end
        nComp = csString.ReverseFind(' ');
		Temp = csString.Left(nComp - 1);
		//Get the day
		SysTime.wDay =atoi(Temp.Right(2));
	}
	//Convert the system time to a filetime
	::SystemTimeToFileTime(&SysTime, &FileTime);
	//Convert the local file time to file time based on the 
	//Coordinated Universal Time (UTC). 
	::LocalFileTimeToFileTime(&FileTime, &ftUTC);
	return(ftUTC);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnTimer(UINT nIDEvent) 
//
// Sample Call: Not called directly by user.  Called upon timeout of a Set
// Timer() call.
//
// Description: Called when a timer goes off.
//	- If nIDEvent = 1, kills Timer 1 and calls OnClickedButtonMainConnect().
//		This is the connect dialog that is displayed when the 
//		appllication starts.
//	- If nIDEvent = 2, if m_bReadReady is FALSE, kills Timer 2 and sets 
//		m_bReadReady to TRUE.
//
// Input/Output data elements:
//	- Input argument UINT nIDEvent.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnTimer(UINT nIDEvent) 
{	//nIDEvent is the timer number specified by SetTimer()
	//Timer 1 is set by CMainView::OnInitialUpdate(), opens
	//the "Connect" screen when the application starts
	if(nIDEvent == 1)
	{	//Kill Timer 1 and call the function
		KillTimer(1);
		OnClickedButtonMainConnect();
	}
	//Timer 2 is set by CMainView::OnStream()when the socket is
	//done reading - keeps checking until m_breadReady is 
	//false, then sets it true
	if(nIDEvent == 2)
	{
		if(!m_bReadReady)
		{	//If m_bReadReady is FALSE, kill Timer 2 and set
			KillTimer(2);
			m_bReadReady = TRUE;
		}
	}
		CFormView::OnTimer(nIDEvent);
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::ReadResponse()
//
// Sample Call: ReadResponse();
//
// Description: Reads command responses coming in on the stream 
// (control) socket.
//	- Initializes variables, clears the string buffer.
//	- If the socket is not ready for a read, loops, waiting for 
//		m_bReadReady to be set to TRUE and pumping messages.
//	- Copies data into buffer from stream socket.
//	- Copies buffer to a CString.
//	- Parses individual lines and prints them to the screen.
//	- Clears the buffer and frees its memory.
//	- If the message is not an error message, return TRUE, else return FALSE.
//
// Input/Output data elements:
//	- Output: Returns a Boolean variable.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
BOOL CMainView::ReadResponse()
{
	CWaitCursor cWait;
	LPVOID pDataRead;		// pointer to data just read
	int nCrLfPos,
		ncsLineLen,
		nStrCount = 0,
		nLen = 0;
	char pszMessage[READ_BUF_LEN + 1];	// Buffer to receive data
	CString csLine,
			csTemp,
			csDoneWrite;
	MSG msg;
	//Clear string buffer
	memset(pszMessage, 0, READ_BUF_LEN + 1);
	//Pump messages while waiting
loop:	while(!m_bReadReady)
	{
	    while (::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) 
		{ 
			if ( !AfxGetApp()->PumpMessage( ) ) 
			{ 
				::PostQuitMessage(0); 
				 break; 
			} 
		} 
	}

	m_bReadReady = FALSE;
	//Copy data into buffer from stream socket
	pDataRead = m_pStream->Read(&nLen);
	//Copy data into char string
	wsprintf(pszMessage,"%s",pDataRead);
	//Append terminator to string
	pszMessage[nLen] = '\0';
	//Make message a CString
	csLine.Format(pszMessage);
	//Parse individual lines and show them
	while((nCrLfPos = csLine.Find("\r\n")) != -1)
	{	//While there is a CR/LF remaining
		ncsLineLen = csLine.GetLength();
		//Strip the CR/LF and put into csTemp
		csTemp = csLine.Left(nCrLfPos);
		//Take the current string out of csLine
		csLine = csLine.Right(ncsLineLen - nCrLfPos - 2);
		//Print the data to the string
		PrintToStatus(csTemp, NULL);
	}
	//Free memory and clear buffer
	*(char *)pDataRead = '\0';
	free(pDataRead);
	memset(pszMessage, 0, READ_BUF_LEN + 1);
	*pszMessage = '\0';

	if(csTemp[3] == '-')	//Multiline response
		goto loop;			//Read next line

	m_csResponse = csTemp;
	//If these are not error messages
	if((csTemp[0] == '1') || (csTemp[0] == '2') || 
		(csTemp.Left(3) == "331") || (csTemp.Left(3) == "350"))
		return TRUE;
	else
		return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::PostDirectory()
//
// Sample Call: PostDirectory();
//
// Description: Sends a PWD command and handles the response.
//	- Gets a pointer to the remote combobox.
//	- Sends the PWD command.
//	- Reads the response.
//	- Gets the directory name from the response.
//	- If the directory doesn't exist in the combobox, add it to the box 
//		and select it.  If the directory already exists, selects it.
//	- Calls Port().  If the call is successful:
//		- Receiving a list.  If ASCII is specified, send the TYPE A command.
//		- Send the LIST command, and read both responses.
//		- If both responses are read OK, returns TRUE, else returns FALSE.
//
// Input/Output data elements:
//	- Output: Returns a Boolean variable.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
BOOL CMainView::PostDirectory()
{	//Gat e pointer to the remote combobox
	CComboBox* pcbRem = (CComboBox*)GetDlgItem(IDC_COMBO_REMDIR);
	CString csTemp;

	SendCommand("PWD", "" );
	if(ReadResponse())	//If command was successful
	{
		//Copy the reply to csTemp
		csTemp = m_csResponse;
		//csTemp = everything to the left of the last " character
		csTemp = csTemp.Left(csTemp.ReverseFind('"'));
		//csTemp = everything to the right of the first " - 
		//we now have the directory name
		csTemp = csTemp.Right(csTemp.GetLength() - 
			csTemp.ReverseFind('"') - 1);

		//If directory exists in combo box, use it; else, add and select
		if(pcbRem->FindStringExact(-1, csTemp) == CB_ERR)
		{	//String doesn't exist - add it			
			pcbRem->AddString(csTemp);
			//And select it in the combobox
			pcbRem->SetCurSel(pcbRem->FindStringExact(-1, csTemp));
		}
		else
			pcbRem->SetCurSel(pcbRem->FindStringExact(-1, csTemp));
		//Not sending a file
		m_bSendFile = FALSE;
		//Clear the buffer
		memset(m_pszBuf, 0, 100);
		*m_pszBuf = '\0';
		//If PORT command was successful
		if(Port())
		{	//Receiving a list
			m_csListOrFile = "Receiving List";
			if(m_bBinary)
			{	//If ASCII is specified
				SendCommand("TYPE A", "");
				ReadResponse();
			}
			//Send LIST command
			SendCommand("LIST", "");
			if(ReadResponse())	//If no error responses
			{
				if(ReadResponse())
					return TRUE;
				else
					return FALSE;
			}
		}
	}
	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::EnableFunctions()
//
// Sample Call: EnableFunctions();
//
// Description: Enables various buttons.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::EnableFunctions()
{	//Enable buttons
	GetDlgItem(IDC_BUTTON_MAIN_REMRET)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_MAIN_REMREFRESH)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_MAIN_REMMKDIR)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_MAIN_REMDEL)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_MAIN_LOCSEND)->EnableWindow(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::DisableFunctions()
//
// Sample Call: DisableFunctions();
//
// Description: Disables various buttons.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::DisableFunctions()
{	//Disable buttons
	GetDlgItem(IDC_BUTTON_MAIN_REMRET)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_MAIN_REMREFRESH)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_MAIN_REMMKDIR)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_MAIN_REMDEL)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_MAIN_LOCSEND)->EnableWindow(FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// BOOL CMainView::Port()
//
// Sample Call: Port();
//
// Description: Creates a data socket and sends the PORT command.
//	- Creates a new CStreamSocket object.
//	- Creates a data socket with the object.
//	- Creates a SOCKADDR_IN object and initializes it.
//	- Gets the stream socket name.
//	- Gets the listening socket's port information.
//	- Gets the current local IP address.
//	- Formats a string for the PORT command with the IP address 
//		and port information.
//	- Writes the PORT command out on the control socket.
//	- Reads the server's response - if not an error message, returns TRUE, 
//		else, returns FALSE.
//
// Input/Output data elements:
//	- Output: Returns a Boolean variable.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
BOOL CMainView::Port()
{
	//Create a listening socket
	if(!m_pListenSocket)
	{ // initialize the stream socket object
		m_pListenSocket = new CStreamSocket(this, 
			WM_USER_LISTEN);
		//Create a data socket
		if (m_pListenSocket->CreateDataSocket() != 
			CWINSOCK_NOERROR)
		{	//If socket creation fails
			PrintToStatus("Listen socket creation failed", NULL);
			m_csStatus = "Listen socket creation failed";
			delete m_pListenSocket;
			m_pListenSocket = NULL;
			return FALSE;
		}
	}
	
		//Set the PORT arguments
		SOCKADDR_IN cont_address;	//Create SOCKADDR_IN object
		cont_address.sin_addr  = m_in;
		int ncont_addressLen = sizeof(cont_address);
		//Get the socket name and put in cont_address
		getsockname(m_pStream->m_s, (LPSOCKADDR)&cont_address,
			&ncont_addressLen);
		//port is the listening sockets port
		unsigned char *port = (unsigned char *)
			&(m_pListenSocket->m_sinaddrAssigned.sin_port);
		//Copy the info for status bar message
		memcpy(&m_sinaddrDataPort, 
			&m_pListenSocket->m_sinaddrAssigned, sizeof(SOCKADDR_IN));
		//host is the current IP address
		unsigned char *host = (unsigned char *)
			&(cont_address.sin_addr);
		//Format the port command with host and port info
	    wsprintf(m_pszBuf, "PORT %i,%i,%i,%i,%i,%i\r\n",
			host[0], host[1], host[2], host[3], port[0], port[1]);
		//Write the PORT command out on the stream socket
		m_pStream->Write(strlen(m_pszBuf)/*+ 1*/, m_pszBuf);
		//If PORT command successful
		if(ReadResponse())
			return TRUE;
		else	//else
			return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedRadioMainBinary() 
//
// Sample Call: OnClickedRadioMainBinary();
//
// Description: Called when the "Binary" button is clicked.
//	- Gets pointers to the "Binary" and "Ascii" buttons.
//	- Sets the "Binary" button.  Unsets the "Ascii" button.
//	- Sets the binary flag.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedRadioMainBinary() 
{
	//Get pointers to teh "Binary" and "Ascii" buttons
	CButton *pRadBin = (CButton *)GetDlgItem(IDC_RADIO_MAIN_BINARY);
	CButton *pRadAsc = (CButton *)GetDlgItem(IDC_RADIO_MAIN_Ascii);
	//Show "Binary" button as checked, "Ascii" button as unchecked
	pRadBin->SetCheck(TRUE);
	pRadAsc->SetCheck(FALSE);
	m_bBinary = TRUE;	//Set the flag
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedRadioMainAscii() 
//
// Sample Call: OnClickedRadioMainAscii();
//
// Description: Called when the "Ascii" button is clicked.
//	- Gets pointers to the "Binary" and "Ascii" buttons.
//	- Sets the "Ascii" button.  Unsets the "Binary" button.
//	- Sets the binary flag.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedRadioMainAscii() 
{
	//Get pointers to teh "Binary" and "Ascii" buttons
	CButton *pRadBin = (CButton *)GetDlgItem(IDC_RADIO_MAIN_BINARY);
	CButton *pRadAsc = (CButton *)GetDlgItem(IDC_RADIO_MAIN_Ascii);
	//Show "Ascii" button as checked, "Binary" button as unchecked
	pRadBin->SetCheck(FALSE);
	pRadAsc->SetCheck(TRUE);
	m_bBinary = FALSE;	//Set flag
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnClickedRadioMainAuto() 
//
// Sample Call: OnClickedRadioMainAuto();
//
// Description: Called when the "Auto" button is clicked.
//	- If m_bAuto is TRUE:
//		- Sets m_bAuto to FALSE.
//		- Gets a pointer to the button.
//		- Unchecks the button.
//	- If m_bAuto is FALSE, sets it to TRUE.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnClickedRadioMainAuto() 
{
	if(m_bAuto)
	{	//If m_bAuto is TRUE
		m_bAuto = FALSE;
		//Get a pointer to the button
		CButton *pRadAuto = (CButton *)GetDlgItem(IDC_RADIO_MAIN_AUTO);
		pRadAuto->SetCheck(FALSE);	//Uncheck the button
	}
	else
		m_bAuto = TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::KillStream()
//
// Sample Call: KillStream();
//
// Description: Destroys the stream socket and object.
//	- If m_pStream is positive, destroys the socket, and deletes and 
//		sets the object to NULL.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::KillStream()
{
	if (m_pStream)
	{
		m_pStream->DestroySocket();
		delete m_pStream;
		m_pStream = NULL;
	}

}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnButtonMainExit() 
//
// Sample Call: OnButtonMainExit();
//
// Description: Called when the "Exit" button is pressed - sends a 
// WM_CLOSE message to the main window.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnButtonMainExit() 
{
	AfxGetMainWnd()->SendMessage(WM_CLOSE);	
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnUpdateStatus(CCmdUI *pCmdUI) 
//
// Sample Call: Not called by the user.
//
// Description: Updates the status text in the first status bar pane.
//	- Enables the pane.
//	- Formats the status message string.
//	- Sets the text string in the status pane.
//
// Input/Output data elements:
//	- Input: Argument CCmdUI *pCmdUI.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnUpdateStatus(CCmdUI *pCmdUI)
{
    pCmdUI->Enable();	//Enable the pane
	CString strPage;
	//Format the string with the status message
	strPage.Format( "%s", m_csStatus); 
	pCmdUI->SetText(strPage);	//Set the pane text
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::OnUpdateUtility(CCmdUI *pCmdUI)
//
// Sample Call: Not called by the user.
//
// Description: Updates the status line in the third status bar pane.
//	- Enables the pane.
//	- Sets the text string in the status pane based on m_csBytes.
//
// Input/Output data elements:
//	- Input: Argument CCmdUI *pCmdUI.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::OnUpdateUtility(CCmdUI *pCmdUI)
{
    pCmdUI->Enable();	//Enable the pane
	pCmdUI->SetText(m_csBytes);	//Set the pane text
}

/////////////////////////////////////////////////////////////////////////////
// void CMainView::UpdateControls()
//
// Sample Call: UpdateControls();
//
// Description: Forces frame items to update, as sometimes they 
// won't in a blocking thread.
//	- Gets a pointer to the main frame window.
//	- Calls OnUpdateCmdUI() to update the status bar.
//
// Input/Output data elements: None.
//
// Error handling: None.
//
// Author:	Jim Dunne
//
// Date:	Sept 26, 1997
//
void CMainView::UpdateControls()
{
	CMainFrame *pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainWnd;
	pMainFrame->m_wndStatusBar.OnUpdateCmdUI(pMainFrame, TRUE);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
My name is Jim Dunne. I'm retired from the US Air Force.

In some of my spare time, I develop some ping/traceroute applications that I first wrote as a Master's in Computer Engineering thesis.

My resume is at http://www.dunnes.net/resume.

Comments and Discussions