📄 ftpclient.cpp
字号:
tstring strSingleLine(strResponse);
const int iRetCode=CCnv::TStringToLong(strResponse);
// handle multi-line server responses
while( !(strSingleLine.length()>3 &&
strSingleLine.at(3)==_T(' ') &&
CCnv::TStringToLong(strSingleLine)==iRetCode) )
{
if( !GetSingleResponseLine(strSingleLine) )
return false;
strResponse += _T("\r\n") + strSingleLine;
}
}
bool fRet = Reply.Set(strResponse);
for( TObserverSet::const_iterator it=m_setObserver.begin(); it!=m_setObserver.end(); it++ )
(*it)->OnResponse(Reply);
return fRet;
}
/// Reads a single response line from the server control channel.
/// @param[out] strResponse Response of the server as string.
bool CFTPClient::GetSingleResponseLine(tstring& strResponse) const
{
if( !IsConnected() )
return false;
try
{
if( m_qResponseBuffer.empty() )
{
// internal buffer is empty ==> get response from ftp-server
int iNum=0;
std::string strTemp;
do
{
iNum=m_apSckControlConnection->Receive(&(*m_vBuffer.begin()), static_cast<int>(m_vBuffer.size())-1, mc_uiTimeout);
if( mc_uiResponseWait !=0 )
Sleep(mc_uiResponseWait);
m_vBuffer[iNum] = '\0';
strTemp+=&(*m_vBuffer.begin());
} while( iNum==static_cast<int>(m_vBuffer.size())-1 && m_apSckControlConnection->CheckReadability() );
// each line in response is a separate entry in the internal buffer
while( strTemp.length() )
{
size_t iCRLF=strTemp.find('\n');
if( iCRLF != std::string::npos )
{
m_qResponseBuffer.push(strTemp.substr(0, iCRLF+1));
strTemp.erase(0, iCRLF+1);
}
else
{
// this is not rfc standard; normally each command must end with CRLF
// in this case it doesn't
m_qResponseBuffer.push(strTemp);
strTemp.clear();
}
}
if( m_qResponseBuffer.empty() )
return false;
}
// get first response-line from buffer
strResponse = CCnv::ConvertToTString(m_qResponseBuffer.front().c_str());
m_qResponseBuffer.pop();
// remove CrLf if exists
if( strResponse.length()> 1 && strResponse.substr(strResponse.length()-2)==_T("\r\n") )
strResponse.erase(strResponse.length()-2, 2);
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), CCnv::ConvertToTString(__FILE__), __LINE__);
const_cast<CFTPClient*>(this)->m_apSckControlConnection->Cleanup();
return false;
}
return true;
}
/// Executes the ftp command CDUP (change to parent directory).
/// This command is a special case of CFTPClient::ChangeWorkingDirectory
/// (CWD), and is included to simplify the implementation of programs for
/// transferring directory trees between operating systems having different
/// syntaxes for naming the parent directory.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::ChangeToParentDirectory() const
{
CReply Reply;
if( !SendCommand(_T("CDUP"), Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command QUIT.
/// This command terminates a USER and if file transfer is not in progress,
/// the server closes the control connection. If file transfer is in progress,
/// the connection will remain open for result response and the server will
/// then close it.
/// If the user-process is transferring files for several USERs but does not
/// wish to close and then reopen connections for each, then the REIN command
/// should be used instead of QUIT.
/// An unexpected close on the control connection will cause the server to take
/// the effective action of an abort (ABOR) and a logout.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::Logout()
{
CReply Reply;
if( !SendCommand(_T("QUIT"), Reply) )
return FTP_ERROR;
CloseControlChannel();
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command PASV. Set the passive mode.
/// This command requests the server-DTP (data transfer process) on a data to
/// "listen" port (which is not its default data port) and to wait for a
/// connection rather than initiate one upon receipt of a transfer command.
/// The response to this command includes the host and port address this
/// server is listening on.
/// @param[out] ulIpAddress IP address the server is listening on.
/// @param[out] ushPort Port the server is listening on.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::Passive(ULONG& ulIpAddress, USHORT& ushPort) const
{
CReply Reply;
if( !SendCommand(_T("PASV"), Reply) )
return FTP_ERROR;
if( Reply.Code().IsPositiveCompletionReply() )
{
if( !GetIpAddressFromResponse(Reply.Value(), ulIpAddress, ushPort) )
return FTP_ERROR;
}
return SimpleErrorCheck(Reply);
}
/// Parses a response string and extracts the ip address and port information.
/// @param[in] strResponse The response string of a ftp server which holds
/// the ip address and port information.
/// @param[out] ulIpAddress Buffer for the ip address.
/// @param[out] ushPort Buffer for the port information.
/// @retval true Everything went ok.
/// @retval false An error occurred (invalid format).
bool CFTPClient::GetIpAddressFromResponse(const tstring& strResponse, ULONG& ulIpAddress, USHORT& ushPort) const
{
// parsing of ip-address and port implemented with a finite state machine
// ...(192,168,1,1,3,44)...
enum T_enState { state0, state1, state2, state3, state4 } enState = state0;
tstring strIpAddress, strPort;
USHORT ushTempPort = 0;
ULONG ulTempIpAddress = 0;
int iCommaCnt = 4;
for( tstring::const_iterator it=strResponse.begin(); it!=strResponse.end(); ++it )
{
switch( enState )
{
case state0:
if( *it == _T('(') )
enState = state1;
break;
case state1:
if( *it == _T(',') )
{
if( --iCommaCnt == 0 )
{
enState = state2;
ulTempIpAddress += CCnv::TStringToLong(strIpAddress.c_str());
}
else
{
ulTempIpAddress += CCnv::TStringToLong(strIpAddress.c_str())<<8*iCommaCnt;
strIpAddress.clear();
}
}
else
{
if( !tisdigit(*it) )
return false;
strIpAddress += *it;
}
break;
case state2:
if( *it == _T(',') )
{
ushTempPort = static_cast<USHORT>(CCnv::TStringToLong(strPort.c_str())<<8);
strPort.clear();
enState = state3;
}
else
{
if( !tisdigit(*it) )
return false;
strPort += *it;
}
break;
case state3:
if( *it == _T(')') )
{
// compiler warning if using +=operator
ushTempPort = ushTempPort + static_cast<USHORT>(CCnv::TStringToLong(strPort.c_str()));
enState = state4;
}
else
{
if( !tisdigit(*it) )
return false;
strPort += *it;
}
}
}
if( enState==state4 )
{
ulIpAddress = ulTempIpAddress;
ushPort = ushTempPort;
}
return enState==state4;
}
/// Executes the ftp command ABOR.
/// This command tells the server to abort the previous FTP service command
/// and any associated transfer of data. The abort command may require
/// "special action", as discussed in the Section on FTP Commands, to force
/// recognition by the server. No action is to be taken if the previous
/// command has been completed (including data transfer). The control
/// connection is not to be closed by the server, but the data connection
/// must be closed.
/// There are two cases for the server upon receipt of this command:<BR>
/// (1) the FTP service command was already completed, or <BR>
/// (2) the FTP service command is still in progress.<BR>
/// In the first case, the server closes the data connection (if it is open)
/// and responds with a 226 reply, indicating that the abort command was
/// successfully processed.
/// In the second case, the server aborts the FTP service in progress and
/// closes the data connection, returning a 426 reply to indicate that the
/// service request terminated abnormally. The server then sends a 226 reply,
/// indicating that the abort command was successfully processed.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::Abort() const
{
if( m_fTransferInProgress )
{
m_fAbortTransfer = true;
return FTP_OK;
}
m_fAbortTransfer = false;
CReply Reply;
if( !SendCommand(_T("ABOR"), Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command PWD (PRINT WORKING DIRECTORY)
/// This command causes the name of the current working directory
/// to be returned in the reply.
int CFTPClient::PrintWorkingDirectory() const
{
CReply Reply;
if( !SendCommand(_T("PWD"), Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command SYST (SYSTEM)
/// This command is used to find out the type of operating system at the server.
/// The reply shall have as its first word one of the system names listed in the
/// current version of the Assigned Numbers document [Reynolds, Joyce, and
/// Jon Postel, "Assigned Numbers", RFC 943, ISI, April 1985.].
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::System() const
{
CReply Reply;
if( !SendCommand(_T("SYST"), Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command NOOP
/// This command does not affect any parameters or previously entered commands.
/// It specifies no action other than that the server send an FTP_OK reply.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::Noop() const
{
CReply Reply;
if( !SendCommand(_T("NOOP"), Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command PORT (DATA PORT)
/// The argument is a HOST-PORT specification for the data port to be used in data
/// connection. There are defaults for both the user and server data ports, and
/// under normal circumstances this command and its reply are not needed. If
/// this command is used, the argument is the concatenation of a 32-bit internet
/// host address and a 16-bit TCP port address.
/// @param[in] strHostIP IP-address like xxx.xxx.xxx.xxx
/// @param[in] uiPort 16-bit TCP port address.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::DataPort(const tstring& strHostIP, USHORT ushPort) const
{
tstring strPortCmd;
// convert the port number to 2 bytes + add to the local IP
strPortCmd = CMakeString() << _T("PORT ") << strHostIP << _T(",") << (ushPort>>8) << _T(",") << (ushPort&0xFF);
ReplaceStr(strPortCmd, _T("."), _T(","));
CReply Reply;
if( !SendCommand(strPortCmd, Reply) )
return FTP_ERROR;
return SimpleErrorCheck(Reply);
}
/// Executes the ftp command TYPE (REPRESENTATION TYPE)
/// see Documentation of nsFTP::CRepresentation
/// @param[in] representation see Documentation of nsFTP::CRepresentation
/// @param[in] iSize Indicates Bytesize for type LocalByte.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::RepresentationType(const CRepresentation& representation, DWORD dwSize) const
{
tstring strCmd;
switch( representation.Type().AsEnum() )
{
case CType::tyASCII: strCmd = _T("TYPE A"); break;
case CType::tyEBCDIC: strCmd = _T("TYPE E"); break;
case CType::tyImage: strCmd = _T("TYPE I"); break;
case CType::tyLocalByte: strCmd = CMakeString() << _T("TYPE L ") << dwSize; break;
default:
ASSERT( false );
return FTP_ERROR;
}
if( representation.Type()==CType::tyASCII ||
representation.Type()==CType::tyEBCDIC )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -