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