📄 ftpclient.cpp
字号:
(*it)->OnPostSendFile(strLocalFile, strRemoteFile, lLocalFileSize);
return fRet;
}
/// Executes a commando that result in a communication over the data port.
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] representation see documentation of CFTPClient::CRepresentation
/// @param[in] fPasv see documentation of CFTPClient::Passive
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
/// @param[in] pObserver Object for observing the execution of the command.
bool CFTPClient::ExecuteDatachannelCommand(const CDatachannelCmd& crDatachannelCmd, const tstring& strPath, const CRepresentation& representation,
bool fPasv, DWORD dwByteOffset, ITransferNotification* pObserver) const
{
if( m_fTransferInProgress )
return false;
if( !IsConnected() )
return false;
// check representation
if( m_apCurrentRepresentation.get()==NULL )
m_apCurrentRepresentation.reset(new CRepresentation(CType::ASCII()));
if( representation!=*m_apCurrentRepresentation )
{
// transmit representation to server
if( RepresentationType(representation)!=FTP_OK )
return false;
*m_apCurrentRepresentation = representation;
}
std::auto_ptr<IBlockingSocket> apSckDataConnection(m_apSckControlConnection->CreateInstance());
if( fPasv )
{
if( !OpenPassiveDataConnection(*apSckDataConnection, crDatachannelCmd, strPath, dwByteOffset) )
return false;
}
else
{
if( !OpenActiveDataConnection(*apSckDataConnection, crDatachannelCmd, strPath, dwByteOffset) )
return false;
}
const bool fTransferOK = TransferData(crDatachannelCmd, pObserver, *apSckDataConnection);
apSckDataConnection->Close();
// get response from ftp server
CReply Reply;
if( !fTransferOK || !GetResponse(Reply) || !Reply.Code().IsPositiveCompletionReply() )
return false;
return true;
}
/// Executes a commando that result in a communication over the data port.
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for sending/receiving data.
bool CFTPClient::TransferData(const CDatachannelCmd& crDatachannelCmd, ITransferNotification* pObserver, IBlockingSocket& sckDataConnection) const
{
switch( crDatachannelCmd.AsEnum() )
{
case CDatachannelCmd::cmdSTOR:
case CDatachannelCmd::cmdSTOU:
case CDatachannelCmd::cmdAPPE:
{
if( !SendData(pObserver, sckDataConnection) )
return false;
}
break;
case CDatachannelCmd::cmdRETR:
case CDatachannelCmd::cmdLIST:
case CDatachannelCmd::cmdNLST:
if( !ReceiveData(pObserver, sckDataConnection) )
return false;
break;
default:
ASSERT( false );
return false;
}
return true;
}
/// Opens an active data connection.
/// @param[out] sckDataConnection
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
bool CFTPClient::OpenActiveDataConnection(IBlockingSocket& sckDataConnection, const CDatachannelCmd& crDatachannelCmd, const tstring& strPath, DWORD dwByteOffset) const
{
std::auto_ptr<IBlockingSocket> apSckServer(m_apSckControlConnection->CreateInstance());
USHORT ushLocalSock = 0;
try
{
// INADDR_ANY = ip address of localhost
// second parameter "0" means that the WINSOCKAPI ask for a port
CSockAddr csaAddressTemp(INADDR_ANY, 0);
apSckServer->Create(SOCK_STREAM);
apSckServer->Bind(csaAddressTemp);
apSckServer->GetSockAddr(csaAddressTemp);
ushLocalSock=csaAddressTemp.Port();
apSckServer->Listen();
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
apSckServer->Cleanup();
return false;
}
// get own ip address
CSockAddr csaLocalAddress;
m_apSckControlConnection->GetSockAddr(csaLocalAddress);
// transmit the socket (ip address + port) to the server
// the ftp server establishes then the data connection
if( DataPort(csaLocalAddress.DottedDecimal(), ushLocalSock)!=FTP_OK )
return false;
// if resuming is activated then set offset
if( m_fResumeIfPossible &&
(crDatachannelCmd==CDatachannelCmd::cmdSTOR || crDatachannelCmd==CDatachannelCmd::cmdRETR || crDatachannelCmd==CDatachannelCmd::cmdAPPE ) &&
(dwByteOffset!=0 && Restart(dwByteOffset)!=FTP_OK) )
return false;
// send FTP command RETR/STOR/NLST/LIST to the server
CReply Reply;
if( !SendCommand(GetCmdString(crDatachannelCmd, strPath), Reply) ||
!Reply.Code().IsPositivePreliminaryReply() )
return false;
// accept the data connection
CSockAddr sockAddrTemp;
if( !apSckServer->Accept(sckDataConnection, sockAddrTemp) )
return false;
return true;
}
/// Opens a passive data connection.
/// @param[out] sckDataConnection
/// @param[in] crDatachannelCmd Command to be executeted.
/// @param[in] strPath Parameter for the command usually a path.
/// @param[in] dwByteOffset Server marker at which file transfer is to be restarted.
bool CFTPClient::OpenPassiveDataConnection(IBlockingSocket& sckDataConnection, const CDatachannelCmd& crDatachannelCmd, const tstring& strPath, DWORD dwByteOffset) const
{
ULONG ulRemoteHostIP = 0;
USHORT ushServerSock = 0;
// set passive mode
// the ftp server opens a port and tell us the socket (ip address + port)
// this socket is used for opening the data connection
if( Passive(ulRemoteHostIP, ushServerSock)!=FTP_OK )
return false;
// establish connection
CSockAddr sockAddrTemp;
try
{
sckDataConnection.Create(SOCK_STREAM);
CSockAddr csaAddress(ulRemoteHostIP, ushServerSock);
sckDataConnection.Connect(csaAddress);
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
sckDataConnection.Cleanup();
return false;
}
// if resuming is activated then set offset
if( m_fResumeIfPossible &&
(crDatachannelCmd==CDatachannelCmd::cmdSTOR || crDatachannelCmd==CDatachannelCmd::cmdRETR || crDatachannelCmd==CDatachannelCmd::cmdAPPE ) &&
(dwByteOffset!=0 && Restart(dwByteOffset)!=FTP_OK) )
return false;
// send FTP command RETR/STOR/NLST/LIST to the server
CReply Reply;
if( !SendCommand(GetCmdString(crDatachannelCmd, strPath), Reply) ||
!Reply.Code().IsPositivePreliminaryReply() )
return false;
return true;
}
/// Sends data over a socket to the server.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for the send action.
bool CFTPClient::SendData(ITransferNotification* pObserver, IBlockingSocket& sckDataConnection) const
{
try
{
m_fTransferInProgress=true;
int iNumWrite;
size_t bytesRead;
pObserver->OnPreBytesSend(m_vBuffer, bytesRead);
while( !m_fAbortTransfer && bytesRead!=0 )
{
iNumWrite = sckDataConnection.Write(&(*m_vBuffer.begin()), static_cast<int>(bytesRead), mc_uiTimeout);
ASSERT( iNumWrite == static_cast<int>(bytesRead) );
for( TObserverSet::const_iterator it=m_setObserver.begin(); it!=m_setObserver.end(); it++ )
(*it)->OnBytesSent(m_vBuffer, iNumWrite);
pObserver->OnPreBytesSend(m_vBuffer, bytesRead);
}
m_fTransferInProgress=false;
if( m_fAbortTransfer )
{
Abort();
return false;
}
}
catch(CBlockingSocketException& blockingException)
{
m_fTransferInProgress=false;
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
sckDataConnection.Cleanup();
return false;
}
return true;
}
/// Receives data over a socket from the server.
/// @param[in] pObserver Object for observing the execution of the command.
/// @param[in] sckDataConnection Socket which is used for receiving the data.
bool CFTPClient::ReceiveData(ITransferNotification* pObserver, IBlockingSocket& sckDataConnection) const
{
try
{
m_fTransferInProgress = true;
for( TObserverSet::const_iterator it=m_setObserver.begin(); it!=m_setObserver.end(); it++ )
(*it)->OnBeginReceivingData();
int iNumRead=sckDataConnection.Receive(&(*m_vBuffer.begin()), static_cast<int>(m_vBuffer.size()), mc_uiTimeout);
long lTotalBytes = iNumRead;
while( !m_fAbortTransfer && iNumRead!=0 )
{
for( TObserverSet::const_iterator it=m_setObserver.begin(); it!=m_setObserver.end(); it++ )
(*it)->OnBytesReceived(m_vBuffer, iNumRead);
pObserver->OnBytesReceived(m_vBuffer, iNumRead);
iNumRead=sckDataConnection.Receive(&(*m_vBuffer.begin()), static_cast<int>(m_vBuffer.size()), mc_uiTimeout);
lTotalBytes += iNumRead;
}
for( TObserverSet::const_iterator it2=m_setObserver.begin(); it2!=m_setObserver.end(); it2++ )
(*it2)->OnEndReceivingData(lTotalBytes);
m_fTransferInProgress=false;
if( m_fAbortTransfer )
{
Abort();
return false;
}
}
catch(CBlockingSocketException& blockingException)
{
m_fTransferInProgress=false;
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
sckDataConnection.Cleanup();
return false;
}
return true;
}
/// Returns the command string for a specific command.
/// @param[in] crDatachannelCmd Command for which the string should be returned.
/// @param[in] strPath Parameter which have to be added to the command.
tstring CFTPClient::GetCmdString(const CDatachannelCmd& crDatachannelCmd, const tstring& strPath) const
{
switch( crDatachannelCmd.AsEnum() )
{
case CDatachannelCmd::cmdLIST: return strPath.empty()?_T("LIST"):_T("LIST ") + strPath;
case CDatachannelCmd::cmdNLST: return strPath.empty()?_T("NLST"):_T("NLST ") + strPath;
case CDatachannelCmd::cmdSTOR: return _T("STOR ") + strPath;
case CDatachannelCmd::cmdSTOU: return _T("STOU ") + strPath;
case CDatachannelCmd::cmdRETR: return _T("RETR ") + strPath;
case CDatachannelCmd::cmdAPPE: return _T("APPE ") + strPath;
default:
ASSERT( false );
}
return _T("");
}
/// Sends a command to the server.
/// @param[in] strCommand Command to send.
bool CFTPClient::SendCommand(const tstring& strCommand) const
{
if( !IsConnected() )
return false;
try
{
for( TObserverSet::const_iterator it=m_setObserver.begin(); it!=m_setObserver.end(); it++ )
(*it)->OnSendCommand(strCommand);
m_apSckControlConnection->Write((CCnv::ConvertToString(strCommand) + "\r\n").c_str(), static_cast<int>(strCommand.length())+2, mc_uiTimeout);
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
const_cast<CFTPClient*>(this)->m_apSckControlConnection->Cleanup();
return false;
}
return true;
}
/// Sends a command to the server.
/// @param[in] strCommand Command to send.
/// @param[out] Reply The Reply of the server to the sent command.
bool CFTPClient::SendCommand(const tstring& strCommand, CReply& Reply) const
{
if( !SendCommand(strCommand) || !GetResponse(Reply) )
return false;
return true;
}
/// This function gets the server response.
/// A server response can exists of more than one line. This function
/// returns the full response (multiline).
/// @param[out] Reply Reply of the server to a command.
bool CFTPClient::GetResponse(CReply& Reply) const
{
tstring strResponse;
if( !GetSingleResponseLine(strResponse) )
return false;
if( strResponse.length()>3 && strResponse.at(3)==_T('-') )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -