www.pudn.com > BesidesFTPServer.rar > DataSocket.cpp
/********************************************************************/ /* */ /* DataSocket.cpp */ /* */ /* Implementation of the Data Socket. */ /* */ /* Programmed by Pablo van der Meer */ /* http://www.pablovandermeer.nl */ /* */ /* Last updated: 05 september 2002 */ /* */ /********************************************************************/ #include "stdafx.h" #include "resource.h" #include "DataSocket.h" #include "ControlSocket.h" #include "ConnectThread.h" #include#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define PACKET_SIZE 4096 /********************************************************************/ /* */ /* Function name : CDataSocket::CDataSocket */ /* Description : Constructor */ /* */ /********************************************************************/ CDataSocket::CDataSocket(CControlSocket *pSocket) { m_pControlSocket = pSocket; m_strListing = ""; m_File.m_hFile = NULL; m_bConnected = FALSE; m_nTotalBytesSend = 0; m_nTotalBytesTransfered = 0; } /********************************************************************/ /* */ /* Function name : CDataSocket::~CDataSocket */ /* Description : Destructor */ /* */ /********************************************************************/ CDataSocket::~CDataSocket() { m_bConnected = FALSE; } #if 0 BEGIN_MESSAGE_MAP(CDataSocket, CAsyncSocket) //{{AFX_MSG_MAP(CDataSocket) //}}AFX_MSG_MAP END_MESSAGE_MAP() #endif // 0 /********************************************************************/ /* */ /* Function name : OnSend */ /* Description : Called by the framework to notify socket that */ /* it can send data by calling the Send method. */ /* */ /********************************************************************/ void CDataSocket::OnSend(int nErrorCode) { CAsyncSocket::OnSend(nErrorCode); switch(m_pControlSocket->m_nStatus) { case STATUS_LIST: { while (m_nTotalBytesTransfered < m_nTotalBytesSend) { DWORD dwRead; int dwBytes; CString strDataBlock; dwRead = m_strListing.GetLength(); if (dwRead <= PACKET_SIZE) { strDataBlock = m_strListing; } else { strDataBlock = m_strListing.Left(PACKET_SIZE); dwRead = strDataBlock.GetLength(); } if ((dwBytes = Send(strDataBlock, dwRead)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { Sleep(0); return; } else { TCHAR szError[256]; wsprintf(szError, "Server Socket failed to send: %d", GetLastError()); // close the data connection. Close(); m_nTotalBytesSend = 0; m_nTotalBytesTransfered = 0; // change status m_pControlSocket->m_nStatus = STATUS_IDLE; m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); } } else { m_nTotalBytesTransfered += dwBytes; m_strListing = m_strListing.Mid(dwBytes); ((CConnectThread *)AfxGetThread())->IncSentBytes(dwBytes); } } if (m_nTotalBytesTransfered == m_nTotalBytesSend) { // close the data connection. Close(); m_nTotalBytesSend = 0; m_nTotalBytesTransfered = 0; // change status m_pControlSocket->m_nStatus = STATUS_IDLE; // tell the client the transfer is complete. m_pControlSocket->SendResponse("226 Transfer complete"); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); } break; } case STATUS_DOWNLOAD: { while (m_nTotalBytesTransfered < m_nTotalBytesSend) { // allocate space to store data byte data[PACKET_SIZE]; m_File.Seek(m_nTotalBytesTransfered, CFile::begin); DWORD dwRead = m_File.Read(data, PACKET_SIZE); int dwBytes; if ((dwBytes = Send(data, dwRead)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { Sleep(0); break; } else { TCHAR szError[256]; wsprintf(szError, "Server Socket failed to send: %d", GetLastError()); // close file. m_File.Close(); m_File.m_hFile = NULL; // close the data connection. Close(); m_nTotalBytesSend = 0; m_nTotalBytesTransfered = 0; // change status m_pControlSocket->m_nStatus = STATUS_IDLE; m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // download failed ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_DOWNLOADFAILED); } } else { m_nTotalBytesTransfered += dwBytes; ((CConnectThread *)AfxGetThread())->IncSentBytes(dwBytes); } } if (m_nTotalBytesTransfered == m_nTotalBytesSend) { // close file. m_File.Close(); m_File.m_hFile = NULL; // close the data connection. Close(); m_nTotalBytesSend = 0; m_nTotalBytesTransfered = 0; // change status m_pControlSocket->m_nStatus = STATUS_IDLE; // tell the client the transfer is complete. m_pControlSocket->SendResponse("226 Transfer complete"); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // download successfull ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_DOWNLOADSUCCEEDED); } break; } } } /********************************************************************/ /* */ /* Function name : OnAccept */ /* Description : Notify this listening socket that it can accept */ /* pending connection requests. */ /* */ /********************************************************************/ void CDataSocket::OnAccept(int nErrorCode) { // Accept the connection using a temp CSocket object. CAsyncSocket tmpSocket; Accept(tmpSocket); SOCKET socket = tmpSocket.Detach(); Close(); // re-use same socket for connection Attach(socket); // we're connected! m_bConnected = TRUE; CAsyncSocket::OnAccept(nErrorCode); } /********************************************************************/ /* */ /* Function name : OnConnect */ /* Description : Called by the framework to notify connecting */ /* socket that its connection attempt is completed */ /* */ /********************************************************************/ void CDataSocket::OnConnect(int nErrorCode) { m_bConnected = TRUE; if (nErrorCode) { // change status m_pControlSocket->m_nStatus = STATUS_IDLE; m_pControlSocket->SendResponse("425 Can't open data connection."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); } CAsyncSocket::OnConnect(nErrorCode); } /********************************************************************/ /* */ /* Function name : OnClose */ /* Description : Called by the framework to notify this socket */ /* that the connected socket is closed. */ /* */ /********************************************************************/ void CDataSocket::OnClose(int nErrorCode) { if (m_pControlSocket) { // shutdown sends ShutDown(1); if (m_pControlSocket->m_nStatus == STATUS_UPLOAD) { while(Receive() != 0) { // receive remaining data } } else { m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // upload failed ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_UPLOADFAILED); } } m_pControlSocket->m_nStatus = STATUS_IDLE; m_bConnected = FALSE; CAsyncSocket::OnClose(nErrorCode); } /********************************************************************/ /* */ /* Function name : OnReceive */ /* Description : Called by the framework to notify this socket */ /* that there is data in the buffer that can be */ /* retrieved by calling the Receive member function.*/ /* */ /********************************************************************/ void CDataSocket::OnReceive(int nErrorCode) { CAsyncSocket::OnReceive(nErrorCode); Receive(); } /********************************************************************/ /* */ /* Function name : Receive */ /* Description : Receive data from a socket */ /* */ /********************************************************************/ int CDataSocket::Receive() { int nRead = 0; if (m_pControlSocket->m_nStatus == STATUS_UPLOAD) { if (m_File.m_hFile == NULL) return 0; byte data[PACKET_SIZE]; nRead = CAsyncSocket::Receive(data, PACKET_SIZE); switch(nRead) { case 0: { m_File.Close(); m_File.m_hFile = NULL; Close(); // tell the client the transfer is complete. m_pControlSocket->SendResponse("226 Transfer complete"); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // upload succesfull ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_UPLOADSUCCEEDED); break; } case SOCKET_ERROR: { if (GetLastError() != WSAEWOULDBLOCK) { m_File.Close(); m_File.m_hFile = NULL; Close(); m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // upload failed ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_UPLOADFAILED); } break; } default: { ((CConnectThread *)AfxGetThread())->IncReceivedBytes(nRead); TRY { m_File.Write(data, nRead); } CATCH_ALL(e) { m_File.Close(); m_File.m_hFile = NULL; Close(); m_pControlSocket->SendResponse("450 Can't access file."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // upload failed ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_UPLOADFAILED); return 0; } END_CATCH_ALL; break; } } } return nRead; } /********************************************************************/ /* */ /* Function name : SendListing */ /* Description : Send listing to client */ /* */ /********************************************************************/ void CDataSocket::SendListing(LPCTSTR lpszListing) { m_strListing = lpszListing; m_nTotalBytesSend = m_strListing.GetLength(); m_nTotalBytesTransfered = 0; // start sending... OnSend(0); } /********************************************************************/ /* */ /* Function name : SendFile */ /* Description : Send file to client */ /* */ /********************************************************************/ void CDataSocket::SendFile(LPCTSTR lpszFilename) { if (!PrepareSendFile(lpszFilename)) { // change status m_pControlSocket->m_nStatus = STATUS_IDLE; m_pControlSocket->SendResponse("426 Connection closed; transfer aborted."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); return; } // start sending... OnSend(0); } /********************************************************************/ /* */ /* Function name : RetrieveFile */ /* Description : Retrieve file from client */ /* */ /********************************************************************/ void CDataSocket::RetrieveFile(LPCTSTR lpszFilename) { if (!PrepareReceiveFile(lpszFilename)) { // change status m_pControlSocket->m_nStatus = STATUS_IDLE; m_pControlSocket->SendResponse("450 Can't access file."); // destroy this socket AfxGetThread()->PostThreadMessage(WM_THREADMSG, 0, 0); // upload failed ((CConnectThread *)AfxGetThread())->UpdateStatistic(FTPSTAT_UPLOADFAILED); return; } } /********************************************************************/ /* */ /* Function name : PrepareReceiveFile */ /* Description : Prepare socket to receive a file. */ /* */ /********************************************************************/ BOOL CDataSocket::PrepareReceiveFile(LPCTSTR lpszFilename) { // close file if it's already open if (m_File.m_hFile != NULL) { m_File.Close(); } // open destination file if (!m_File.Open(lpszFilename, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyWrite)) { return FALSE; } m_nTotalBytesReceive = 0; m_nTotalBytesTransfered = 0; m_File.SetLength(m_pControlSocket->m_dwRestartOffset); m_File.SeekToEnd(); return TRUE; } /********************************************************************/ /* */ /* Function name : PrepareSendFile */ /* Description : Prepare socket to send a file. */ /* */ /********************************************************************/ BOOL CDataSocket::PrepareSendFile(LPCTSTR lpszFilename) { // close file if it's already open if (m_File.m_hFile != NULL) { m_File.Close(); } // open source file (bug fix by Mutex) if (!m_File.Open(lpszFilename, CFile::modeRead | CFile::shareDenyNone | CFile::typeBinary)) { return FALSE; } m_nTotalBytesSend = m_File.GetLength(); if (m_pControlSocket->m_dwRestartOffset < m_nTotalBytesSend) { m_nTotalBytesTransfered = m_pControlSocket->m_dwRestartOffset; } else { m_nTotalBytesTransfered = 0; } return TRUE; }