]>
git.saurik.com Git - wxWidgets.git/blob - src/common/ftp.cpp
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: FTP protocol
4 // Author: Guilhem Lavaux
8 // Copyright: (c) 1997, 1998 Guilhem Lavaux
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "ftp.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
28 #if defined(__WXMAC__)
29 #include "/wx/mac/macsock.h"
33 #include "wx/string.h"
35 #include "wx/sckaddr.h"
36 #include "wx/socket.h"
38 #include "wx/sckstrm.h"
39 #include "wx/protocol/protocol.h"
40 #include "wx/protocol/ftp.h"
47 // the length of FTP status code (3 digits)
48 static const size_t LEN_CODE
= 3;
50 #define FTP_BSIZE 1024
52 IMPLEMENT_DYNAMIC_CLASS(wxFTP
, wxProtocol
)
53 IMPLEMENT_PROTOCOL(wxFTP
, wxT("ftp"), wxT("ftp"), TRUE
)
55 ////////////////////////////////////////////////////////////////
56 ////// wxFTP constructor and destructor ////////////////////////
57 ////////////////////////////////////////////////////////////////
62 m_lastError
= wxPROTO_NOERR
;
65 m_user
= wxT("anonymous");
66 m_passwd
<< wxGetUserId() << wxT('@') << wxGetFullHostName();
69 SetFlags(wxSOCKET_NONE
);
77 ////////////////////////////////////////////////////////////////
78 ////// wxFTP connect and login methods /////////////////////////
79 ////////////////////////////////////////////////////////////////
80 bool wxFTP::Connect(wxSockAddress
& addr
, bool WXUNUSED(wait
))
82 if (!wxProtocol::Connect(addr
)) {
83 m_lastError
= wxPROTO_NETERR
;
87 if (!m_user
|| !m_passwd
) {
88 m_lastError
= wxPROTO_CONNERR
;
94 if (!GetResult('2')) {
99 command
.sprintf(wxT("USER %s"), (const wxChar
*)m_user
);
100 if (!SendCommand(command
, '3')) {
105 command
.sprintf(wxT("PASS %s"), (const wxChar
*)m_passwd
);
106 if (!SendCommand(command
, '2')) {
114 bool wxFTP::Connect(const wxString
& host
)
117 wxString my_host
= host
;
119 addr
.Hostname(my_host
);
120 addr
.Service(wxT("ftp"));
122 return Connect(addr
);
125 bool wxFTP::Close(bool force
)
131 m_lastError
= wxPROTO_STREAMING
;
139 SendCommand(wxT("QUIT"), '2');
141 return wxSocketClient::Close();
144 // ============================================================================
146 // ============================================================================
148 // ----------------------------------------------------------------------------
149 // Send command to FTP server
150 // ----------------------------------------------------------------------------
152 bool wxFTP::SendCommand(const wxString
& command
, char exp_ret
)
158 m_lastError
= wxPROTO_STREAMING
;
162 tmp_str
= command
+ wxT("\r\n");
163 const wxWX2MBbuf tmp_buf
= tmp_str
.mb_str();
164 if ( Write(wxMBSTRINGCAST tmp_buf
, strlen(tmp_buf
)).Error())
166 m_lastError
= wxPROTO_NETERR
;
170 wxLogTrace(_T("ftp"), _T("==> %s"), command
.c_str());
172 return GetResult(exp_ret
);
175 // ----------------------------------------------------------------------------
176 // Recieve servers reply
177 // ----------------------------------------------------------------------------
179 bool wxFTP::GetResult(char exp
)
183 // we handle multiline replies here according to RFC 959: it says that a
184 // reply may either be on 1 line of the form "xyz ..." or on several lines
185 // in whuch case it looks like
189 // and the intermeidate lines may start with xyz or not
190 bool badReply
= FALSE
;
191 bool firstLine
= TRUE
;
192 bool endOfReply
= FALSE
;
193 while ( !endOfReply
&& !badReply
)
195 m_lastError
= ReadLine(m_lastResult
);
199 // unless this is an intermediate line of a multiline reply, it must
200 // contain the code in the beginning and '-' or ' ' following it
201 if ( m_lastResult
.Len() < LEN_CODE
+ 1 )
209 wxLogTrace(_T("ftp"), _T("<== %s %s"),
210 code
.c_str(), m_lastResult
.c_str());
213 else // line has at least 4 chars
215 // this is the char which tells us what we're dealing with
216 wxChar chMarker
= m_lastResult
.GetChar(LEN_CODE
);
220 code
= wxString(m_lastResult
, LEN_CODE
);
221 wxLogTrace(_T("ftp"), _T("<== %s %s"),
222 code
.c_str(), m_lastResult
.c_str() + LEN_CODE
+ 1);
239 else // subsequent line of multiline reply
241 if ( wxStrncmp(m_lastResult
, code
, LEN_CODE
) == 0 )
243 if ( chMarker
== _T(' ') )
248 wxLogTrace(_T("ftp"), _T("<== %s %s"),
249 code
.c_str(), m_lastResult
.c_str() + LEN_CODE
+ 1);
253 // just part of reply
254 wxLogTrace(_T("ftp"), _T("<== %s %s"),
255 code
.c_str(), m_lastResult
.c_str());
263 wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
264 m_lastResult
.c_str());
266 m_lastError
= wxPROTO_PROTERR
;
271 if ( code
.GetChar(0) != exp
)
273 m_lastError
= wxPROTO_PROTERR
;
281 ////////////////////////////////////////////////////////////////
282 ////// wxFTP low-level methods /////////////////////////////////
283 ////////////////////////////////////////////////////////////////
284 bool wxFTP::ChDir(const wxString
& dir
)
288 str
.Prepend(wxT("CWD "));
289 return SendCommand(str
, '2');
292 bool wxFTP::MkDir(const wxString
& dir
)
295 str
.Prepend(wxT("MKD "));
296 return SendCommand(str
, '2');
299 bool wxFTP::RmDir(const wxString
& dir
)
303 str
.Prepend(wxT("PWD "));
304 return SendCommand(str
, '2');
307 wxString
wxFTP::Pwd()
311 if ( SendCommand(wxT("PWD"), '2') )
313 // the result is at least that long if SendCommand() succeeded
314 const wxChar
*p
= m_lastResult
.c_str() + LEN_CODE
+ 1;
317 wxLogDebug(_T("Missing starting quote in reply for PWD: %s"), p
);
325 // check if the quote is doubled
327 if ( !*p
|| *p
!= _T('"') )
329 // no, this is the end
332 //else: yes, it is: this is an embedded quote in the
333 // filename, treat as normal char
341 wxLogDebug(_T("Missing ending quote in reply for PWD: %s"),
342 m_lastResult
.c_str() + LEN_CODE
+ 1);
350 bool wxFTP::Rename(const wxString
& src
, const wxString
& dst
)
354 str
= wxT("RNFR ") + src
;
355 if (!SendCommand(str
, '3'))
358 str
= wxT("RNTO ") + dst
;
359 return SendCommand(str
, '2');
362 bool wxFTP::RmFile(const wxString
& path
)
368 return SendCommand(str
, '2');
371 ////////////////////////////////////////////////////////////////
372 ////// wxFTP download*upload ///////////////////////////////////
373 ////////////////////////////////////////////////////////////////
375 class wxInputFTPStream
: public wxSocketInputStream
{
380 wxInputFTPStream(wxFTP
*ftp_clt
, wxSocketBase
*sock
)
381 : wxSocketInputStream(*sock
), m_ftp(ftp_clt
) {}
382 size_t GetSize() const { return m_ftpsize
; }
383 virtual ~wxInputFTPStream(void)
385 if (LastError() == wxStream_NOERROR
)
386 m_ftp
->GetResult('2');
393 class wxOutputFTPStream
: public wxSocketOutputStream
{
397 wxOutputFTPStream(wxFTP
*ftp_clt
, wxSocketBase
*sock
)
398 : wxSocketOutputStream(*sock
), m_ftp(ftp_clt
) {}
399 virtual ~wxOutputFTPStream(void)
401 if (LastError() != wxStream_NOERROR
)
402 m_ftp
->GetResult('2');
409 wxSocketClient
*wxFTP::GetPort()
412 wxSocketClient
*client
;
419 if (!SendCommand(wxT("PASV"), '2'))
422 addr_pos
= m_lastResult
.Find(wxT('('));
423 if (addr_pos
== -1) {
424 m_lastError
= wxPROTO_PROTERR
;
427 straddr
= m_lastResult(addr_pos
+1, m_lastResult
.Length());
428 wxSscanf((const wxChar
*)straddr
,wxT("%d,%d,%d,%d,%d,%d"),&a
[2],&a
[3],&a
[4],&a
[5],&a
[0],&a
[1]);
430 hostaddr
= (wxUint16
)a
[5] << 24 | (wxUint16
)a
[4] << 16 |
431 (wxUint16
)a
[3] << 8 | a
[2];
432 addr
.Hostname(hostaddr
);
434 port
= (wxUint16
)a
[0] << 8 | a
[1];
437 client
= new wxSocketClient();
438 if (!client
->Connect(addr
)) {
442 client
->Notify(FALSE
);
453 if ( !SendCommand(wxT("ABOR"), '4') )
456 return GetResult('2');
459 wxInputStream
*wxFTP::GetInputStream(const wxString
& path
)
463 wxInputFTPStream
*in_stream
;
465 if (!SendCommand(wxT("TYPE I"), '2'))
468 wxSocketClient
*sock
= GetPort();
471 m_lastError
= wxPROTO_NETERR
;
475 tmp_str
= wxT("RETR ") + wxURL::ConvertFromURI(path
);
476 if (!SendCommand(tmp_str
, '1'))
479 in_stream
= new wxInputFTPStream(this, sock
);
481 pos_size
= m_lastResult
.Index(wxT('('));
482 if (pos_size
!= wxNOT_FOUND
) {
483 wxString str_size
= m_lastResult(pos_size
+1, m_lastResult
.Index(wxT(')'))-1);
485 in_stream
->m_ftpsize
= wxAtoi(WXSTRINGCAST str_size
);
487 sock
->SetFlags(wxSOCKET_WAITALL
);
492 wxOutputStream
*wxFTP::GetOutputStream(const wxString
& path
)
496 if (!SendCommand(wxT("TYPE I"), '2'))
499 wxSocketClient
*sock
= GetPort();
501 tmp_str
= wxT("STOR ") + path
;
502 if (!SendCommand(tmp_str
, '1'))
505 return new wxOutputFTPStream(this, sock
);
508 bool wxFTP::GetList(wxArrayString
& files
, const wxString
& wildcard
)
510 wxSocketBase
*sock
= GetPort();
516 wxString line
= _T("NLST");
519 // notice that there is no space here
523 if ( !SendCommand(line
, '1') )
530 while ( ReadLine(sock
, line
) == wxPROTO_NOERR
)
537 // the file list should be terminated by "226 Transfer complete""
538 if ( !GetResult('2') )
544 wxList
*wxFTP::GetList(const wxString
& wildcard
)
546 wxList
*file_list
= new wxList
;
547 wxSocketBase
*sock
= GetPort();
548 wxString tmp_str
= wxT("NLST");
550 if (!wildcard
.IsNull())
553 if (!SendCommand(tmp_str
, '1')) {
559 while (GetLine(sock
, tmp_str
) == wxPROTO_NOERR
) {
560 file_list
->Append((wxObject
*)(new wxString(tmp_str
)));
563 if (!GetResult('2')) {
565 file_list
->DeleteContents(TRUE
);