]>
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"
25 #if wxUSE_SOCKETS && wxUSE_STREAMS
30 #if defined(__WXMAC__)
31 #include "/wx/mac/macsock.h"
35 #include "wx/string.h"
37 #include "wx/sckaddr.h"
38 #include "wx/socket.h"
40 #include "wx/sckstrm.h"
41 #include "wx/protocol/protocol.h"
42 #include "wx/protocol/ftp.h"
49 // the length of FTP status code (3 digits)
50 static const size_t LEN_CODE
= 3;
52 #define FTP_BSIZE 1024
54 IMPLEMENT_DYNAMIC_CLASS(wxFTP
, wxProtocol
)
55 IMPLEMENT_PROTOCOL(wxFTP
, wxT("ftp"), wxT("ftp"), TRUE
)
57 ////////////////////////////////////////////////////////////////
58 ////// wxFTP constructor and destructor ////////////////////////
59 ////////////////////////////////////////////////////////////////
64 m_lastError
= wxPROTO_NOERR
;
67 m_user
= wxT("anonymous");
68 m_passwd
<< wxGetUserId() << wxT('@') << wxGetFullHostName();
71 SetFlags(wxSOCKET_NONE
);
84 ////////////////////////////////////////////////////////////////
85 ////// wxFTP connect and login methods /////////////////////////
86 ////////////////////////////////////////////////////////////////
87 bool wxFTP::Connect(wxSockAddress
& addr
, bool WXUNUSED(wait
))
89 if (!wxProtocol::Connect(addr
)) {
90 m_lastError
= wxPROTO_NETERR
;
94 if (!m_user
|| !m_passwd
) {
95 m_lastError
= wxPROTO_CONNERR
;
101 if (!GetResult('2')) {
106 command
.sprintf(wxT("USER %s"), (const wxChar
*)m_user
);
107 if (!SendCommand(command
, '3')) {
112 command
.sprintf(wxT("PASS %s"), (const wxChar
*)m_passwd
);
113 if (!SendCommand(command
, '2')) {
121 bool wxFTP::Connect(const wxString
& host
)
124 wxString my_host
= host
;
126 addr
.Hostname(my_host
);
127 addr
.Service(wxT("ftp"));
129 return Connect(addr
);
136 m_lastError
= wxPROTO_STREAMING
;
141 SendCommand(wxT("QUIT"), '2');
143 return wxSocketClient::Close();
146 // ============================================================================
148 // ============================================================================
150 // ----------------------------------------------------------------------------
151 // Send command to FTP server
152 // ----------------------------------------------------------------------------
154 bool wxFTP::SendCommand(const wxString
& command
, char exp_ret
)
160 m_lastError
= wxPROTO_STREAMING
;
164 tmp_str
= command
+ wxT("\r\n");
165 const wxWX2MBbuf tmp_buf
= tmp_str
.mb_str();
166 if ( Write(wxMBSTRINGCAST tmp_buf
, strlen(tmp_buf
)).Error())
168 m_lastError
= wxPROTO_NETERR
;
172 wxLogTrace(_T("ftp"), _T("==> %s"), command
.c_str());
174 return GetResult(exp_ret
);
177 // ----------------------------------------------------------------------------
178 // Recieve servers reply
179 // ----------------------------------------------------------------------------
181 bool wxFTP::GetResult(char exp
)
185 // we handle multiline replies here according to RFC 959: it says that a
186 // reply may either be on 1 line of the form "xyz ..." or on several lines
187 // in whuch case it looks like
191 // and the intermeidate lines may start with xyz or not
192 bool badReply
= FALSE
;
193 bool firstLine
= TRUE
;
194 bool endOfReply
= FALSE
;
195 while ( !endOfReply
&& !badReply
)
197 m_lastError
= ReadLine(m_lastResult
);
201 // unless this is an intermediate line of a multiline reply, it must
202 // contain the code in the beginning and '-' or ' ' following it
203 if ( m_lastResult
.Len() < LEN_CODE
+ 1 )
211 wxLogTrace(_T("ftp"), _T("<== %s %s"),
212 code
.c_str(), m_lastResult
.c_str());
215 else // line has at least 4 chars
217 // this is the char which tells us what we're dealing with
218 wxChar chMarker
= m_lastResult
.GetChar(LEN_CODE
);
222 code
= wxString(m_lastResult
, LEN_CODE
);
223 wxLogTrace(_T("ftp"), _T("<== %s %s"),
224 code
.c_str(), m_lastResult
.c_str() + LEN_CODE
+ 1);
241 else // subsequent line of multiline reply
243 if ( wxStrncmp(m_lastResult
, code
, LEN_CODE
) == 0 )
245 if ( chMarker
== _T(' ') )
250 wxLogTrace(_T("ftp"), _T("<== %s %s"),
251 code
.c_str(), m_lastResult
.c_str() + LEN_CODE
+ 1);
255 // just part of reply
256 wxLogTrace(_T("ftp"), _T("<== %s %s"),
257 code
.c_str(), m_lastResult
.c_str());
265 wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
266 m_lastResult
.c_str());
268 m_lastError
= wxPROTO_PROTERR
;
273 if ( code
.GetChar(0) != exp
)
275 m_lastError
= wxPROTO_PROTERR
;
283 ////////////////////////////////////////////////////////////////
284 ////// wxFTP low-level methods /////////////////////////////////
285 ////////////////////////////////////////////////////////////////
286 bool wxFTP::ChDir(const wxString
& dir
)
290 str
.Prepend(wxT("CWD "));
291 return SendCommand(str
, '2');
294 bool wxFTP::MkDir(const wxString
& dir
)
297 str
.Prepend(wxT("MKD "));
298 return SendCommand(str
, '2');
301 bool wxFTP::RmDir(const wxString
& dir
)
305 str
.Prepend(wxT("PWD "));
306 return SendCommand(str
, '2');
309 wxString
wxFTP::Pwd()
313 if ( SendCommand(wxT("PWD"), '2') )
315 // the result is at least that long if SendCommand() succeeded
316 const wxChar
*p
= m_lastResult
.c_str() + LEN_CODE
+ 1;
319 wxLogDebug(_T("Missing starting quote in reply for PWD: %s"), p
);
327 // check if the quote is doubled
329 if ( !*p
|| *p
!= _T('"') )
331 // no, this is the end
334 //else: yes, it is: this is an embedded quote in the
335 // filename, treat as normal char
343 wxLogDebug(_T("Missing ending quote in reply for PWD: %s"),
344 m_lastResult
.c_str() + LEN_CODE
+ 1);
352 bool wxFTP::Rename(const wxString
& src
, const wxString
& dst
)
356 str
= wxT("RNFR ") + src
;
357 if (!SendCommand(str
, '3'))
360 str
= wxT("RNTO ") + dst
;
361 return SendCommand(str
, '2');
364 bool wxFTP::RmFile(const wxString
& path
)
370 return SendCommand(str
, '2');
373 ////////////////////////////////////////////////////////////////
374 ////// wxFTP download*upload ///////////////////////////////////
375 ////////////////////////////////////////////////////////////////
377 class wxInputFTPStream
: public wxSocketInputStream
{
382 wxInputFTPStream(wxFTP
*ftp_clt
, wxSocketBase
*sock
)
383 : wxSocketInputStream(*sock
), m_ftp(ftp_clt
) {}
384 size_t GetSize() const { return m_ftpsize
; }
385 virtual ~wxInputFTPStream(void)
387 if (LastError() == wxStream_NOERROR
)
388 m_ftp
->GetResult('2');
395 class wxOutputFTPStream
: public wxSocketOutputStream
{
399 wxOutputFTPStream(wxFTP
*ftp_clt
, wxSocketBase
*sock
)
400 : wxSocketOutputStream(*sock
), m_ftp(ftp_clt
) {}
401 virtual ~wxOutputFTPStream(void)
405 // close data connection first, this will generate "transfer
410 m_ftp
->GetResult('2');
414 // abort data connection first
417 // and close it after
423 wxSocketClient
*wxFTP::GetPort()
426 wxSocketClient
*client
;
433 if (!SendCommand(wxT("PASV"), '2'))
436 addr_pos
= m_lastResult
.Find(wxT('('));
437 if (addr_pos
== -1) {
438 m_lastError
= wxPROTO_PROTERR
;
441 straddr
= m_lastResult(addr_pos
+1, m_lastResult
.Length());
442 wxSscanf((const wxChar
*)straddr
,wxT("%d,%d,%d,%d,%d,%d"),&a
[2],&a
[3],&a
[4],&a
[5],&a
[0],&a
[1]);
444 hostaddr
= (wxUint16
)a
[5] << 24 | (wxUint16
)a
[4] << 16 |
445 (wxUint16
)a
[3] << 8 | a
[2];
446 addr
.Hostname(hostaddr
);
448 port
= (wxUint16
)a
[0] << 8 | a
[1];
451 client
= new wxSocketClient();
452 if (!client
->Connect(addr
)) {
456 client
->Notify(FALSE
);
467 if ( !SendCommand(wxT("ABOR"), '4') )
470 return GetResult('2');
473 wxInputStream
*wxFTP::GetInputStream(const wxString
& path
)
477 wxInputFTPStream
*in_stream
;
479 if (!SendCommand(wxT("TYPE I"), '2'))
482 wxSocketClient
*sock
= GetPort();
485 m_lastError
= wxPROTO_NETERR
;
489 tmp_str
= wxT("RETR ") + wxURL::ConvertFromURI(path
);
490 if (!SendCommand(tmp_str
, '1'))
493 in_stream
= new wxInputFTPStream(this, sock
);
495 pos_size
= m_lastResult
.Index(wxT('('));
496 if (pos_size
!= wxNOT_FOUND
) {
497 wxString str_size
= m_lastResult(pos_size
+1, m_lastResult
.Index(wxT(')'))-1);
499 in_stream
->m_ftpsize
= wxAtoi(WXSTRINGCAST str_size
);
501 sock
->SetFlags(wxSOCKET_WAITALL
);
506 wxOutputStream
*wxFTP::GetOutputStream(const wxString
& path
)
510 if (!SendCommand(wxT("TYPE I"), '2'))
513 wxSocketClient
*sock
= GetPort();
515 tmp_str
= wxT("STOR ") + path
;
516 if (!SendCommand(tmp_str
, '1'))
519 return new wxOutputFTPStream(this, sock
);
522 bool wxFTP::GetList(wxArrayString
& files
, const wxString
& wildcard
)
524 wxSocketBase
*sock
= GetPort();
530 wxString line
= _T("NLST");
533 // notice that there is no space here
537 if ( !SendCommand(line
, '1') )
544 while ( ReadLine(sock
, line
) == wxPROTO_NOERR
)
551 // the file list should be terminated by "226 Transfer complete""
552 if ( !GetResult('2') )
558 wxList
*wxFTP::GetList(const wxString
& wildcard
)
560 wxList
*file_list
= new wxList
;
561 wxSocketBase
*sock
= GetPort();
562 wxString tmp_str
= wxT("NLST");
564 if (!wildcard
.IsNull())
567 if (!SendCommand(tmp_str
, '1')) {
573 while (GetLine(sock
, tmp_str
) == wxPROTO_NOERR
) {
574 file_list
->Append((wxObject
*)(new wxString(tmp_str
)));
577 if (!GetResult('2')) {
579 file_list
->DeleteContents(TRUE
);