www.pudn.com > ftpserversrc.rar > ConnectSocket.cpp


/********************************************************************/ 
/*																	*/ 
/*  CONNECTSOCKET.CPP												*/ 
/*																	*/ 
/*  Implementation of the Connect Socket.							*/ 
/*	This class is a part of the ConnectThread which handles			*/ 
/*  socket connections. Incomming data is processed in OnReceive	*/ 
/*																	*/ 
/*  Programmed by Pablo van der Meer								*/ 
/*	http://www.pablovandermeer.nl									*/ 
/*																	*/ 
/*  Last updated: 10 july 2002										*/ 
/*																	*/ 
/********************************************************************/ 
 
#include "stdafx.h" 
#include "FTPServerApp.h" 
#include "FTPServer.h" 
#include "ConnectSocket.h" 
#include "ConnectThread.h" 
#include "ApplicationDlg.h" 
#include "DataSocket.h" 
 
extern CFTPServer theServer; 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : CConnectSocket::CConnectSocket					*/ 
/* Description   : Constructor										*/ 
/*																	*/ 
/********************************************************************/ 
CConnectSocket::CConnectSocket() 
{ 
	m_bLoggedon = FALSE; 
	m_bRenameFile = FALSE; 
 
	// reset transfer status 
	m_TransferStatus.m_pDataSocket = NULL; 
	m_TransferStatus.m_strRemoteHost = ""; 
	m_TransferStatus.m_nRemotePort = -1; 
	m_TransferStatus.m_bPassiveMode = FALSE; 
	m_TransferStatus.m_nRest = -1; 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : CConnectSocket::~CConnectSocket					*/ 
/* Description   : Destructor										*/ 
/*																	*/ 
/********************************************************************/ 
CConnectSocket::~CConnectSocket() 
{ 
	DestroyDataSocket(); 
 
	// tell our thread we have been closed 
	AfxGetThread()->PostThreadMessage(WM_QUIT,0,0); 
 
	TRACE0("CConnectSocket destroyed.\n"); 
} 
 
 
// Do not edit the following lines, which are needed by ClassWizard. 
#if 0 
BEGIN_MESSAGE_MAP(CConnectSocket, CSocket) 
	//{{AFX_MSG_MAP(CConnectSocket) 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
#endif	// 0 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : OnClose											*/		 
/* Description   : Send WM_QUIT message to the thread containing	*/ 
/*				   the socket to shutdown once the connection is	*/ 
/*                 closed.											*/ 
/*																	*/ 
/********************************************************************/ 
void CConnectSocket::OnClose(int nErrorCode)  
{ 
	Close(); 
	// destroy connection 
	m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0); 
	TRACE("CConnectSocket() OnClose()\n"); 
 
	CSocket::OnClose(nErrorCode); 
} 
 
 
#define BUFFERSIZE 4096 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : OnReceive										*/		 
/* Description   : Called by the framework to notify this socket	*/ 
/*                 that there is data in the buffer.				*/ 
/*																	*/ 
/********************************************************************/ 
void CConnectSocket::OnReceive(int nErrorCode)  
{ 
	TCHAR buff[BUFFERSIZE]; 
 
	int nRead = Receive(buff, BUFFERSIZE); 
	switch (nRead) 
	{ 
		case 0: 
			Close(); 
			break; 
 
		case SOCKET_ERROR: 
			if (GetLastError() != WSAEWOULDBLOCK)  
			{ 
				TCHAR szError[256]; 
				wsprintf(szError, "OnReceive error: %d", GetLastError()); 
				 
				AfxMessageBox (szError); 
			} 
			break; 
 
		default: 
			if (nRead != SOCKET_ERROR && nRead != 0) 
			{ 
				((CConnectThread *)AfxGetThread())->IncReceivedBytes(nRead); 
 
				// terminate the string 
				buff[nRead] = 0;  
				m_RxBuffer += CString(buff); 
				 
				GetRxLine(); 
			}	 
			break; 
	} 
	CSocket::OnReceive(nErrorCode); 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name: GetRxCommand										*/		 
/* Description  : Get command from receiver buffer.					*/ 
/*																	*/ 
/********************************************************************/ 
BOOL CConnectSocket::GetRxCommand(CString &strCommand, CString &strArguments) 
{ 
	if (!m_strCommands.IsEmpty()) 
	{ 
		CString strBuff = m_strCommands.RemoveHead(); 
		int nIndex = strBuff.Find(" "); 
		if (nIndex != -1) 
		{ 
			CString strPassword = strBuff; 
			strPassword.MakeUpper(); 
			// make password invisible 
			if (strPassword.Left(5) == "PASS ") 
			{ 
				strPassword = strBuff.Left(5); 
				for (int i=0; i 0); 
	 
	if(bOK) 
	{ 
		bOK = FD_ISSET(m_hSocket, &readSocketSet); 
	} 
	 
	if(bOK) 
	{ 
		CHAR szBuffer[1] = ""; 
		iRet = ::recv(m_hSocket, szBuffer, 1, MSG_PEEK); 
		bOK = (iRet > 0); 
		if(!bOK) 
		{ 
			INT iError = ::WSAGetLastError(); 
			bConnDropped = (( iError == WSAENETRESET) || 
				(iError == WSAECONNABORTED) || 
				(iError == WSAECONNRESET) || 
				(iError == WSAEINVAL) || 
				(iRet == 0)); 
		} 
	} 
    return(bConnDropped); 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name: SendResponse										*/		 
/* Description  : Send response to client.							*/ 
/*																	*/ 
/********************************************************************/ 
BOOL CConnectSocket::SendResponse(CString strData) 
{ 
	// is connection still active ? 
	if (HasConnectionDropped()) 
	{ 
		FireStatusMessage("could not send reply, disconnected.", 2);	 
 
		Close(); 
		// tell our thread we have been closed 
		 
		// destroy connection 
		m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0); 
		return FALSE; 
	} 
 
	int nBytes = CSocket::Send(strData + "\r\n", strData.GetLength()+2); 
	if (nBytes == SOCKET_ERROR) 
	{ 
		Close(); 
		FireStatusMessage("could not send reply, disconnected.", 2);	 
 
		// tell our thread we have been closed 
		m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0); 
 
		return FALSE; 
	} 
 
	FireStatusMessage(strData, 2); 
 
	((CConnectThread *)AfxGetThread())->IncSentBytes(nBytes); 
	return TRUE; 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name: ParseCommand										*/		 
/* Description  : Parse and execute command from client.			*/ 
/*																	*/ 
/********************************************************************/ 
void CConnectSocket::ParseCommand() 
{ 
	static CFTPCommand commandList[] =  
	{ 
		{TOK_USER,	"USER", TRUE}, 
		{TOK_PASS,	"PASS", TRUE}, 
		{TOK_CWD,	"CWD",	TRUE}, 
		{TOK_PWD,	"PWD",	FALSE}, 
		{TOK_PORT,	"PORT", TRUE}, 
		{TOK_PASV,	"PASV", FALSE}, 
		{TOK_TYPE,	"TYPE", TRUE}, 
		{TOK_LIST,	"LIST", FALSE}, 
		{TOK_REST,	"REST", TRUE}, 
		{TOK_CDUP,	"CDUP", FALSE}, 
		{TOK_RETR,	"RETR", TRUE}, 
		{TOK_STOR,	"STOR", TRUE}, 
		{TOK_SIZE,	"SIZE", TRUE}, 
		{TOK_DELE,	"DELE", TRUE}, 
		{TOK_RMD,	"RMD",	TRUE}, 
		{TOK_MKD,	"MKD",	TRUE}, 
		{TOK_RNFR,	"RNFR", TRUE}, 
		{TOK_RNTO,	"RNTO", TRUE}, 
		{TOK_ABOR,	"ABOR", FALSE},  
		{TOK_SYST,	"SYST", FALSE}, 
		{TOK_NOOP,	"NOOP", FALSE}, 
		{TOK_BYE,	"BYE",  FALSE}, 
		{TOK_QUIT,	"QUIT",  FALSE}, 
		{TOK_ERROR,	"",		FALSE}, 
	}; 
	 
	// parse command 
	CString strCommand, strArguments; 
	if (!GetRxCommand(strCommand, strArguments)) 
	{ 
		return; 
	} 
 
	int nCommand; 
 
	// find command in command list 
	for (nCommand = TOK_USER; nCommand < TOK_ERROR; nCommand++) 
	{ 
		// found command ? 
		if (strCommand == commandList[nCommand].m_pszName) 
		{ 
			// did we expect an argument ? 
			if (commandList[nCommand].m_bHasArguments && (strArguments == "")) 
			{ 
				SendResponse("501 Syntax error"); 
				return; 
			} 
			break;			 
		} 
	} 
 
	if (nCommand == TOK_ERROR) 
	{ 
		// command is not in our list 
		SendResponse("500 Syntax error, command unrecognized."); 
		return; 
	} 
	 
	// no commands are excepted before successfull logged on 
	if (nCommand > TOK_PASS && !m_bLoggedon) 
	{ 
		SendResponse("530 Please log in with USER and PASS first."); 
		return; 
	} 
 
	// proces command 
	switch(nCommand) 
	{ 
		// specify username 
		case TOK_USER: 
		{ 
			strArguments.MakeLower(); 
			m_bLoggedon = FALSE; 
			m_strUserName = strArguments; 
 
			CString strPeerAddress; 
			UINT nPeerPort; 
			GetPeerName(strPeerAddress, nPeerPort); 
 
			// tell FTP server a new user has connected 
			CConnectThread *pThread = (CConnectThread *)m_pThread; 
			((CFTPServer *)pThread->m_pWndServer)->m_pEventSink->OnFTPUserConnected(m_pThread->m_nThreadID, m_strUserName, strPeerAddress); 
 
			SendResponse("331 Password required for " + strArguments); 
		} 
		break; 
 
		// specify password 
		case TOK_PASS: 
		{ 
			// already logged on ? 
			if (m_bLoggedon) 
			{ 
				SendResponse("503 Bad sequence of commands."); 
			} 
			else 
			{ 
				// check user and password 
				CUser user; 
				if (theServer.m_UserManager.CheckUser(m_strUserName, strArguments, user)) 
				{ 
					// set home directory of user 
					m_strCurrentDir = "/"; 
 
					// succesfully logged on 
					m_bLoggedon = TRUE; 
					SendResponse("230 Logged on"); 
				} 
				else  
					SendResponse("530 Login or password incorrect!"); 
			} 
		} 
		break; 
		 
		// change current directory 
		case TOK_CWD: 
		{ 
			int nResult = theServer.m_UserManager.ChangeDirectory(m_strUserName, m_strCurrentDir, strArguments); 
			 
			CString str; 
			switch(nResult) 
			{ 
				case 0: 
					str.Format("250 CWD successful. \"%s\" is current directory.", m_strCurrentDir); 
					SendResponse(str); 
					break; 
				case 1: 
					str.Format("550 CWD failed. \"%s\": Permission denied.", strArguments); 
					SendResponse(str); 
					break; 
				default: 
					str.Format("550 CWD failed. \"%s\": directory not found.", strArguments); 
					SendResponse(str); 
					break; 
			} 
		} 
		break;  
 
		// print current directory 
		case TOK_PWD: 
		{ 
			CString str; 
			str.Format("257 \"%s\" is current directory.", m_strCurrentDir); 
			SendResponse(str);	 
		} 
		break; 
 
		// specify IP and port (PORT a1,a2,a3,a4,p1,p2) -> IP address a1.a2.a3.a4, port p1*256+p2.  
		case TOK_PORT: 
		{ 
			CString strSub; 
			int nCount=0; 
 
			while (AfxExtractSubString(strSub, strArguments, nCount++, ',')) 
			{ 
				switch(nCount) 
				{ 
					case 1:	// a1 
						m_TransferStatus.m_strRemoteHost = strSub; 
						m_TransferStatus.m_strRemoteHost += "."; 
						break; 
					case 2:	// a2 
						m_TransferStatus.m_strRemoteHost += strSub; 
						m_TransferStatus.m_strRemoteHost += "."; 
						break; 
					case 3:	// a3 
						m_TransferStatus.m_strRemoteHost += strSub; 
						m_TransferStatus.m_strRemoteHost += "."; 
						break; 
					case 4:	// a4 
						m_TransferStatus.m_strRemoteHost += strSub; 
						break; 
					case 5:	// p1 
						m_TransferStatus.m_nRemotePort = 256*atoi(strSub); 
						break; 
					case 6:	// p2 
						m_TransferStatus.m_nRemotePort += atoi(strSub); 
						break; 
				} 
			} 
			m_TransferStatus.m_bPassiveMode = FALSE; 
			SendResponse("200 Port command successful"); 
			break; 
		} 
		 
		// switch to passive mode 
		case TOK_PASV: 
		{ 
			// delete existing datasocket 
			DestroyDataSocket(); 
 
			// create new data socket 
			m_TransferStatus.m_pDataSocket = new CDataSocket(this, -1); 
 
			if (!m_TransferStatus.m_pDataSocket->Create()) 
			{ 
				DestroyDataSocket();				 
				SendResponse("421 Can't create socket"); 
				break; 
			} 
			// start listening 
			m_TransferStatus.m_pDataSocket->Listen(); 
			m_TransferStatus.m_pDataSocket->AsyncSelect(); 
			 
			CString strIP, strTmp; 
			UINT nPort; 
			 
			// get our ip address 
			GetSockName(strIP, nPort); 
			// Now retrieve the port 
			m_TransferStatus.m_pDataSocket->GetSockName(strTmp, nPort); 
			// Reformat the ip 
			strIP.Replace(".",","); 
			// tell the client which address/port to connect to 
			CString str; 
			str.Format("227 Entering Passive Mode (%s,%d,%d)", strIP, nPort/256, nPort%256); 
			SendResponse(str); 
			m_TransferStatus.m_bPassiveMode = TRUE; 
			break; 
		}  
 
		case TOK_TYPE: 
		{ 
			SendResponse("200 Type set to " + strArguments); 
		} 
		break; 
 
		// list current directory 
		case TOK_LIST: 
		{ 
			if(!m_TransferStatus.m_bPassiveMode && (m_TransferStatus.m_strRemoteHost == "" || m_TransferStatus.m_nRemotePort == -1)) 
			{ 
				SendResponse("503 Bad sequence of commands."); 
			} 
			else 
			{ 
				// if client did not specify a directory use current dir 
				if (strArguments == "") 
				{ 
					strArguments = m_strCurrentDir; 
				} 
				else 
				{ 
					// check if argument is file or directory 
					CString strResult; 
					int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_LIST, strResult); 
					if (nResult == 0) 
					{ 
						strArguments = strResult; 
					} 
				} 
				CString strListing; 
				int nResult = theServer.m_UserManager.GetDirectoryList(m_strUserName, strArguments, strListing); 
				switch(nResult) 
				{ 
					case 1: 
						SendResponse("550 Permission denied"); 
						break; 
					case 2: 
						SendResponse("550 Directory not found"); 
						break; 
					default: 
						if (!m_TransferStatus.m_bPassiveMode) 
						{ 
							CDataSocket *pDataSocket = new CDataSocket(this, 0); 
							pDataSocket->Create(); 
							pDataSocket->SetData(strListing); 
							pDataSocket->AsyncSelect(); 
							m_TransferStatus.m_pDataSocket = pDataSocket; 
							if (!pDataSocket->Connect(m_TransferStatus.m_strRemoteHost, m_TransferStatus.m_nRemotePort)) 
							{ 
								if (GetLastError() != WSAEWOULDBLOCK) 
								{ 
									SendResponse("425 Can't open data connection"); 
									break; 
								} 
							} 
							SendResponse("150 Opening data channel for directory list.");  
						} 
						else 
						{ 
							m_TransferStatus.m_pDataSocket->SetData(strListing); 
							m_TransferStatus.m_pDataSocket->SetTransferType(0); 
						} 
						break; 
				} 
			}  
			break; 
		}  
		// change to parent directory 
		case TOK_CDUP: 
		{ 
			CString strDirectory = ".."; 
			CString str; 
			int nResult = theServer.m_UserManager.ChangeDirectory(m_strUserName, m_strCurrentDir, strDirectory); 
			switch(nResult) 
			{ 
				case 0: 
					str.Format("250 CWD successful. \"%s\" is current directory.", m_strCurrentDir); 
					SendResponse(str); 
					break; 
				case 1: 
					str.Format("550 CWD failed. \"%s\": Permission denied.", strDirectory); 
					SendResponse(str); 
					break; 
				case 2: 
					str.Format("550 CWD failed. \"%s\": directory not found.", strDirectory); 
					SendResponse(str); 
					break; 
			} 
		} 
		break;  
 
		// retrieve file 
		case TOK_RETR: 
		{ 
			if(!m_TransferStatus.m_bPassiveMode && (m_TransferStatus.m_strRemoteHost == "" || m_TransferStatus.m_nRemotePort == -1)) 
			{ 
				SendResponse("503 Bad sequence of commands."); 
				break; 
			} 
			 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_DOWNLOAD, strResult); 
			switch(nResult) 
			{ 
				case 1: 
					SendResponse("550 Permission denied"); 
					break; 
				case 2: 
					SendResponse("550 File not found"); 
					break; 
				default: 
					if (!m_TransferStatus.m_bPassiveMode) 
					{ 
						CDataSocket *pDataSocket = new CDataSocket(this, 1); 
						m_TransferStatus.m_pDataSocket = pDataSocket; 
						pDataSocket->Create(); 
						pDataSocket->AsyncSelect(); 
						pDataSocket->SetData(strResult); 
						if (pDataSocket->Connect(m_TransferStatus.m_strRemoteHost ,m_TransferStatus.m_nRemotePort) == 0) 
						{ 
							if (GetLastError() != WSAEWOULDBLOCK) 
							{ 
								SendResponse("425 Can't open data connection"); 
								break; 
							} 
						} 
						SendResponse("150 Opening data channel for file transfer."); 
					} 
					else 
					{ 
						m_TransferStatus.m_pDataSocket->SetData(strResult); 
						m_TransferStatus.m_pDataSocket->SetTransferType(1); 
					} 
					break; 
			} 
			break;	 
		} 
 
		// client wants to upload file 
		case TOK_STOR: 
		{ 
			if(m_TransferStatus.m_bPassiveMode == -1) 
			{ 
				SendResponse("503 Bad sequence of commands."); 
				break; 
			} 
			if(!m_TransferStatus.m_bPassiveMode && (m_TransferStatus.m_strRemoteHost == "" || m_TransferStatus.m_nRemotePort == -1)) 
			{ 
				SendResponse("503 Bad sequence of commands."); 
				break; 
			} 
			 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_UPLOAD, strResult); 
			switch(nResult) 
			{ 
				case 1: 
					SendResponse("550 Permission denied"); 
					break; 
				case 2: 
					SendResponse("550 Filename invalid"); 
					break; 
				default: 
					if (!m_TransferStatus.m_bPassiveMode) 
					{ 
						CDataSocket *pDataSocket = new CDataSocket(this, 2); 
						m_TransferStatus.m_pDataSocket = pDataSocket; 
						pDataSocket->Create(); 
						pDataSocket->AsyncSelect(); 
						pDataSocket->SetData(strResult); 
 
						if (pDataSocket->Connect(m_TransferStatus.m_strRemoteHost, m_TransferStatus.m_nRemotePort) == 0) 
						{ 
							if (GetLastError() != WSAEWOULDBLOCK) 
							{ 
								SendResponse("425 Can't open data connection"); 
								break; 
							} 
						} 
						SendResponse("150 Opening data channel for file transfer."); 
					} 
					else 
					{ 
						m_TransferStatus.m_pDataSocket->SetData(strResult); 
						m_TransferStatus.m_pDataSocket->SetTransferType(2); 
					} 
					break; 
			} 
		} 
		break; 
		 
		// get file size 
		case TOK_SIZE: 
		{ 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_DOWNLOAD, strResult); 
			switch(nResult) 
			{ 
				case 1: 
					SendResponse("550 Permission denied"); 
					break; 
				case 2: 
					SendResponse("550 File not found"); 
					break; 
				default: 
				{ 
					CFileStatus status; 
					CFile::GetStatus(strResult, status); 
					CString strResponse; 
					strResponse.Format("213 %d", status.m_size); 
					SendResponse(strResponse); 
					break; 
				} 
			} 
		} 
		break; 
		 
		// delete file 
		case TOK_DELE: 
		{ 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_DELETE, strResult); 
			switch(nResult) 
			{ 
				case 1: 
					SendResponse("550 Permission denied"); 
					break; 
				case 2: 
					SendResponse("550 File not found"); 
					break; 
				default: 
					try 
					{ 
						CFile::Remove(strResult); 
					} 
					catch(CFileException *e) 
					{ 
						e->Delete(); 
						SendResponse("450 Internal error deleting the file."); 
						break; 
					} 
					SendResponse("250 File deleted successfully");			 
					break; 
			} 
		} 
		break; 
		 
		// remove directory 
		case TOK_RMD:  
		{ 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetDirectory(m_strUserName, strArguments, m_strCurrentDir, FTP_DELETE, strResult); 
			switch(nResult) 
			{ 
				case 1: 
					SendResponse("550 Permission denied"); 
					break; 
				case 2: 
					SendResponse("550 Directory not found"); 
					break; 
				default: 
					if (!RemoveDirectory(strResult)) 
					{ 
						if (GetLastError() == ERROR_DIR_NOT_EMPTY) 
							SendResponse("550 Directory not empty."); 
						else 
							SendResponse("450 Internal error deleting the directory."); 
					} 
					else 
					{ 
						SendResponse("250 Directory deleted successfully"); 
					} 
					break; 
			} 
		} 
		break;	 
		 
		// create directory 
		case TOK_MKD:  
		{ 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetDirectory(m_strUserName, strArguments, m_strCurrentDir, FTP_CREATE_DIR, strResult); 
			switch(nResult) 
			{ 
				case 0: 
					SendResponse("550 Directory already exists"); 
					break; 
				case 1: 
					SendResponse("550 Can't create directory. Permission denied"); 
					break; 
				case 3: 
					SendResponse("550 Directoryname not valid"); 
					break; 
				default: 
					strResult += "\\"; 
					CString strDir; 
					BOOL bResult; 
					// create directory structure one part at a time 
					while (strResult != "") 
					{ 
						strDir += strResult.Left(strResult.Find("\\")+1); 
						strResult = strResult.Mid(strResult.Find("\\")+1); 
						bResult = CreateDirectory(strDir, 0); 
					} 
					if (!bResult) 
					{ 
						SendResponse("450 Internal error creating the directory."); 
					} 
					else 
						SendResponse("250 Directory created successfully"); 
					break; 
			} 
		} 
		break; 
		 
		// rename file or directory (part 1) 
		case TOK_RNFR: 
		{ 
			CString strResult; 
			int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_RENAME, strResult); 
			if (nResult == 0) 
			{ 
				m_strRenameFile = strResult; 
				m_bRenameFile = TRUE; 
				SendResponse("350 File exists, ready for destination name."); 
				break; 
			} 
			else 
			{ 
				// client wants to rename directory 
				nResult = theServer.m_UserManager.GetDirectory(m_strUserName, strArguments, m_strCurrentDir, FTP_RENAME, strResult); 
				switch(nResult) 
				{ 
					case 0: 
						m_strRenameFile = strResult; 
						m_bRenameFile = FALSE; 
						SendResponse("350 Directory exists, ready for destination name."); 
						break; 
					case 1: 
						SendResponse("550 Permission denied"); 
						break; 
					default:  
						SendResponse("550 file/directory not found"); 
						break; 
				} 
			}	 
		} 
		break; 
 
		// rename file or directory (part 2) 
		case TOK_RNTO: 
		{ 
			if (m_strRenameFile.IsEmpty()) 
			{ 
				SendResponse("503 Bad sequence of commands!"); 
				break; 
			} 
			if (m_bRenameFile) 
			{ 
				CString strResult; 
				// check destination filename 
				int nResult = theServer.m_UserManager.GetFileName(m_strUserName, strArguments, m_strCurrentDir, FTP_RENAME, strResult); 
				switch(nResult) 
				{ 
					case 0: 
						SendResponse("550 file exists"); 
						break; 
					case 1: 
						SendResponse("550 Permission denied"); 
						break; 
					default: 
						if (!MoveFile(m_strRenameFile, strResult)) 
							SendResponse("450 Internal error renaming the file"); 
						else 
							SendResponse("250 file renamed successfully"); 
						break; 
				} 
			} 
			else 
			{ 
				CString strResult; 
				// check destination directory name 
				int nResult = theServer.m_UserManager.GetDirectory(m_strUserName, strArguments, m_strCurrentDir, FTP_RENAME, strResult); 
				switch(nResult) 
				{ 
					case 0: 
						SendResponse("550 directory exists"); 
						break; 
					case 1: 
						SendResponse("550 Permission denied"); 
						break; 
					case 3: 
						SendResponse("550 Filename invalid"); 
						break; 
					default: 
						if (!MoveFile(m_strRenameFile, strResult)) 
							SendResponse("450 Internal error renaming the file"); 
						else 
							SendResponse("250 file renamed successfully"); 
						break; 
				} 
			}		 
		} 
		break; 
 
		// abort transfer 
		case TOK_ABOR: 
		{ 
			if (m_TransferStatus.m_pDataSocket) 
			{ 
				if (m_TransferStatus.m_pDataSocket->GetStatus() != XFERMODE_IDLE) 
					SendResponse("426 Connection closed; transfer aborted."); 
 
				// destroy data socket 
				m_pThread->PostThreadMessage(WM_THREADMSG, 0, 0); 
//				DestroyDataSocket(); 
			} 
			SendResponse("226 ABOR command successful"); 
			break; 
		}  
 
		// get system info 
		case TOK_SYST: 
			SendResponse("215 UNIX emulated by Pablo's FTP Server"); 
			break; 
		 
		// close connection 
		case TOK_BYE: 
		case TOK_QUIT: 
		{ 
			// send goodbye message to client 
			CConnectThread *pThread = (CConnectThread *)m_pThread; 
			CString strText = ((CFTPServer *)pThread->m_pWndServer)->GetGoodbyeMessage(); 
			SendResponse("220 " + strText); 
 
			Close(); 
			 
			// tell our thread we have been closed 
		 
			// destroy connection 
			m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0); 
			break; 
		} 
		// dummy instruction 
		case TOK_NOOP: 
			SendResponse("200 OK"); 
			break; 
 
		default: 
			SendResponse("502 Command not implemented."); 
			break; 
	} 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name: FireStatusMessage									*/		 
/* Description  : Fire status message.								*/ 
/*																	*/ 
/********************************************************************/ 
void CConnectSocket::FireStatusMessage(LPCTSTR lpszStatus, int nType) 
{ 
	CConnectThread *pThread = (CConnectThread *)m_pThread; 
	((CFTPServer *)pThread->m_pWndServer)->AddTraceLine(nType, "[%d] %s", m_pThread->m_nThreadID, lpszStatus); 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name: DestroyDataSocket									*/		 
/* Description  : Close data transfer socket.						*/ 
/*																	*/ 
/********************************************************************/ 
void CConnectSocket::DestroyDataSocket() 
{ 
	if (!m_TransferStatus.m_pDataSocket) 
		return; 
 
//	m_TransferStatus.m_pDataSocket->Close(); 
	 
	delete m_TransferStatus.m_pDataSocket; 
 
	// reset transfer status 
	m_TransferStatus.m_pDataSocket = NULL; 
	m_TransferStatus.m_strRemoteHost = ""; 
	m_TransferStatus.m_nRemotePort = -1; 
	m_TransferStatus.m_bPassiveMode = FALSE; 
	m_TransferStatus.m_nRest = -1; 
} 
 
 
 
 
char *CConnectSocket::GetResponseMessage(int nResponse)  
{ 
	switch(nResponse)  
	{ 
		case 110 : return "Restart marker reply"; break; 
		case 120 : return "Service ready in nnn minutes."; break; 
		case 125 : return "Data connection already open; transfer starting."; break; 
		case 150 : return "File status okay; about to open data connection."; break; 
		case 200 : return "Command okay."; break; 
		case 202 : return "Command not implemented, superfluous at this site."; break; 
		case 211 : return "System status, or system help reply."; break; 
		case 212 : return "Directory status."; break; 
		case 213 : return "File status."; break; 
		case 214 : return "Help message."; break; 
		case 215 : return "NAME system type."; break; 
		case 220 : return "Service ready for new user."; break; 
		case 221 : return "Service closing control connection."; break; 
		case 225 : return "Data connection open; no transfer in progress."; break; 
		case 226 : return "Closing data connection successfully."; break; 
		case 227 : return "Entering Passive Mode (h1,h2,h3,h4,p1,p2)."; break; 
		case 230 : return "User logged in, proceed."; break; 
		case 250 : return "Requested file action okay, completed."; break; 
		case 257 : return "PATHNAME created."; break; 
		case 331 : return "User name okay, need password."; break; 
		case 332 : return "Need account for login."; break; 
		case 350 : return "Requested file action pending further information."; break; 
		case 421 : return "Service not available, closing control connection."; break; 
		case 425 : return "Can't open data connection."; break; 
		case 426 : return "Connection closed; transfer aborted."; break; 
		case 450 : return "Requested file action not taken - File unavailable."; break; 
		case 451 : return "Requested action aborted: local error in processing."; break; 
		case 452 : return "Requested action not taken - Insufficient storage space in system."; break; 
		case 500 : return "Syntax error, command unrecognized."; break; 
		case 501 : return "Syntax error in parameters or arguments."; break; 
		case 502 : return "Command not implemented."; break; 
		case 503 : return "Bad sequence of commands."; break; 
		case 504 : return "Command not implemented for that parameter."; break; 
		case 530 : return "Not logged in."; break; 
		case 532 : return "Need account for storing files."; break; 
		case 550 : return "Requested action not taken - File unavailable."; break; 
		case 551 : return "Requested action aborted: page type unknown."; break; 
		case 552 : return "Requested file action aborted - Exceeded storage allocation"; break; 
		case 553 : return "Requested action not taken - File name not allowed."; break; 
		default  : return "Unknown error"; 
	} 
} 
 
 
void CConnectSocket::Shutdown(int nReason) 
{ 
	DestroyDataSocket(); 
 
	switch(nReason) 
	{ 
		case 1: 
			SendResponse("421 Connection timed out."); 
			break; 
		case 2: 
			SendResponse("421 No-transfer-time exceeded. Closing control connection."); 
			break; 
		case 3: 
			SendResponse("421 Login time exceeded. Closing control connection."); 
			break; 
		case 4: 
			SendResponse("421 Kicked by Administrator"); 
			break; 
		case 5: 
			SendResponse("421 Problem with server"); 
			break; 
		default: 
			SendResponse("421 Server is going offline"); 
			break; 
	} 
	FireStatusMessage("disconnected.", 0);	 
	Close(); 
 
	// tell our thread we have been closed 
//	m_pThread->PostThreadMessage(WM_QUIT,0,0); 
}