📄 ftp.cpp
字号:
public:
wxInputFTPStream(wxFTP *ftp, wxSocketBase *sock)
: wxSocketInputStream(*sock)
{
m_ftp = ftp;
// socket timeout automatically set in GetPort function
}
virtual ~wxInputFTPStream()
{
delete m_i_socket; // keep at top
// when checking the result, the stream will
// almost always show an error, even if the file was
// properly transfered, thus, lets just grab the result
// we are looking for "226 transfer completed"
char code = m_ftp->GetResult();
if ('2' == code)
{
// it was a good transfer.
// we're done!
m_ftp->m_streaming = false;
return;
}
// did we timeout?
if (0 == code)
{
// the connection is probably toast. issue an abort, and
// then a close. there won't be any more waiting
// for this connection
m_ftp->Abort();
m_ftp->Close();
return;
}
// There was a problem with the transfer and the server
// has acknowledged it. If we issue an "ABORT" now, the user
// would get the "226" for the abort and think the xfer was
// complete, thus, don't do anything here, just return
}
wxFTP *m_ftp;
DECLARE_NO_COPY_CLASS(wxInputFTPStream)
};
class wxOutputFTPStream : public wxSocketOutputStream
{
public:
wxOutputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
: wxSocketOutputStream(*sock), m_ftp(ftp_clt)
{
}
virtual ~wxOutputFTPStream(void)
{
if ( IsOk() )
{
// close data connection first, this will generate "transfer
// completed" reply
delete m_o_socket;
// read this reply
m_ftp->GetResult(); // save result so user can get to it
m_ftp->m_streaming = false;
}
else
{
// abort data connection first
m_ftp->Abort();
// and close it after
delete m_o_socket;
}
}
wxFTP *m_ftp;
DECLARE_NO_COPY_CLASS(wxOutputFTPStream)
};
void wxFTP::SetDefaultTimeout(wxUint32 Value)
{
m_uiDefaultTimeout = Value;
SetTimeout(Value); // sets it for this socket
}
wxSocketBase *wxFTP::GetPort()
{
/*
PASSIVE: Client sends a "PASV" to the server. The server responds with
an address and port number which it will be listening on. Then
the client connects to the server at the specified address and
port.
ACTIVE: Client sends the server a PORT command which includes an
address and port number which the client will be listening on.
The server then connects to the client at that address and
port.
*/
wxSocketBase *socket = m_bPassive ? GetPassivePort() : GetActivePort();
if ( !socket )
{
m_bEncounteredError = true;
return NULL;
}
// Now set the time for the new socket to the default or user selected
// timeout period
socket->SetTimeout(m_uiDefaultTimeout);
return socket;
}
wxSocketBase *wxFTP::AcceptIfActive(wxSocketBase *sock)
{
if ( m_bPassive )
return sock;
// now wait for a connection from server
wxSocketServer *sockSrv = (wxSocketServer *)sock;
if ( !sockSrv->WaitForAccept() )
{
m_lastError = wxPROTO_CONNERR;
wxLogError(_("Timeout while waiting for FTP server to connect, try passive mode."));
delete sock;
sock = NULL;
}
else
{
sock = sockSrv->Accept(true);
delete sockSrv;
}
return sock;
}
wxString wxFTP::GetPortCmdArgument(wxIPV4address addrLocal,
wxIPV4address addrNew)
{
// Just fills in the return value with the local IP
// address of the current socket. Also it fill in the
// PORT which the client will be listening on
wxString addrIP = addrLocal.IPAddress();
int portNew = addrNew.Service();
// We need to break the PORT number in bytes
addrIP.Replace(_T("."), _T(","));
addrIP << _T(',')
<< wxString::Format(_T("%d"), portNew >> 8) << _T(',')
<< wxString::Format(_T("%d"), portNew & 0xff);
// Now we have a value like "10,0,0,1,5,23"
return addrIP;
}
wxSocketBase *wxFTP::GetActivePort()
{
// we need an address to listen on
wxIPV4address addrNew, addrLocal;
GetLocal(addrLocal);
addrNew.AnyAddress();
addrNew.Service(0); // pick an open port number.
wxSocketServer *sockSrv = new wxSocketServer(addrNew);
if (!sockSrv->Ok())
{
// We use Ok() here to see if everything is ok
m_lastError = wxPROTO_PROTERR;
delete sockSrv;
return NULL;
}
//gets the new address, actually it is just the port number
sockSrv->GetLocal(addrNew);
// Now we create the argument of the PORT command, we send in both
// addresses because the addrNew has an IP of "0.0.0.0", so we need the
// value in addrLocal
wxString port = GetPortCmdArgument(addrLocal, addrNew);
if ( !DoSimpleCommand(_T("PORT "), port) )
{
m_lastError = wxPROTO_PROTERR;
delete sockSrv;
wxLogError(_("The FTP server doesn't support the PORT command."));
return NULL;
}
sockSrv->Notify(false); // Don't send any events
return sockSrv;
}
wxSocketBase *wxFTP::GetPassivePort()
{
if ( !DoSimpleCommand(_T("PASV")) )
{
wxLogError(_("The FTP server doesn't support passive mode."));
return NULL;
}
const wxChar *addrStart = wxStrchr(m_lastResult, _T('('));
const wxChar *addrEnd = addrStart ? wxStrchr(addrStart, _T(')')) : NULL;
if ( !addrEnd )
{
m_lastError = wxPROTO_PROTERR;
return NULL;
}
// get the port number and address
int a[6];
wxString straddr(addrStart + 1, addrEnd);
wxSscanf(straddr, wxT("%d,%d,%d,%d,%d,%d"),
&a[2],&a[3],&a[4],&a[5],&a[0],&a[1]);
wxUint32 hostaddr = (wxUint16)a[2] << 24 |
(wxUint16)a[3] << 16 |
(wxUint16)a[4] << 8 |
a[5];
wxUint16 port = (wxUint16)(a[0] << 8 | a[1]);
wxIPV4address addr;
addr.Hostname(hostaddr);
addr.Service(port);
wxSocketClient *client = new wxSocketClient();
if ( !client->Connect(addr) )
{
delete client;
return NULL;
}
client->Notify(false);
return client;
}
bool wxFTP::Abort()
{
if ( !m_streaming )
return true;
m_streaming = false;
if ( !CheckCommand(wxT("ABOR"), '4') )
return false;
return CheckResult('2');
}
wxInputStream *wxFTP::GetInputStream(const wxString& path)
{
if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
return NULL;
wxSocketBase *sock = GetPort();
if ( !sock )
{
m_lastError = wxPROTO_NETERR;
return NULL;
}
wxString tmp_str = wxT("RETR ") + wxURI::Unescape(path);
if ( !CheckCommand(tmp_str, '1') )
return NULL;
sock = AcceptIfActive(sock);
if ( !sock )
return NULL;
sock->SetFlags(wxSOCKET_WAITALL);
m_streaming = true;
wxInputFTPStream *in_stream = new wxInputFTPStream(this, sock);
return in_stream;
}
wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
{
if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
return NULL;
wxSocketBase *sock = GetPort();
wxString tmp_str = wxT("STOR ") + path;
if ( !CheckCommand(tmp_str, '1') )
return NULL;
sock = AcceptIfActive(sock);
m_streaming = true;
return new wxOutputFTPStream(this, sock);
}
// ----------------------------------------------------------------------------
// FTP directory listing
// ----------------------------------------------------------------------------
bool wxFTP::GetList(wxArrayString& files,
const wxString& wildcard,
bool details)
{
wxSocketBase *sock = GetPort();
if (!sock)
return false;
// NLST : List of Filenames (including Directory's !)
// LIST : depending on BS of FTP-Server
// - Unix : result like "ls" command
// - Windows : like "dir" command
// - others : ?
wxString line(details ? _T("LIST") : _T("NLST"));
if ( !wildcard.empty() )
{
line << _T(' ') << wildcard;
}
if ( !CheckCommand(line, '1') )
{
m_lastError = wxPROTO_PROTERR;
wxLogDebug(_T("FTP 'LIST' command returned unexpected result from server"));
delete sock;
return false;
}
sock = AcceptIfActive(sock);
if ( !sock )
return false;
files.Empty();
while (ReadLine(sock, line) == wxPROTO_NOERR )
{
files.Add(line);
}
delete sock;
// the file list should be terminated by "226 Transfer complete""
return CheckResult('2');
}
bool wxFTP::FileExists(const wxString& fileName)
{
// This function checks if the file specified in fileName exists in the
// current dir. It does so by simply doing an NLST (via GetList).
// If this succeeds (and the list is not empty) the file exists.
bool retval = false;
wxArrayString fileList;
if ( GetList(fileList, fileName, false) )
{
// Some ftp-servers (Ipswitch WS_FTP Server 1.0.5 does this)
// displays this behaviour when queried on a non-existing file:
// NLST this_file_does_not_exist
// 150 Opening ASCII data connection for directory listing
// (no data transferred)
// 226 Transfer complete
// Here wxFTP::GetList(...) will succeed but it will return an empty
// list.
retval = !fileList.IsEmpty();
}
return retval;
}
// ----------------------------------------------------------------------------
// FTP GetSize
// ----------------------------------------------------------------------------
int wxFTP::GetFileSize(const wxString& fileName)
{
// return the filesize of the given file if possible
// return -1 otherwise (predominantly if file doesn't exist
// in current dir)
int filesize = -1;
// Check for existance of file via wxFTP::FileExists(...)
if ( FileExists(fileName) )
{
wxString command;
// First try "SIZE" command using BINARY(IMAGE) transfermode
// Especially UNIX ftp-servers distinguish between the different
// transfermodes and reports different filesizes accordingly.
// The BINARY size is the interesting one: How much memory
// will we need to hold this file?
TransferMode oldTransfermode = m_currentTransfermode;
SetTransferMode(BINARY);
command << _T("SIZE ") << fileName;
bool ok = CheckCommand(command, '2');
if ( ok )
{
// The answer should be one line: "213 <filesize>\n"
// 213 is File Status (STD9)
// "SIZE" is not described anywhere..? It works on most servers
int statuscode;
if ( wxSscanf(GetLastResult().c_str(), _T("%i %i"),
&statuscode, &filesize) == 2 )
{
// We've gotten a good reply.
ok = true;
}
else
{
// Something bad happened.. A "2yz" reply with no size
// Fallback
ok = false;
}
}
// Set transfermode back to the original. Only the "SIZE"-command
// is dependant on transfermode
if ( oldTransfermode != NONE )
{
SetTransferMode(oldTransfermode);
}
// this is not a direct else clause.. The size command might return an
// invalid "2yz" reply
if ( !ok )
{
// The server didn't understand the "SIZE"-command or it
// returned an invalid reply.
// We now try to get details for the file with a "LIST"-command
// and then parse the output from there..
wxArrayString fileList;
if ( GetList(fileList, fileName, true) )
{
if ( !fileList.IsEmpty() )
{
// We _should_ only get one line in return, but just to be
// safe we run through the line(s) returned and look for a
// substring containing the name we are looking for. We
// stop the iteration at the first occurrence of the
// filename. The search is not case-sensitive.
bool foundIt = false;
size_t i;
for ( i = 0; !foundIt && i < fileList.Count(); i++ )
{
foundIt = fileList[i].Upper().Contains(fileName.Upper());
}
if ( foundIt )
{
// The index i points to the first occurrence of
// fileName in the array Now we have to find out what
// format the LIST has returned. There are two
// "schools": Unix-like
//
// '-rw-rw-rw- owner group size month day time filename'
//
// or Windows-like
//
// 'date size filename'
// check if the first character is '-'. This would
// indicate Unix-style (this also limits this function
// to searching for files, not directories)
if ( fileList[i].Mid(0, 1) == _T("-") )
{
if ( wxSscanf(fileList[i].c_str(),
_T("%*s %*s %*s %*s %i %*s %*s %*s %*s"),
&filesize) != 9 )
{
// Hmm... Invalid response
wxLogTrace(FTP_TRACE_MASK,
_T("Invalid LIST response"));
}
}
else // Windows-style response (?)
{
if ( wxSscanf(fileList[i].c_str(),
_T("%*s %*s %i %*s"),
&filesize) != 4 )
{
// something bad happened..?
wxLogTrace(FTP_TRACE_MASK,
_T("Invalid or unknown LIST response"));
}
}
}
}
}
}
}
// filesize might still be -1 when exiting
return filesize;
}
#endif // wxUSE_PROTOCOL_FTP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -