📄 ftpclient.cpp
字号:
default:
ASSERT( false );
}
return "";
}
/// Sends a command to the server.
/// @param[in] strCommand Command to send.
bool CFTPClient::SendCommand(CStringA& strCommand)
{
if( !IsConnected() )
return false;
try
{
if(m_pNotification)
m_pNotification->OnSendCommand(strCommand);
CDataStack2 data;
data.Append(strCommand.GetString(),strCommand.GetLength());
data.Append("\r\n",2);
m_apSckControlConnection->Write(data.GetData(), data.Length(), mc_uiTimeout);
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), __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(CStringA& strCommand, CReply& Reply)
{
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)
{
CStringA strResponse;
if( !GetSingleResponseLine(strResponse) )
return false;
if( strResponse.GetLength()>3 && strResponse.GetAt(3)=='-' )
{
CStringA strSingleLine(strResponse);
int iRetCode=StringToLong(strResponse);
// handle multi-line server responses
while( !(strSingleLine.GetLength()>3 &&
strSingleLine.GetAt(3)==' ' &&
StringToLong(strSingleLine)==iRetCode) )
{
if( !GetSingleResponseLine(strSingleLine) )
return false;
strResponse += "\r\n" + strSingleLine;
}
}
bool fRet = Reply.Set(strResponse);
if(m_pNotification)
m_pNotification->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(CStringA& strResponse)
{
if( !IsConnected() )
return false;
try
{
if( m_qResponseBuffer.empty() )
{
// internal buffer is empty ==> get response from ftp-server
int iNum=0;
CStringA strTemp;
do
{
iNum = m_apSckControlConnection->Receive(m_vBuffer.GetData(), static_cast<int>(m_vBuffer.Length())-1, mc_uiTimeout);
if( mc_uiResponseWait !=0 )
Sleep(mc_uiResponseWait);
m_vBuffer.SetAt(iNum,'\0');
strTemp += m_vBuffer.GetData();
} while( iNum==static_cast<int>(m_vBuffer.Length())-1 && m_apSckControlConnection->CheckReadability() );
// each line in response is a separate entry in the internal buffer
int nPos1 = 0,nPos2 = -1;
nPos2 = strTemp.Find('\n',nPos1);
while( nPos2 > -1 )
{
if( nPos2 != -1 )
{
m_qResponseBuffer.push(strTemp.Mid(nPos1, nPos2 - nPos1 + 1));
nPos1 = nPos2 + 1;
nPos2 = strTemp.Find('\n',nPos1);
}
else
{
// this is not rfc standard; normally each command must end with CRLF
// in this case it doesn't
m_qResponseBuffer.push(strTemp);
}
}
if( m_qResponseBuffer.empty() )
return false;
}
// get first response-line from buffer
strResponse = m_qResponseBuffer.front();
m_qResponseBuffer.pop();
// remove CrLf if exists
if( strResponse.GetLength() > 1 && strResponse.Mid(strResponse.GetLength()-2) == "\r\n")
{
strResponse = strResponse.Left(strResponse.GetLength()-2);
}
}
catch(CBlockingSocketException& blockingException)
{
ReportError(blockingException.GetErrorMessage(), __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()
{
CStringA strCmd;
strCmd = (CMakeString() << "CDUP").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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()
{
CStringA strCmd;
strCmd = (CMakeString() << "QUIT").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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)
{
CStringA strCmd;
strCmd = (CMakeString() << "PASV").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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(CStringA strResponse, ULONG& ulIpAddress, USHORT& ushPort)
{
// 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;
CStringA strIpAddress, strPort;
USHORT ushTempPort = 0;
ULONG ulTempIpAddress = 0;
int iCommaCnt = 4;
for( int it = 0; it < strResponse.GetLength(); it++ )
{
switch( enState )
{
case state0:
if( strResponse.GetAt(it) == '(' )
enState = state1;
break;
case state1:
if( strResponse.GetAt(it) == ',' )
{
if( --iCommaCnt == 0 )
{
enState = state2;
ulTempIpAddress += StringToLong(strIpAddress);
}
else
{
ulTempIpAddress += StringToLong(strIpAddress)<<8*iCommaCnt;
strIpAddress = "";
}
}
else
{
if( !_istdigit(strResponse.GetAt(it)) )
return false;
strIpAddress += strResponse.GetAt(it);
}
break;
case state2:
if( strResponse.GetAt(it) == ',' )
{
ushTempPort = static_cast<USHORT>(StringToLong(strPort)<<8);
strPort = "";
enState = state3;
}
else
{
if( !_istdigit(strResponse.GetAt(it)) )
return false;
strPort += strResponse.GetAt(it);
}
break;
case state3:
if( strResponse.GetAt(it) == ')' )
{
// compiler warning if using +=operator
ushTempPort = ushTempPort + static_cast<USHORT>(StringToLong(strPort));
enState = state4;
}
else
{
if( !_istdigit(strResponse.GetAt(it)) )
return false;
strPort += strResponse.GetAt(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()
{
if( m_fTransferInProgress )
{
m_fAbortTransfer = true;
return FTP_OK;
}
CStringA strCmd;
strCmd = (CMakeString() << "ABOR").GetString();
m_fAbortTransfer = false;
CReply Reply;
if( !SendCommand(strCmd, 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()
{
CStringA strCmd;
strCmd = (CMakeString() << "PWD").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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()
{
CStringA strCmd;
strCmd = (CMakeString() << "SYST").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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()
{
CStringA strCmd;
strCmd = (CMakeString() << "NOOP").GetString();
CReply Reply;
if( !SendCommand(strCmd, 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(CStringA& strHostIP, USHORT ushPort)
{
CStringA strPortCmd;
// convert the port number to 2 bytes + add to the local IP
strPortCmd = (CMakeString() << "PORT " << strHostIP << "," << (ushPort>>8) << "," << (ushPort&0xFF)).GetString();
strPortCmd.Replace(".", ",");
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(CRepresentation& representation, DWORD dwSize)
{
CStringA strCmd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -