www.pudn.com > BesidesFTPServer.rar > ControlSocket.cpp
/********************************************************************/
/* */
/* CONTROLSOCKET.CPP */
/* */
/* Implementation of the CControlSocket class. */
/* This class is a part of the CConnectThread which handles */
/* socket connections. Incomming data is processed in OnReceive */
/* */
/* Programmed by Pablo van der Meer */
/* Based partially on and inspired by FileZilla Server. */
/* */
/* http://www.pablovandermeer.nl */
/* */
/* Last updated: October 20, 2002 */
/* */
/********************************************************************/
#include "stdafx.h"
#include "FTPServerApp.h"
#include "FTPServer.h"
#include "ControlSocket.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 : CControlSocket::CControlSocket */
/* Description : Constructor */
/* */
/********************************************************************/
CControlSocket::CControlSocket()
{
m_nStatus = STATUS_LOGIN;
m_bRenameFile = FALSE;
m_pDataSocket = NULL;
m_strRemoteHost = "";
m_nRemotePort = -1;
m_dwRestartOffset = 0;
m_bPassiveMode = FALSE;
}
/********************************************************************/
/* */
/* Function name : CControlSocket::~CControlSocket */
/* Description : Destructor */
/* */
/********************************************************************/
CControlSocket::~CControlSocket()
{
DestroyDataConnection();
// tell our thread we have been closed
AfxGetThread()->PostThreadMessage(WM_QUIT,0,0);
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CControlSocket, CSocket)
//{{AFX_MSG_MAP(CControlSocket)
//}}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 CControlSocket::OnClose(int nErrorCode)
{
Close();
// destroy connection
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
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 CControlSocket::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 ´يخَ: %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);
GetCommandLine();
}
break;
}
CSocket::OnReceive(nErrorCode);
}
/********************************************************************/
/* */
/* Function name: GetCommandLine */
/* Description : Parse complete command line */
/* */
/********************************************************************/
void CControlSocket::GetCommandLine()
{
CString strTemp;
int nIndex;
while(!m_RxBuffer.IsEmpty())
{
nIndex = m_RxBuffer.Find("\r\n");
if (nIndex != -1)
{
strTemp = m_RxBuffer.Left(nIndex);
m_RxBuffer = m_RxBuffer.Mid(nIndex + 2);
if (!strTemp.IsEmpty())
{
m_strCommands.AddTail(strTemp);
// parse and execute command
ProcessCommand();
}
}
else
break;
}
}
/********************************************************************/
/* */
/* Function name: GetCommand */
/* Description : Get command from receiver buffer. */
/* */
/********************************************************************/
BOOL CControlSocket::GetCommand(CString &strCommand, CString &strArguments)
{
if (!m_strCommands.IsEmpty())
{
CString strBuff = m_strCommands.RemoveHead();
// log command line
PostStatusMessage(strBuff, 1);
int nIndex = strBuff.Find(" ");
if (nIndex != -1)
{
strCommand = strBuff.Left(nIndex);
strArguments = strBuff.Mid(nIndex+1);
}
else
{
strCommand = strBuff;
}
if (!strCommand.IsEmpty())
{
strCommand.MakeUpper();
// who screwed up ???
if (strCommand.Right(4) == "ABOR")
{
strCommand = "ABOR";
}
return TRUE;
}
}
return FALSE;
}
/********************************************************************/
/* */
/* Function name: HasConnectionDropped */
/* Description : Check if connection has been dropped. */
/* Used to detect if client has crashed. */
/* */
/********************************************************************/
BOOL CControlSocket::HasConnectionDropped(void)
{
BOOL bConnDropped = FALSE;
INT iRet = 0;
BOOL bOK = TRUE;
if (m_hSocket == INVALID_SOCKET)
return TRUE;
struct timeval timeout = { 0, 0 };
fd_set readSocketSet;
FD_ZERO(&readSocketSet);
FD_SET(m_hSocket, &readSocketSet);
iRet = ::select(0, &readSocketSet, NULL, NULL, &timeout);
bOK = (iRet > 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 CControlSocket::SendResponse(LPCTSTR pstrFormat, ...)
{
CString str;
// format arguments and put them in CString
va_list args;
va_start(args, pstrFormat);
str.FormatV(pstrFormat, args);
// is connection still active ?
if (HasConnectionDropped())
{
PostStatusMessage("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(str + "\r\n", str.GetLength()+2);
if (nBytes == SOCKET_ERROR)
{
Close();
PostStatusMessage("Could not send reply, disconnected.", 2);
// tell our thread we have been closed
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
return FALSE;
}
PostStatusMessage(str, 2);
((CConnectThread *)AfxGetThread())->IncSentBytes(nBytes);
return TRUE;
}
/********************************************************************/
/* */
/* Function name: ProcessCommand */
/* Description : Parse and execute command from client. */
/* */
/* Based on code provided by FileZilla Server. */
/* http://sourceforge.net/projects/filezilla */
/* */
/********************************************************************/
void CControlSocket::ProcessCommand()
{
static CFTPCommand commandList[] =
{
{TOK_ABOR, "ABOR", FALSE, "Abort transfer: ABOR"},
{TOK_APPE, "APPE", TRUE, "Append file to existing file: APPE file-name"},
{TOK_BYE, "BYE", FALSE, "Logout or break the connection: BYE"},
{TOK_CDUP, "CDUP", FALSE, "Change to parent directory: CDUP"},
{TOK_CWD, "CWD", TRUE, "Change working directory: CWD [directory-name]"},
{TOK_DELE, "DELE", TRUE , "Delete file: DELE file-name"},
{TOK_HELP, "HELP", FALSE, "Show help: HELP [command]"},
{TOK_LIST, "LIST", FALSE, "Get directory listing: LIST [path-name]"},
{TOK_MKD, "MKD", TRUE, "Make directory: MKD path-name"},
{TOK_NLST, "NLST", FALSE, "List filenames only: NLST [path-name]"},
{TOK_NOOP, "NOOP", FALSE, "Do nothing: NOOP"},
{TOK_PASS, "PASS", TRUE, "Supply a user password: PASS password"},
{TOK_PASV, "PASV", FALSE, "Set server in passive mode: PASV"},
{TOK_PASW, "P@SW", FALSE, "Set server in passive mode: P@SW"},
{TOK_PORT, "PORT", TRUE, "Specify the client port number: PORT a0,a1,a2,a3,a4,a5"},
{TOK_PWD, "PWD", FALSE, "Get current directory: PWD"},
{TOK_QUIT, "QUIT", FALSE, "Logout or break the connection: QUIT"},
{TOK_REST, "REST", TRUE, "Set restart transfer marker: REST marker"},
{TOK_RETR, "RETR", TRUE, "Get file: RETR file-name"},
{TOK_RMD, "RMD", TRUE, "Remove directory: RMD path-name"},
{TOK_RNFR, "RNFR", TRUE, "Specify old path name of file to be renamed: RNFR file-name"},
{TOK_RNTO, "RNTO", TRUE, "Specify new path name of file to be renamed: RNTO file-name"},
{TOK_SIZE, "SIZE", TRUE, "Get filesize: SIZE file-name"},
{TOK_STOR, "STOR", TRUE, "Store file: STOR file-name"},
{TOK_SYST, "SYST", FALSE, "Get operating system type: SYST"},
{TOK_TYPE, "TYPE", TRUE, "Set filetype: TYPE [A | I]"},
{TOK_USER, "USER", TRUE, "Supply a username: USER username"},
{TOK_XCWD, "XCWD", TRUE, "Change working directory: XCWD [directory-name]"},
{TOK_XMKD, "XMKD", TRUE, "Make directory: XMKD path-name"},
{TOK_XPWD, "XPWD", FALSE, "Get current directory: XPWD"},
{TOK_XRMD, "XRMD", TRUE, "Remove directory: XRMD path-name"},
{TOK_ERROR, "", FALSE, ""},
};
// parse command
CString strCommand, strArguments;
int nCommand;
if (!GetCommand(strCommand, strArguments))
{
return;
}
// find command in command list
for (nCommand = TOK_ABOR; nCommand < TOK_ERROR; nCommand++)
{
// found command ?
if (strCommand == commandList[nCommand].m_pszName)
{
// did we expect an argument ?
if (commandList[nCommand].m_bHasArguments && (strArguments.IsEmpty()))
{
SendResponse("501 Syntax error: Invalid number of parameters.");
return;
}
break;
}
}
// no commands are excepted before successful logged on
if ((nCommand != TOK_USER && nCommand != TOK_PASS) && (m_nStatus == STATUS_LOGIN))
{
SendResponse("530 Please login with USER and PASS.");
return;
}
// proces command
switch(nCommand)
{
// specify username
case TOK_USER:
{
m_nStatus = STATUS_LOGIN;
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 %s", m_strUserName);
}
break;
// specify password
case TOK_PASS:
{
// already logged on ?
if (m_strUserName.IsEmpty())
{
SendResponse("503 Login with USER first.");
}
else
{
// now we have user name and password, attempt to login the client
if (theServer.m_UserManager.GetUser(m_strUserName, m_User))
{
// check password
if ((!m_User.m_strPassword.Compare(strArguments) || m_User.m_strPassword.IsEmpty()) && !m_User.m_bAccountDisabled)
{
// set home directory of user
m_strCurrentDir = "/";
// succesfully logged on
m_nStatus = STATUS_IDLE;
SendResponse("230 User successfully logged in.");
break;
}
}
SendResponse("530 Not logged in, user or password incorrect!");
}
}
break;
// change transfer type
case TOK_TYPE:
{
// let's pretend we did something...
SendResponse("200 Type set to %s", strArguments);
}
break;
// print current directory
case TOK_PWD:
case TOK_XPWD:
{
SendResponse("257 \"%s\" is current directory.", m_strCurrentDir);
}
break;
// change to parent directory
case TOK_CDUP:
strArguments = "..";
// change working directory
case TOK_CWD:
case TOK_XCWD:
{
// try to change to specified directory
CString strLocalPath;
int nResult = CheckDirectory(strArguments, FTP_LIST, strLocalPath);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
// user has no permissions
SendResponse("550 \"%s\": Permission denied.", strArguments);
break;
case ERROR_PATH_NOT_FOUND:
// unable to convert to local path
SendResponse("550 \"%s\": Directory not found.", strArguments);
break;
default:
// everything is successful
CString strRelativePath;
if (GetRelativePath(strLocalPath, strRelativePath))
{
m_strCurrentDir = strRelativePath;
}
SendResponse("250 \"%s\" is current directory.", m_strCurrentDir);
break;
}
}
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:
{
CStringArray strArray;
// split argument into seperate parts
Split(strArguments, ',', strArray);
// determine remote hostname
m_strRemoteHost.Format("%s.%s.%s.%s", strArray[0], strArray[1], strArray[2], strArray[3]);
// determine remote port
m_nRemotePort = 256*atoi(strArray[4]) + atoi(strArray[5]);
// we're not in PASV mode
m_bPassiveMode = FALSE;
SendResponse("200 Port command successful.");
break;
}
// switch to passive mode
case TOK_PASV:
case TOK_PASW: // takes care of SMC Barricade-bug (thanks to: Dmitri Shvetsov)
{
// in PASV mode the server create a listing socket for the client to connect to.
// delete existing datasocket
DestroyDataConnection();
// create new data socket
m_pDataSocket = new CDataSocket(this);
if (!m_pDataSocket->Create())
{
DestroyDataConnection();
SendResponse("421 Failed to create socket.");
break;
}
// start listening
m_pDataSocket->Listen();
m_pDataSocket->AsyncSelect();
CString strIPAddress, strTmp;
UINT nPort;
// get our ip address
GetSockName(strIPAddress, nPort);
// get listen port
m_pDataSocket->GetSockName(strTmp, nPort);
// replace dots
strIPAddress.Replace(".", ",");
// tell the client which address/port to connect to
SendResponse("227 Entering Passive Mode (%s,%d,%d).", strIPAddress, nPort/256, nPort%256);
m_bPassiveMode = TRUE;
break;
}
// list current directory (or a specified file/directory)
case TOK_LIST:
// list filenames only
case TOK_NLST:
{
CString strListing;
if (!GetDirectoryList(strArguments, strListing, (nCommand == TOK_NLST)))
{
// something went wrong
return;
}
SendResponse("150 Opening ASCII mode data connection for directory list.");
// create data connection with client
if (CreateDataConnection())
{
if (strListing.IsEmpty())
{
// close data connection with client
DestroyDataConnection();
SendResponse("226 Transfer complete.");
m_nStatus = STATUS_IDLE;
return;
}
}
else
{
// close data connection with client
DestroyDataConnection();
return;
}
m_nStatus = STATUS_LIST;
m_pDataSocket->AsyncSelect();
// send the listing
m_pDataSocket->SendListing(strListing);
break;
}
// retrieve file
case TOK_RETR:
{
CString strResult;
int nResult = CheckFileName(strArguments, FTP_DOWNLOAD, strResult);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
case ERROR_FILE_NOT_FOUND:
SendResponse("550 File not found.");
break;
default:
SendResponse("150 Opening BINARY mode data connection for file transfer.");
// create socket connection for file transfer
if (!CreateDataConnection())
{
m_nStatus = STATUS_IDLE;
// close data connection with client
DestroyDataConnection();
}
else
{
m_nStatus = STATUS_DOWNLOAD;
m_pDataSocket->AsyncSelect();
// send the file
m_pDataSocket->SendFile(strResult);
}
break;
}
break;
}
// client wants to upload file
case TOK_STOR:
// client wants to append file
case TOK_APPE:
{
CString strResult;
int nResult = CheckFileName(strArguments, FTP_UPLOAD, strResult);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
case ERROR_FILE_NOT_FOUND:
SendResponse("550 Filename invalid.");
break;
default:
m_dwRestartOffset = 0;
// determine offset for appending file
if (nCommand == TOK_APPE)
{
CFileStatus status;
if (CFile::GetStatus(strResult, status))
{
m_dwRestartOffset = status.m_size;
}
}
SendResponse("150 Opening BINARY mode data connection for file transfer.");
// create socket connection for file transfer
if (!CreateDataConnection())
{
m_nStatus = STATUS_IDLE;
// close data connection with client
DestroyDataConnection();
}
else
{
m_nStatus = STATUS_UPLOAD;
// retrieve the file
m_pDataSocket->RetrieveFile(strResult);
m_pDataSocket->AsyncSelect();
}
break;
}
}
break;
// restart transfer
case TOK_REST:
{
m_dwRestartOffset = atol(strArguments);
SendResponse("350 Restarting at %d.", m_dwRestartOffset);
}
break;
// get file size
case TOK_SIZE:
{
CString strResult;
int nResult = CheckFileName(strArguments, FTP_DOWNLOAD, strResult);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
case ERROR_FILE_NOT_FOUND:
SendResponse("550 File not found.");
break;
default:
{
CFileStatus status;
CFile::GetStatus(strResult, status);
SendResponse("213 %d", status.m_size);
break;
}
}
}
break;
// delete file
case TOK_DELE:
{
CString strResult;
int nResult = CheckFileName(strArguments, FTP_DELETE, strResult);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
case ERROR_FILE_NOT_FOUND:
SendResponse("550 File not found.");
break;
default:
CString strRelativePath;
GetRelativePath(strResult, strRelativePath);
// delete the file
if (!DeleteFile(strResult))
{
SendResponse("450 Internal error deleting the file: \"%s\".", strRelativePath);
}
else
{
SendResponse("250 File \"%s\" was deleted successfully.", strRelativePath);
}
break;
}
}
break;
// remove directory
case TOK_RMD:
case TOK_XRMD:
{
CString strResult;
int nResult = CheckDirectory(strArguments, FTP_DELETE, strResult);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
case ERROR_PATH_NOT_FOUND:
SendResponse("550 Directory not found.");
break;
default:
// remove the directory
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:
case TOK_XMKD:
{
CString strResult;
int nResult = CheckDirectory(strArguments, FTP_CREATE_DIR, strResult);
switch(nResult)
{
case ERROR_SUCCESS:
SendResponse("550 Directory already exists.");
break;
case ERROR_ACCESS_DENIED:
SendResponse("550 Can't create directory. Permission denied.");
break;
default:
// create directory structure
if (!MakeSureDirectoryPathExists(strResult))
{
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;
// is argument a valid filename ?
int nResult = CheckFileName(strArguments, FTP_RENAME, strResult);
if (nResult == ERROR_SUCCESS)
{
m_strRenameFile = strResult;
m_bRenameFile = TRUE;
SendResponse("350 File exists, ready for destination name.");
break;
}
else
{
// client wants to rename directory
nResult = CheckDirectory(strArguments, FTP_RENAME, strResult);
switch(nResult)
{
case ERROR_SUCCESS:
m_strRenameFile = strResult;
m_bRenameFile = FALSE;
SendResponse("350 Directory exists, ready for destination name.");
break;
case ERROR_ACCESS_DENIED:
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;
}
CString strResult;
int nResult;
// check destination filename/directory
if (m_bRenameFile)
nResult = CheckFileName(strArguments, FTP_RENAME, strResult);
else
nResult = CheckDirectory(strArguments, FTP_RENAME, strResult);
switch(nResult)
{
case ERROR_SUCCESS:
SendResponse("550 %s already exists.", m_bRenameFile ? "File" : "Directory");
break;
case ERROR_ACCESS_DENIED:
SendResponse("550 Permission denied.");
break;
default:
CString strRelativePath;
GetRelativePath(m_strRenameFile, strRelativePath);
// rename file/directory
if (!MoveFile(m_strRenameFile, strResult))
{
SendResponse("450 Internal error renaming the %s: \"%s\".", m_bRenameFile ? "file" : "directory", strRelativePath);
}
else
{
SendResponse("250 %s \"%s\" renamed successfully.", m_bRenameFile ? "File" : "Directory", strRelativePath);
}
break;
}
}
break;
// abort transfer
case TOK_ABOR:
{
if (m_pDataSocket)
{
if (m_nStatus != STATUS_IDLE)
{
SendResponse("426 Data connection closed.");
}
// destroy data connection
m_pThread->PostThreadMessage(WM_THREADMSG, 0, 0);
}
SendResponse("226 ABOR command successful.");
break;
}
// get system info
case TOK_SYST:
SendResponse("215 UNIX emulated by LQB's FTP Server.");
break;
// close connection
case TOK_QUIT:
case TOK_BYE:
{
// send goodbye message to client
CConnectThread *pThread = (CConnectThread *)m_pThread;
SendResponse("220 %s", ((CFTPServer *)pThread->m_pWndServer)->GetGoodbyeMessage());
Close();
// tell our thread we have been closed
// destroy connection
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
break;
}
// display help
case TOK_HELP:
// if client did not specify a command, display all available commands
if (strArguments == "")
{
CString strResponse = "214-The following commands are recognized:\r\n";
// find command in command list
for (int i = TOK_ABOR; i < TOK_ERROR; i++)
{
strResponse += commandList[i].m_pszName;
// four commands on one line
if ((i+1) % 4 == 0)
strResponse += "\r\n";
else
strResponse += "\t";
}
strResponse += "\r\n214 HELP command successful.";
SendResponse(strResponse);
}
else
{
int nHelpCmd;
// find command in command list
for (nHelpCmd = TOK_ABOR; nHelpCmd < TOK_ERROR; nHelpCmd++)
{
// found command ?
if (strArguments.CompareNoCase(commandList[nHelpCmd].m_pszName) == 0)
{
break;
}
}
if (nHelpCmd != TOK_ERROR)
{
// show help about command
SendResponse("214 %s", commandList[nHelpCmd].m_pszDescription);
}
else
{
SendResponse("501 Unknown command %s", strArguments);
}
}
break;
// dummy instruction
case TOK_NOOP:
SendResponse("200 OK");
break;
default:
SendResponse("502 Command not implemented - Try HELP.");
break;
}
}
/********************************************************************/
/* */
/* Function name: PostStatusMessage */
/* Description : Post status message. */
/* */
/********************************************************************/
void CControlSocket::PostStatusMessage(LPCTSTR lpszStatus, int nType)
{
CConnectThread *pThread = (CConnectThread *)m_pThread;
((CFTPServer *)pThread->m_pWndServer)->AddTraceLine(nType, "[%u] %s", m_pThread->m_nThreadID, lpszStatus);
}
/********************************************************************/
/* */
/* Function name: CreateDataConnection */
/* Description : Create data transfer connection. */
/* */
/********************************************************************/
BOOL CControlSocket::CreateDataConnection()
{
if (!m_bPassiveMode)
{
// need PORT command first !
if (m_strRemoteHost == "" || m_nRemotePort == -1)
{
SendResponse("425 Can't open data connection.");
return FALSE;
}
m_pDataSocket = new CDataSocket(this);
if (m_pDataSocket->Create())
{
// temporary ignore FD_READ and FD_WRITE
m_pDataSocket->AsyncSelect(FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);
// connect to remote site
if (m_pDataSocket->Connect(m_strRemoteHost, m_nRemotePort) == 0)
{
if (GetLastError() != WSAEWOULDBLOCK)
{
SendResponse("425 Can't open data connection.");
return FALSE;
}
}
}
else
{
SendResponse("421 Failed to create data connection socket.");
return FALSE;
}
}
// wait until we're connected (for max 10 seconds)
DWORD dwTickCount = GetTickCount() + 10000;
while (!m_pDataSocket->m_bConnected)
{
DoEvents();
if (dwTickCount < GetTickCount())
{
SendResponse("421 Failed to create data connection socket.");
return FALSE;
}
}
return TRUE;
}
/********************************************************************/
/* */
/* Function name: DestroyDataConnection */
/* Description : Close data transfer connection. */
/* */
/********************************************************************/
void CControlSocket::DestroyDataConnection()
{
if (!m_pDataSocket)
return;
delete m_pDataSocket;
// reset transfer status
m_pDataSocket = NULL;
m_strRemoteHost = "";
m_nRemotePort = -1;
m_dwRestartOffset = 0;
m_bPassiveMode = FALSE;
}
/********************************************************************/
/* */
/* Function name : GetLocalPath */
/* Description : Convert relative path to local path */
/* */
/********************************************************************/
BOOL CControlSocket::GetLocalPath(LPCTSTR lpszRelativePath, CString &strLocalPath)
{
CStringList partList;
CString strSub;
int nCount=0;
BOOL bPathExists = TRUE;
// split path in parts
while(AfxExtractSubString(strSub, lpszRelativePath, nCount++, '/'))
{
// get rid of insignificant dots
if (strSub != "..")
{
strSub.TrimLeft('.');
strSub.TrimRight('.');
}
if (!strSub.IsEmpty())
partList.AddTail(strSub);
}
// search for home directory
for (int i=0; i do not go back further than root
if (!IsVirtualDirectory(strHomeDir))
{
// go back one level
int nPos = strHomeDir.ReverseFind('\\');
if (nPos != -1)
{
strCheckDir = strHomeDir.Left(nPos);
}
}
else
{
strCheckDir = m_User.m_DirectoryArray[i].m_strDir;
}
}
else
{
strCheckDir = strHomeDir + "\\" + strPart;
}
// does directory exist ?
if (FileExists(strCheckDir, TRUE))
{
strHomeDir = strCheckDir;
}
else
// does file exist ?
if (FileExists(strCheckDir, FALSE))
{
strHomeDir = strCheckDir;
}
else
{
BOOL bFound = FALSE;
// virtual directories exist only in the root
if (strHomeDir == m_User.m_DirectoryArray[i].m_strDir)
{
// maybe it's a virtual directory
for (int j=0; j use current dir
if (strDirectory.IsEmpty())
{
strDirectory = m_strCurrentDir;
}
CString strLocalPath;
int nResult = CheckDirectory(strDirectory, FTP_LIST, strLocalPath);
switch(nResult)
{
case ERROR_ACCESS_DENIED:
// user has no permissions
SendResponse("550 \"%s\": Permission denied.", lpszDirectory);
return FALSE;
case ERROR_PATH_NOT_FOUND:
// unable to convert to local path
SendResponse("550 \"%s\": Directory not found.", lpszDirectory);
return FALSE;
default:
// everything is successful
break;
}
CFileFind find;
BOOL bFound = FALSE;
// check if it's a directory
if ((GetFileAttributes(strLocalPath) & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
{
CString strPath = strLocalPath;
if (strPath.Right(1)==_T("\\"))
bFound = find.FindFile(strLocalPath + "*.*");
else
bFound = find.FindFile(strLocalPath + "\\*.*");
}
else
{
// it's a file
bFound = find.FindFile(strLocalPath);
}
while (bFound)
{
bFound = find.FindNextFile();
// skip "." and ".."
if (find.IsDots())
continue;
if (!bNamesOnly)
{
// permissions
if (find.IsDirectory())
strResult += "drwx------";
else
strResult += "-rwx------";
// groups
strResult += " 1 user group ";
// file size
CString strLength;
strLength.Format("%d", find.GetLength());
CString strFiller = " ";
strResult += strFiller.Left(strFiller.GetLength() - strLength.GetLength());
strResult += strLength;
// file date
strResult += GetFileDate(find);
}
// file name
strResult += find.GetFileName();
// end of line
strResult += "\r\n";
}
// if it's the root dir also show virtual directories
for (int i=0; i