PageRenderTime 31ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/common/ftp.cpp

https://bitbucket.org/lennonchan/cafu
C++ | 1022 lines | 737 code | 130 blank | 155 comment | 90 complexity | b3e1d53cb32185cbb8a4984e1b532d14 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/common/ftp.cpp
  3. // Purpose: FTP protocol
  4. // Author: Guilhem Lavaux
  5. // Modified by: Mark Johnson, wxWindows@mj10777.de
  6. // 20000917 : RmDir, GetLastResult, GetList
  7. // Vadim Zeitlin (numerous fixes and rewrites to all part of the
  8. // code, support ASCII/Binary modes, better error reporting, more
  9. // robust Abort(), support for arbitrary FTP commands, ...)
  10. // Randall Fox (support for active mode)
  11. // Created: 07/07/1997
  12. // RCS-ID: $Id$
  13. // Copyright: (c) 1997, 1998 Guilhem Lavaux
  14. // (c) 1998-2004 wxWidgets team
  15. // Licence: wxWindows licence
  16. /////////////////////////////////////////////////////////////////////////////
  17. // ============================================================================
  18. // declarations
  19. // ============================================================================
  20. // ----------------------------------------------------------------------------
  21. // headers
  22. // ----------------------------------------------------------------------------
  23. // For compilers that support precompilation, includes "wx.h".
  24. #include "wx/wxprec.h"
  25. #ifdef __BORLANDC__
  26. #pragma hdrstop
  27. #endif
  28. #if wxUSE_PROTOCOL_FTP
  29. #ifndef WX_PRECOMP
  30. #include <stdlib.h>
  31. #include "wx/string.h"
  32. #include "wx/utils.h"
  33. #include "wx/log.h"
  34. #include "wx/intl.h"
  35. #include "wx/wxcrtvararg.h"
  36. #endif // WX_PRECOMP
  37. #include "wx/sckaddr.h"
  38. #include "wx/socket.h"
  39. #include "wx/url.h"
  40. #include "wx/sckstrm.h"
  41. #include "wx/protocol/protocol.h"
  42. #include "wx/protocol/ftp.h"
  43. #ifndef __MWERKS__
  44. #include <memory.h>
  45. #endif
  46. // ----------------------------------------------------------------------------
  47. // constants
  48. // ----------------------------------------------------------------------------
  49. // the length of FTP status code (3 digits)
  50. static const size_t LEN_CODE = 3;
  51. // ----------------------------------------------------------------------------
  52. // macros
  53. // ----------------------------------------------------------------------------
  54. IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
  55. IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), true)
  56. // ============================================================================
  57. // implementation
  58. // ============================================================================
  59. // ----------------------------------------------------------------------------
  60. // wxFTP constructor and destructor
  61. // ----------------------------------------------------------------------------
  62. wxFTP::wxFTP()
  63. {
  64. m_streaming = false;
  65. m_currentTransfermode = NONE;
  66. m_username = wxT("anonymous");
  67. m_password << wxGetUserId() << wxT('@') << wxGetFullHostName();
  68. SetNotify(0);
  69. SetFlags(wxSOCKET_NOWAIT);
  70. m_bPassive = true;
  71. m_bEncounteredError = false;
  72. }
  73. wxFTP::~wxFTP()
  74. {
  75. if ( m_streaming )
  76. {
  77. // if we are streaming, this will issue
  78. // an FTP ABORT command, to tell the server we are aborting
  79. (void)Abort();
  80. }
  81. // now this issues a "QUIT" command to tell the server we are
  82. Close();
  83. }
  84. // ----------------------------------------------------------------------------
  85. // wxFTP connect and login methods
  86. // ----------------------------------------------------------------------------
  87. bool wxFTP::Connect(const wxSockAddress& addr, bool WXUNUSED(wait))
  88. {
  89. if ( !wxProtocol::Connect(addr) )
  90. {
  91. m_lastError = wxPROTO_NETERR;
  92. return false;
  93. }
  94. if ( !m_username )
  95. {
  96. m_lastError = wxPROTO_CONNERR;
  97. return false;
  98. }
  99. // we should have 220 welcome message
  100. if ( !CheckResult('2') )
  101. {
  102. Close();
  103. return false;
  104. }
  105. wxString command;
  106. command.Printf(wxT("USER %s"), m_username.c_str());
  107. char rc = SendCommand(command);
  108. if ( rc == '2' )
  109. {
  110. // 230 return: user accepted without password
  111. m_lastError = wxPROTO_NOERR;
  112. return true;
  113. }
  114. if ( rc != '3' )
  115. {
  116. m_lastError = wxPROTO_CONNERR;
  117. Close();
  118. return false;
  119. }
  120. command.Printf(wxT("PASS %s"), m_password.c_str());
  121. if ( !CheckCommand(command, '2') )
  122. {
  123. m_lastError = wxPROTO_CONNERR;
  124. Close();
  125. return false;
  126. }
  127. m_lastError = wxPROTO_NOERR;
  128. return true;
  129. }
  130. bool wxFTP::Connect(const wxString& host, unsigned short port)
  131. {
  132. wxIPV4address addr;
  133. addr.Hostname(host);
  134. if ( port )
  135. addr.Service(port);
  136. else if (!addr.Service(wxT("ftp")))
  137. addr.Service(21);
  138. return Connect(addr);
  139. }
  140. bool wxFTP::Close()
  141. {
  142. if ( m_streaming )
  143. {
  144. m_lastError = wxPROTO_STREAMING;
  145. return false;
  146. }
  147. if ( IsConnected() )
  148. {
  149. if ( !CheckCommand(wxT("QUIT"), '2') )
  150. {
  151. m_lastError = wxPROTO_CONNERR;
  152. wxLogDebug(wxT("Failed to close connection gracefully."));
  153. }
  154. }
  155. return wxSocketClient::Close();
  156. }
  157. // ============================================================================
  158. // low level methods
  159. // ============================================================================
  160. wxSocketBase *wxFTP::AcceptIfActive(wxSocketBase *sock)
  161. {
  162. if ( m_bPassive )
  163. return sock;
  164. // now wait for a connection from server
  165. wxSocketServer *sockSrv = (wxSocketServer *)sock;
  166. if ( !sockSrv->WaitForAccept() )
  167. {
  168. m_lastError = wxPROTO_CONNERR;
  169. wxLogError(_("Timeout while waiting for FTP server to connect, try passive mode."));
  170. wxDELETE(sock);
  171. }
  172. else
  173. {
  174. m_lastError = wxPROTO_NOERR;
  175. sock = sockSrv->Accept(true);
  176. delete sockSrv;
  177. }
  178. return sock;
  179. }
  180. bool wxFTP::Abort()
  181. {
  182. if ( !m_streaming )
  183. return true;
  184. m_streaming = false;
  185. if ( !CheckCommand(wxT("ABOR"), '4') )
  186. return false;
  187. return CheckResult('2');
  188. }
  189. // ----------------------------------------------------------------------------
  190. // Send command to FTP server
  191. // ----------------------------------------------------------------------------
  192. char wxFTP::SendCommand(const wxString& command)
  193. {
  194. if ( m_streaming )
  195. {
  196. m_lastError = wxPROTO_STREAMING;
  197. return 0;
  198. }
  199. wxString tmp_str = command + wxT("\r\n");
  200. const wxWX2MBbuf tmp_buf = tmp_str.mb_str();
  201. if ( Write(wxMBSTRINGCAST tmp_buf, strlen(tmp_buf)).Error())
  202. {
  203. m_lastError = wxPROTO_NETERR;
  204. return 0;
  205. }
  206. // don't show the passwords in the logs (even in debug ones)
  207. wxString cmd, password;
  208. if ( command.Upper().StartsWith(wxT("PASS "), &password) )
  209. {
  210. cmd << wxT("PASS ") << wxString(wxT('*'), password.length());
  211. }
  212. else
  213. {
  214. cmd = command;
  215. }
  216. LogRequest(cmd);
  217. m_lastError = wxPROTO_NOERR;
  218. return GetResult();
  219. }
  220. // ----------------------------------------------------------------------------
  221. // Receive servers reply
  222. // ----------------------------------------------------------------------------
  223. char wxFTP::GetResult()
  224. {
  225. // if we've already had a read or write timeout error, the connection is
  226. // probably toast, so don't bother, it just wastes the users time
  227. if ( m_bEncounteredError )
  228. return 0;
  229. wxString code;
  230. // m_lastResult will contain the entire server response, possibly on
  231. // multiple lines
  232. m_lastResult.clear();
  233. // we handle multiline replies here according to RFC 959: it says that a
  234. // reply may either be on 1 line of the form "xyz ..." or on several lines
  235. // in whuch case it looks like
  236. // xyz-...
  237. // ...
  238. // xyz ...
  239. // and the intermeidate lines may start with xyz or not
  240. bool badReply = false;
  241. bool firstLine = true;
  242. bool endOfReply = false;
  243. while ( !endOfReply && !badReply )
  244. {
  245. wxString line;
  246. m_lastError = ReadLine(this,line);
  247. if ( m_lastError )
  248. {
  249. m_bEncounteredError = true;
  250. return 0;
  251. }
  252. LogResponse(line);
  253. if ( !m_lastResult.empty() )
  254. {
  255. // separate from last line
  256. m_lastResult += wxT('\n');
  257. }
  258. m_lastResult += line;
  259. // unless this is an intermediate line of a multiline reply, it must
  260. // contain the code in the beginning and '-' or ' ' following it
  261. if ( line.Len() < LEN_CODE + 1 )
  262. {
  263. if ( firstLine )
  264. {
  265. badReply = true;
  266. }
  267. }
  268. else // line has at least 4 chars
  269. {
  270. // this is the char which tells us what we're dealing with
  271. wxChar chMarker = line.GetChar(LEN_CODE);
  272. if ( firstLine )
  273. {
  274. code = wxString(line, LEN_CODE);
  275. switch ( chMarker )
  276. {
  277. case wxT(' '):
  278. endOfReply = true;
  279. break;
  280. case wxT('-'):
  281. firstLine = false;
  282. break;
  283. default:
  284. // unexpected
  285. badReply = true;
  286. }
  287. }
  288. else // subsequent line of multiline reply
  289. {
  290. if ( line.compare(0, LEN_CODE, code) == 0 )
  291. {
  292. if ( chMarker == wxT(' ') )
  293. {
  294. endOfReply = true;
  295. }
  296. }
  297. }
  298. }
  299. }
  300. if ( badReply )
  301. {
  302. wxLogDebug(wxT("Broken FTP server: '%s' is not a valid reply."),
  303. m_lastResult.c_str());
  304. m_lastError = wxPROTO_PROTERR;
  305. return 0;
  306. }
  307. else
  308. m_lastError = wxPROTO_NOERR;
  309. // if we got here we must have a non empty code string
  310. return (char)code[0u];
  311. }
  312. // ----------------------------------------------------------------------------
  313. // wxFTP simple commands
  314. // ----------------------------------------------------------------------------
  315. bool wxFTP::SetTransferMode(TransferMode transferMode)
  316. {
  317. if ( transferMode == m_currentTransfermode )
  318. {
  319. // nothing to do
  320. return true;
  321. }
  322. wxString mode;
  323. switch ( transferMode )
  324. {
  325. default:
  326. wxFAIL_MSG(wxT("unknown FTP transfer mode"));
  327. // fall through
  328. case BINARY:
  329. mode = wxT('I');
  330. break;
  331. case ASCII:
  332. mode = wxT('A');
  333. break;
  334. }
  335. if ( !DoSimpleCommand(wxT("TYPE"), mode) )
  336. {
  337. wxLogError(_("Failed to set FTP transfer mode to %s."),
  338. (transferMode == ASCII ? _("ASCII") : _("binary")));
  339. return false;
  340. }
  341. // If we get here the operation has been successfully completed
  342. // Set the status-member
  343. m_currentTransfermode = transferMode;
  344. return true;
  345. }
  346. bool wxFTP::DoSimpleCommand(const wxChar *command, const wxString& arg)
  347. {
  348. wxString fullcmd = command;
  349. if ( !arg.empty() )
  350. {
  351. fullcmd << wxT(' ') << arg;
  352. }
  353. if ( !CheckCommand(fullcmd, '2') )
  354. {
  355. wxLogDebug(wxT("FTP command '%s' failed."), fullcmd.c_str());
  356. m_lastError = wxPROTO_NETERR;
  357. return false;
  358. }
  359. m_lastError = wxPROTO_NOERR;
  360. return true;
  361. }
  362. bool wxFTP::ChDir(const wxString& dir)
  363. {
  364. // some servers might not understand ".." if they use different directory
  365. // tree conventions, but they always understand CDUP - should we use it if
  366. // dir == ".."? OTOH, do such servers (still) exist?
  367. return DoSimpleCommand(wxT("CWD"), dir);
  368. }
  369. bool wxFTP::MkDir(const wxString& dir)
  370. {
  371. return DoSimpleCommand(wxT("MKD"), dir);
  372. }
  373. bool wxFTP::RmDir(const wxString& dir)
  374. {
  375. return DoSimpleCommand(wxT("RMD"), dir);
  376. }
  377. wxString wxFTP::Pwd()
  378. {
  379. wxString path;
  380. if ( CheckCommand(wxT("PWD"), '2') )
  381. {
  382. // the result is at least that long if CheckCommand() succeeded
  383. wxString::const_iterator p = m_lastResult.begin() + LEN_CODE + 1;
  384. if ( *p != wxT('"') )
  385. {
  386. wxLogDebug(wxT("Missing starting quote in reply for PWD: %s"),
  387. wxString(p, m_lastResult.end()));
  388. }
  389. else
  390. {
  391. for ( ++p; (bool)*p; ++p ) // FIXME-DMARS
  392. {
  393. if ( *p == wxT('"') )
  394. {
  395. // check if the quote is doubled
  396. ++p;
  397. if ( !*p || *p != wxT('"') )
  398. {
  399. // no, this is the end
  400. break;
  401. }
  402. //else: yes, it is: this is an embedded quote in the
  403. // filename, treat as normal char
  404. }
  405. path += *p;
  406. }
  407. if ( !*p )
  408. {
  409. wxLogDebug(wxT("Missing ending quote in reply for PWD: %s"),
  410. m_lastResult.c_str() + LEN_CODE + 1);
  411. }
  412. }
  413. }
  414. else
  415. {
  416. m_lastError = wxPROTO_PROTERR;
  417. wxLogDebug(wxT("FTP PWD command failed."));
  418. }
  419. return path;
  420. }
  421. bool wxFTP::Rename(const wxString& src, const wxString& dst)
  422. {
  423. wxString str;
  424. str = wxT("RNFR ") + src;
  425. if ( !CheckCommand(str, '3') )
  426. return false;
  427. str = wxT("RNTO ") + dst;
  428. return CheckCommand(str, '2');
  429. }
  430. bool wxFTP::RmFile(const wxString& path)
  431. {
  432. wxString str;
  433. str = wxT("DELE ") + path;
  434. return CheckCommand(str, '2');
  435. }
  436. // ----------------------------------------------------------------------------
  437. // wxFTP port methods
  438. // ----------------------------------------------------------------------------
  439. wxSocketBase *wxFTP::GetPort()
  440. {
  441. /*
  442. PASSIVE: Client sends a "PASV" to the server. The server responds with
  443. an address and port number which it will be listening on. Then
  444. the client connects to the server at the specified address and
  445. port.
  446. ACTIVE: Client sends the server a PORT command which includes an
  447. address and port number which the client will be listening on.
  448. The server then connects to the client at that address and
  449. port.
  450. */
  451. wxSocketBase *socket = m_bPassive ? GetPassivePort() : GetActivePort();
  452. if ( !socket )
  453. {
  454. m_bEncounteredError = true;
  455. return NULL;
  456. }
  457. // Now set the time for the new socket to the default or user selected
  458. // timeout period
  459. socket->SetTimeout(m_uiDefaultTimeout);
  460. return socket;
  461. }
  462. wxString wxFTP::GetPortCmdArgument(const wxIPV4address& addrLocal,
  463. const wxIPV4address& addrNew)
  464. {
  465. // Just fills in the return value with the local IP
  466. // address of the current socket. Also it fill in the
  467. // PORT which the client will be listening on
  468. wxString addrIP = addrLocal.IPAddress();
  469. int portNew = addrNew.Service();
  470. // We need to break the PORT number in bytes
  471. addrIP.Replace(wxT("."), wxT(","));
  472. addrIP << wxT(',')
  473. << wxString::Format(wxT("%d"), portNew >> 8) << wxT(',')
  474. << wxString::Format(wxT("%d"), portNew & 0xff);
  475. // Now we have a value like "10,0,0,1,5,23"
  476. return addrIP;
  477. }
  478. wxSocketBase *wxFTP::GetActivePort()
  479. {
  480. // we need an address to listen on
  481. wxIPV4address addrNew, addrLocal;
  482. GetLocal(addrLocal);
  483. addrNew.AnyAddress();
  484. addrNew.Service(0); // pick an open port number.
  485. wxSocketServer *sockSrv = new wxSocketServer(addrNew);
  486. if (!sockSrv->IsOk())
  487. {
  488. // We use IsOk() here to see if everything is ok
  489. m_lastError = wxPROTO_PROTERR;
  490. delete sockSrv;
  491. return NULL;
  492. }
  493. //gets the new address, actually it is just the port number
  494. sockSrv->GetLocal(addrNew);
  495. // Now we create the argument of the PORT command, we send in both
  496. // addresses because the addrNew has an IP of "0.0.0.0", so we need the
  497. // value in addrLocal
  498. wxString port = GetPortCmdArgument(addrLocal, addrNew);
  499. if ( !DoSimpleCommand(wxT("PORT"), port) )
  500. {
  501. m_lastError = wxPROTO_PROTERR;
  502. delete sockSrv;
  503. wxLogError(_("The FTP server doesn't support the PORT command."));
  504. return NULL;
  505. }
  506. m_lastError = wxPROTO_NOERR;
  507. sockSrv->Notify(false); // Don't send any events
  508. return sockSrv;
  509. }
  510. wxSocketBase *wxFTP::GetPassivePort()
  511. {
  512. if ( !DoSimpleCommand(wxT("PASV")) )
  513. {
  514. m_lastError = wxPROTO_PROTERR;
  515. wxLogError(_("The FTP server doesn't support passive mode."));
  516. return NULL;
  517. }
  518. size_t addrStart = m_lastResult.find(wxT('('));
  519. size_t addrEnd = (addrStart == wxString::npos)
  520. ? wxString::npos
  521. : m_lastResult.find(wxT(')'), addrStart);
  522. if ( addrEnd == wxString::npos )
  523. {
  524. m_lastError = wxPROTO_PROTERR;
  525. return NULL;
  526. }
  527. // get the port number and address
  528. int a[6];
  529. wxString straddr(m_lastResult, addrStart + 1, addrEnd - (addrStart + 1));
  530. wxSscanf(straddr, wxT("%d,%d,%d,%d,%d,%d"),
  531. &a[2],&a[3],&a[4],&a[5],&a[0],&a[1]);
  532. wxUint32 hostaddr = (wxUint16)a[2] << 24 |
  533. (wxUint16)a[3] << 16 |
  534. (wxUint16)a[4] << 8 |
  535. a[5];
  536. wxUint16 port = (wxUint16)(a[0] << 8 | a[1]);
  537. wxIPV4address addr;
  538. addr.Hostname(hostaddr);
  539. addr.Service(port);
  540. wxSocketClient *client = new wxSocketClient();
  541. if ( !client->Connect(addr) )
  542. {
  543. m_lastError = wxPROTO_CONNERR;
  544. delete client;
  545. return NULL;
  546. }
  547. client->Notify(false);
  548. m_lastError = wxPROTO_NOERR;
  549. return client;
  550. }
  551. // ----------------------------------------------------------------------------
  552. // wxFTP download and upload
  553. // ----------------------------------------------------------------------------
  554. class wxInputFTPStream : public wxSocketInputStream
  555. {
  556. public:
  557. wxInputFTPStream(wxFTP *ftp, wxSocketBase *sock)
  558. : wxSocketInputStream(*sock)
  559. {
  560. m_ftp = ftp;
  561. // socket timeout automatically set in GetPort function
  562. }
  563. virtual ~wxInputFTPStream()
  564. {
  565. delete m_i_socket; // keep at top
  566. // when checking the result, the stream will
  567. // almost always show an error, even if the file was
  568. // properly transfered, thus, let's just grab the result
  569. // we are looking for "226 transfer completed"
  570. char code = m_ftp->GetResult();
  571. if ('2' == code)
  572. {
  573. // it was a good transfer.
  574. // we're done!
  575. m_ftp->m_streaming = false;
  576. return;
  577. }
  578. // did we timeout?
  579. if (0 == code)
  580. {
  581. // the connection is probably toast. issue an abort, and
  582. // then a close. there won't be any more waiting
  583. // for this connection
  584. m_ftp->Abort();
  585. m_ftp->Close();
  586. return;
  587. }
  588. // There was a problem with the transfer and the server
  589. // has acknowledged it. If we issue an "ABORT" now, the user
  590. // would get the "226" for the abort and think the xfer was
  591. // complete, thus, don't do anything here, just return
  592. }
  593. wxFTP *m_ftp;
  594. wxDECLARE_NO_COPY_CLASS(wxInputFTPStream);
  595. };
  596. class wxOutputFTPStream : public wxSocketOutputStream
  597. {
  598. public:
  599. wxOutputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
  600. : wxSocketOutputStream(*sock), m_ftp(ftp_clt)
  601. {
  602. }
  603. virtual ~wxOutputFTPStream(void)
  604. {
  605. if ( IsOk() )
  606. {
  607. // close data connection first, this will generate "transfer
  608. // completed" reply
  609. delete m_o_socket;
  610. // read this reply
  611. m_ftp->GetResult(); // save result so user can get to it
  612. m_ftp->m_streaming = false;
  613. }
  614. else
  615. {
  616. // abort data connection first
  617. m_ftp->Abort();
  618. // and close it after
  619. delete m_o_socket;
  620. }
  621. }
  622. wxFTP *m_ftp;
  623. wxDECLARE_NO_COPY_CLASS(wxOutputFTPStream);
  624. };
  625. wxInputStream *wxFTP::GetInputStream(const wxString& path)
  626. {
  627. if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
  628. {
  629. m_lastError = wxPROTO_CONNERR;
  630. return NULL;
  631. }
  632. wxSocketBase *sock = GetPort();
  633. if ( !sock )
  634. {
  635. m_lastError = wxPROTO_NETERR;
  636. return NULL;
  637. }
  638. wxString tmp_str = wxT("RETR ") + wxURI::Unescape(path);
  639. if ( !CheckCommand(tmp_str, '1') )
  640. return NULL;
  641. sock = AcceptIfActive(sock);
  642. if ( !sock )
  643. {
  644. m_lastError = wxPROTO_CONNERR;
  645. return NULL;
  646. }
  647. sock->SetFlags(wxSOCKET_WAITALL);
  648. m_streaming = true;
  649. wxInputFTPStream *in_stream = new wxInputFTPStream(this, sock);
  650. m_lastError = wxPROTO_NOERR;
  651. return in_stream;
  652. }
  653. wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
  654. {
  655. if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
  656. {
  657. m_lastError = wxPROTO_CONNERR;
  658. return NULL;
  659. }
  660. wxSocketBase *sock = GetPort();
  661. wxString tmp_str = wxT("STOR ") + path;
  662. if ( !CheckCommand(tmp_str, '1') )
  663. return NULL;
  664. sock = AcceptIfActive(sock);
  665. m_streaming = true;
  666. m_lastError = wxPROTO_NOERR;
  667. return new wxOutputFTPStream(this, sock);
  668. }
  669. // ----------------------------------------------------------------------------
  670. // FTP directory listing
  671. // ----------------------------------------------------------------------------
  672. bool wxFTP::GetList(wxArrayString& files,
  673. const wxString& wildcard,
  674. bool details)
  675. {
  676. wxSocketBase *sock = GetPort();
  677. if (!sock) {
  678. m_lastError = wxPROTO_NETERR;
  679. return false;
  680. }
  681. // NLST : List of Filenames (including Directory's !)
  682. // LIST : depending on BS of FTP-Server
  683. // - Unix : result like "ls" command
  684. // - Windows : like "dir" command
  685. // - others : ?
  686. wxString line(details ? wxT("LIST") : wxT("NLST"));
  687. if ( !wildcard.empty() )
  688. {
  689. line << wxT(' ') << wildcard;
  690. }
  691. if ( !CheckCommand(line, '1') )
  692. {
  693. m_lastError = wxPROTO_PROTERR;
  694. wxLogDebug(wxT("FTP 'LIST' command returned unexpected result from server"));
  695. delete sock;
  696. return false;
  697. }
  698. sock = AcceptIfActive(sock);
  699. if ( !sock ) {
  700. m_lastError = wxPROTO_CONNERR;
  701. return false;
  702. }
  703. files.Empty();
  704. while (ReadLine(sock, line) == wxPROTO_NOERR )
  705. {
  706. files.Add(line);
  707. }
  708. delete sock;
  709. // the file list should be terminated by "226 Transfer complete""
  710. m_lastError = wxPROTO_NOERR;
  711. return CheckResult('2');
  712. }
  713. bool wxFTP::FileExists(const wxString& fileName)
  714. {
  715. // This function checks if the file specified in fileName exists in the
  716. // current dir. It does so by simply doing an NLST (via GetList).
  717. // If this succeeds (and the list is not empty) the file exists.
  718. bool retval = false;
  719. wxArrayString fileList;
  720. if ( GetList(fileList, fileName, false) )
  721. {
  722. // Some ftp-servers (Ipswitch WS_FTP Server 1.0.5 does this)
  723. // displays this behaviour when queried on a nonexistent file:
  724. // NLST this_file_does_not_exist
  725. // 150 Opening ASCII data connection for directory listing
  726. // (no data transferred)
  727. // 226 Transfer complete
  728. // Here wxFTP::GetList(...) will succeed but it will return an empty
  729. // list.
  730. retval = !fileList.IsEmpty();
  731. }
  732. return retval;
  733. }
  734. // ----------------------------------------------------------------------------
  735. // FTP GetSize
  736. // ----------------------------------------------------------------------------
  737. int wxFTP::GetFileSize(const wxString& fileName)
  738. {
  739. // return the filesize of the given file if possible
  740. // return -1 otherwise (predominantly if file doesn't exist
  741. // in current dir)
  742. int filesize = -1;
  743. // Check for existance of file via wxFTP::FileExists(...)
  744. if ( FileExists(fileName) )
  745. {
  746. wxString command;
  747. // First try "SIZE" command using BINARY(IMAGE) transfermode
  748. // Especially UNIX ftp-servers distinguish between the different
  749. // transfermodes and reports different filesizes accordingly.
  750. // The BINARY size is the interesting one: How much memory
  751. // will we need to hold this file?
  752. TransferMode oldTransfermode = m_currentTransfermode;
  753. SetTransferMode(BINARY);
  754. command << wxT("SIZE ") << fileName;
  755. bool ok = CheckCommand(command, '2');
  756. if ( ok )
  757. {
  758. // The answer should be one line: "213 <filesize>\n"
  759. // 213 is File Status (STD9)
  760. // "SIZE" is not described anywhere..? It works on most servers
  761. int statuscode;
  762. if ( wxSscanf(GetLastResult().c_str(), wxT("%i %i"),
  763. &statuscode, &filesize) == 2 )
  764. {
  765. // We've gotten a good reply.
  766. ok = true;
  767. }
  768. else
  769. {
  770. // Something bad happened.. A "2yz" reply with no size
  771. // Fallback
  772. ok = false;
  773. }
  774. }
  775. // Set transfermode back to the original. Only the "SIZE"-command
  776. // is dependant on transfermode
  777. if ( oldTransfermode != NONE )
  778. {
  779. SetTransferMode(oldTransfermode);
  780. }
  781. // this is not a direct else clause.. The size command might return an
  782. // invalid "2yz" reply
  783. if ( !ok )
  784. {
  785. // The server didn't understand the "SIZE"-command or it
  786. // returned an invalid reply.
  787. // We now try to get details for the file with a "LIST"-command
  788. // and then parse the output from there..
  789. wxArrayString fileList;
  790. if ( GetList(fileList, fileName, true) )
  791. {
  792. if ( !fileList.IsEmpty() )
  793. {
  794. // We _should_ only get one line in return, but just to be
  795. // safe we run through the line(s) returned and look for a
  796. // substring containing the name we are looking for. We
  797. // stop the iteration at the first occurrence of the
  798. // filename. The search is not case-sensitive.
  799. const size_t numFiles = fileList.size();
  800. size_t i;
  801. for ( i = 0; i < fileList.GetCount(); i++ )
  802. {
  803. if ( fileList[i].Upper().Contains(fileName.Upper()) )
  804. break;
  805. }
  806. if ( i != numFiles )
  807. {
  808. // The index i points to the first occurrence of
  809. // fileName in the array Now we have to find out what
  810. // format the LIST has returned. There are two
  811. // "schools": Unix-like
  812. //
  813. // '-rw-rw-rw- owner group size month day time filename'
  814. //
  815. // or Windows-like
  816. //
  817. // 'date size filename'
  818. // check if the first character is '-'. This would
  819. // indicate Unix-style (this also limits this function
  820. // to searching for files, not directories)
  821. if ( fileList[i].Mid(0, 1) == wxT("-") )
  822. {
  823. if ( wxSscanf(fileList[i].c_str(),
  824. wxT("%*s %*s %*s %*s %i %*s %*s %*s %*s"),
  825. &filesize) != 9 )
  826. {
  827. // Hmm... Invalid response
  828. wxLogDebug(wxT("Invalid LIST response"));
  829. }
  830. }
  831. else // Windows-style response (?)
  832. {
  833. if ( wxSscanf(fileList[i].c_str(),
  834. wxT("%*s %*s %i %*s"),
  835. &filesize) != 4 )
  836. {
  837. // something bad happened..?
  838. wxLogDebug(wxT("Invalid or unknown LIST response"));
  839. }
  840. }
  841. }
  842. }
  843. }
  844. }
  845. }
  846. // filesize might still be -1 when exiting
  847. return filesize;
  848. }
  849. #endif // wxUSE_PROTOCOL_FTP