]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/ftp.cpp
OS/2 NativeFontInfo support
[wxWidgets.git] / src / common / ftp.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: 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// Created: 07/07/1997
11// RCS-ID: $Id$
12// Copyright: (c) 1997, 1998 Guilhem Lavaux
13// Licence: wxWindows license
14/////////////////////////////////////////////////////////////////////////////
15
16// ============================================================================
17// declarations
18// ============================================================================
19
20#ifdef __GNUG__
21 #pragma implementation "ftp.h"
22#endif
23
24// ----------------------------------------------------------------------------
25// headers
26// ----------------------------------------------------------------------------
27
28// For compilers that support precompilation, includes "wx.h".
29#include "wx/wxprec.h"
30
31#ifdef __BORLANDC__
32 #pragma hdrstop
33#endif
34
35#if wxUSE_PROTOCOL_FTP
36
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
44
45#include "wx/sckaddr.h"
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
52#if defined(__WXMAC__)
53 #include "wx/mac/macsock.h"
54#endif
55
56#ifndef __MWERKS__
57 #include <memory.h>
58#endif
59
60// ----------------------------------------------------------------------------
61// constants
62// ----------------------------------------------------------------------------
63
64// the length of FTP status code (3 digits)
65static const size_t LEN_CODE = 3;
66
67// ----------------------------------------------------------------------------
68// macros
69// ----------------------------------------------------------------------------
70
71IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
72IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), TRUE)
73
74// ============================================================================
75// implementation
76// ============================================================================
77
78// ----------------------------------------------------------------------------
79// wxFTP constructor and destructor
80// ----------------------------------------------------------------------------
81
82wxFTP::wxFTP()
83{
84 m_lastError = wxPROTO_NOERR;
85 m_streaming = FALSE;
86 m_currentTransfermode = NONE;
87
88 m_user = wxT("anonymous");
89 m_passwd << wxGetUserId() << wxT('@') << wxGetFullHostName();
90
91 SetNotify(0);
92 SetFlags(wxSOCKET_NONE);
93}
94
95wxFTP::~wxFTP()
96{
97 if ( m_streaming )
98 {
99 (void)Abort();
100 }
101
102 Close();
103}
104
105// ----------------------------------------------------------------------------
106// wxFTP connect and login methods
107// ----------------------------------------------------------------------------
108
109bool wxFTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
110{
111 if ( !wxProtocol::Connect(addr) )
112 {
113 m_lastError = wxPROTO_NETERR;
114 return FALSE;
115 }
116
117 if ( !m_user )
118 {
119 m_lastError = wxPROTO_CONNERR;
120 return FALSE;
121 }
122
123 // we should have 220 welcome message
124 if ( !CheckResult('2') )
125 {
126 Close();
127 return FALSE;
128 }
129
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 }
138
139 if ( rc != '3' )
140 {
141 Close();
142 return FALSE;
143 }
144
145 command.Printf(wxT("PASS %s"), m_passwd.c_str());
146 if ( !CheckCommand(command, '2') )
147 {
148 Close();
149 return FALSE;
150 }
151
152 return TRUE;
153}
154
155bool wxFTP::Connect(const wxString& host)
156{
157 wxIPV4address addr;
158 addr.Hostname(host);
159 addr.Service(wxT("ftp"));
160
161 return Connect(addr);
162}
163
164bool wxFTP::Close()
165{
166 if ( m_streaming )
167 {
168 m_lastError = wxPROTO_STREAMING;
169 return FALSE;
170 }
171
172 if ( IsConnected() )
173 {
174 if ( !CheckCommand(wxT("QUIT"), '2') )
175 {
176 wxLogDebug(_T("Failed to close connection gracefully."));
177 }
178 }
179
180 return wxSocketClient::Close();
181}
182
183// ============================================================================
184// low level methods
185// ============================================================================
186
187// ----------------------------------------------------------------------------
188// Send command to FTP server
189// ----------------------------------------------------------------------------
190
191char wxFTP::SendCommand(const wxString& command)
192{
193 if ( m_streaming )
194 {
195 m_lastError = wxPROTO_STREAMING;
196 return 0;
197 }
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
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__
221
222 return GetResult();
223}
224
225// ----------------------------------------------------------------------------
226// Recieve servers reply
227// ----------------------------------------------------------------------------
228
229char wxFTP::GetResult()
230{
231 wxString code;
232
233 // m_lastResult will contain the entire server response, possibly on
234 // multiple lines
235 m_lastResult.clear();
236
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 {
249 wxString line;
250 m_lastError = ReadLine(line);
251 if ( m_lastError )
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;
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
264 if ( line.Len() < LEN_CODE + 1 )
265 {
266 if ( firstLine )
267 {
268 badReply = TRUE;
269 }
270 else
271 {
272 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
273 code.c_str(), line.c_str());
274 }
275 }
276 else // line has at least 4 chars
277 {
278 // this is the char which tells us what we're dealing with
279 wxChar chMarker = line.GetChar(LEN_CODE);
280
281 if ( firstLine )
282 {
283 code = wxString(line, LEN_CODE);
284 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
285 code.c_str(), line.c_str() + LEN_CODE + 1);
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 {
304 if ( wxStrncmp(line, code, LEN_CODE) == 0 )
305 {
306 if ( chMarker == _T(' ') )
307 {
308 endOfReply = TRUE;
309 }
310
311 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
312 code.c_str(), line.c_str() + LEN_CODE + 1);
313 }
314 else
315 {
316 // just part of reply
317 wxLogTrace(FTP_TRACE_MASK, _T("<== %s %s"),
318 code.c_str(), line.c_str());
319 }
320 }
321 }
322 }
323
324 if ( badReply )
325 {
326 wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
327 m_lastResult.c_str());
328
329 m_lastError = wxPROTO_PROTERR;
330
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{
344 if ( transferMode == m_currentTransfermode )
345 {
346 // nothing to do
347 return TRUE;
348 }
349
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 {
368 wxLogError(_("Failed to set FTP transfer mode to %s."), (const wxChar*)
369 (transferMode == ASCII ? _("ASCII") : _("binary")));
370
371 return FALSE;
372 }
373
374 // If we get here the operation has been succesfully completed
375 // Set the status-member
376 m_currentTransfermode = transferMode;
377
378 return TRUE;
379}
380
381bool wxFTP::DoSimpleCommand(const wxChar *command, const wxString& arg)
382{
383 wxString fullcmd = command;
384 if ( !arg.empty() )
385 {
386 fullcmd << _T(' ') << arg;
387 }
388
389 if ( !CheckCommand(fullcmd, '2') )
390 {
391 wxLogDebug(_T("FTP command '%s' failed."), fullcmd.c_str());
392
393 return FALSE;
394 }
395
396 return TRUE;
397}
398
399bool wxFTP::ChDir(const wxString& dir)
400{
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?
404
405 return DoSimpleCommand(_T("CWD"), dir);
406}
407
408bool wxFTP::MkDir(const wxString& dir)
409{
410 return DoSimpleCommand(_T("MKD"), dir);
411}
412
413bool wxFTP::RmDir(const wxString& dir)
414{
415 return DoSimpleCommand(_T("RMD"), dir);
416}
417
418wxString wxFTP::Pwd()
419{
420 wxString path;
421
422 if ( CheckCommand(wxT("PWD"), '2') )
423 {
424 // the result is at least that long if CheckCommand() succeeded
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 }
457 else
458 {
459 wxLogDebug(_T("FTP PWD command failed."));
460 }
461
462 return path;
463}
464
465bool wxFTP::Rename(const wxString& src, const wxString& dst)
466{
467 wxString str;
468
469 str = wxT("RNFR ") + src;
470 if ( !CheckCommand(str, '3') )
471 return FALSE;
472
473 str = wxT("RNTO ") + dst;
474
475 return CheckCommand(str, '2');
476}
477
478bool wxFTP::RmFile(const wxString& path)
479{
480 wxString str;
481 str = wxT("DELE ") + path;
482
483 return CheckCommand(str, '2');
484}
485
486// ----------------------------------------------------------------------------
487// wxFTP download and upload
488// ----------------------------------------------------------------------------
489
490class wxInputFTPStream : public wxSocketInputStream
491{
492public:
493 wxInputFTPStream(wxFTP *ftp, wxSocketBase *sock)
494 : wxSocketInputStream(*sock)
495 {
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
502 }
503
504 size_t GetSize() const { return m_ftpsize; }
505
506 virtual ~wxInputFTPStream()
507 {
508 delete m_i_socket;
509
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 }
521
522 // delete m_i_socket; // moved to top of destructor to accomodate wu-FTPd >= 2.6.0
523 }
524
525 wxFTP *m_ftp;
526 size_t m_ftpsize;
527};
528
529class wxOutputFTPStream : public wxSocketOutputStream
530{
531public:
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;
561};
562
563wxSocketClient *wxFTP::GetPort()
564{
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
574 const wxChar *addrStart = wxStrchr(m_lastResult, _T('('));
575 if ( !addrStart )
576 {
577 m_lastError = wxPROTO_PROTERR;
578
579 return NULL;
580 }
581
582 const wxChar *addrEnd = wxStrchr(addrStart, _T(')'));
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;
615}
616
617bool wxFTP::Abort()
618{
619 if ( !m_streaming )
620 return TRUE;
621
622 m_streaming = FALSE;
623 if ( !CheckCommand(wxT("ABOR"), '4') )
624 return FALSE;
625
626 return CheckResult('2');
627}
628
629wxInputStream *wxFTP::GetInputStream(const wxString& path)
630{
631 int pos_size;
632 wxInputFTPStream *in_stream;
633
634 if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
635 return NULL;
636
637 wxSocketClient *sock = GetPort();
638
639 if ( !sock )
640 {
641 m_lastError = wxPROTO_NETERR;
642 return NULL;
643 }
644
645 wxString tmp_str = wxT("RETR ") + wxURL::ConvertFromURI(path);
646 if ( !CheckCommand(tmp_str, '1') )
647 return NULL;
648
649 m_streaming = TRUE;
650
651 in_stream = new wxInputFTPStream(this, sock);
652
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);
657
658 in_stream->m_ftpsize = wxAtoi(WXSTRINGCAST str_size);
659 }
660
661 sock->SetFlags(wxSOCKET_WAITALL);
662
663 return in_stream;
664}
665
666wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
667{
668 if ( ( m_currentTransfermode == NONE ) && !SetTransferMode(BINARY) )
669 return NULL;
670
671 wxSocketClient *sock = GetPort();
672
673 wxString tmp_str = wxT("STOR ") + path;
674 if ( !CheckCommand(tmp_str, '1') )
675 return FALSE;
676
677 m_streaming = TRUE;
678
679 return new wxOutputFTPStream(this, sock);
680}
681
682// ----------------------------------------------------------------------------
683// FTP directory listing
684// ----------------------------------------------------------------------------
685
686bool wxFTP::GetList(wxArrayString& files,
687 const wxString& wildcard,
688 bool details)
689{
690 wxSocketBase *sock = GetPort();
691 if (!sock)
692 return FALSE;
693
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"));
700 if ( !!wildcard )
701 {
702 line << _T(' ') << wildcard;
703 }
704
705 if (!CheckCommand(line, '1'))
706 {
707 return FALSE;
708 }
709 files.Empty();
710 while ( ReadLine(sock, line) == wxPROTO_NOERR )
711 {
712 files.Add(line);
713 }
714 delete sock;
715
716 // the file list should be terminated by "226 Transfer complete""
717 if ( !CheckResult('2') )
718 return FALSE;
719
720 return TRUE;
721}
722
723bool wxFTP::FileExists(const wxString& fileName)
724{
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
740 // Here wxFTP::GetList(...) will succeed but it will return an empty
741 // list.
742 retval = !fileList.IsEmpty();
743 }
744
745 return retval;
746}
747
748// ----------------------------------------------------------------------------
749// FTP GetSize
750// ----------------------------------------------------------------------------
751
752int wxFTP::GetFileSize(const wxString& fileName)
753{
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;
759
760 // Check for existance of file via wxFTP::FileExists(...)
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?
770 TransferMode oldTransfermode = m_currentTransfermode;
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)
780 // "SIZE" is not described anywhere..? It works on most servers
781 int statuscode;
782 if ( wxSscanf(GetLastResult().c_str(), _T("%i %i"),
783 &statuscode, &filesize) == 2 )
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 }
794 }
795
796 // Set transfermode back to the original. Only the "SIZE"-command
797 // is dependant on transfermode
798 if ( oldTransfermode != NONE )
799 {
800 SetTransferMode(oldTransfermode);
801 }
802
803 if ( !ok ) // this is not a direct else clause.. The size command might return an invalid "2yz" reply
804 {
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 {
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.
819 bool foundIt = FALSE;
820
821 size_t i;
822 for ( i = 0; !foundIt && i < fileList.Count(); i++ )
823 {
824 foundIt = fileList[i].Upper().Contains(fileName.Upper());
825 }
826
827 if ( foundIt )
828 {
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("-") )
844 {
845
846 if ( wxSscanf(fileList[i].c_str(),
847 _T("%*s %*s %*s %*s %i %*s %*s %*s %*s"),
848 &filesize) == 9 )
849 {
850 // We've gotten a good response
851 ok = TRUE;
852 }
853 else
854 {
855 // Hmm... Invalid response
856 wxLogTrace(FTP_TRACE_MASK,
857 _T("Invalid LIST response"));
858 }
859 }
860 else // Windows-style response (?)
861 {
862 if ( wxSscanf(fileList[i].c_str(),
863 _T("%*s %*s %i %*s"),
864 &filesize) == 4 )
865 {
866 // valid response
867 ok = TRUE;
868 }
869 else
870 {
871 // something bad happened..?
872 wxLogTrace(FTP_TRACE_MASK,
873 _T("Invalid or unknown LIST response"));
874 }
875 }
876 }
877 }
878 }
879 }
880 }
881
882 // filesize might still be -1 when exiting
883 return filesize;
884}
885
886
887#if WXWIN_COMPATIBILITY_2
888// deprecated
889wxList *wxFTP::GetList(const wxString& wildcard, bool details)
890{
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;
925}
926#endif // WXWIN_COMPATIBILITY_2
927
928#endif // wxUSE_PROTOCOL_FTP
929