]> git.saurik.com Git - wxWidgets.git/blame - src/common/ftp.cpp
More debug code
[wxWidgets.git] / src / common / ftp.cpp
CommitLineData
f4ada568
GL
1/////////////////////////////////////////////////////////////////////////////
2// Name: ftp.cpp
3// Purpose: FTP protocol
4// Author: Guilhem Lavaux
5// Modified by:
6// Created: 07/07/1997
7// RCS-ID: $Id$
8// Copyright: (c) 1997, 1998 Guilhem Lavaux
9// Licence: wxWindows license
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
2df7be7f 13 #pragma implementation "ftp.h"
f4ada568 14#endif
ec45f8ee
UU
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19ae5cf0 19#ifdef __BORLANDC__
2df7be7f 20 #pragma hdrstop
19ae5cf0
UU
21#endif
22
35a4dab7
GL
23#if wxUSE_SOCKETS
24
469e1e5c 25#ifndef __MWERKS__
f4ada568 26#include <memory.h>
469e1e5c 27#endif
17dff81c
SC
28#if defined(__WXMAC__)
29#include "/wx/mac/macsock.h"
30#endif
31
f4ada568
GL
32#include <stdlib.h>
33#include "wx/string.h"
34#include "wx/utils.h"
f4ada568 35#include "wx/sckaddr.h"
f4ada568
GL
36#include "wx/socket.h"
37#include "wx/url.h"
38#include "wx/sckstrm.h"
39#include "wx/protocol/protocol.h"
40#include "wx/protocol/ftp.h"
8e907a13 41#include "wx/log.h"
f4ada568
GL
42
43#ifdef __BORLANDC__
44#pragma hdrstop
45#endif
46
8e907a13
VZ
47// the length of FTP status code (3 digits)
48static const size_t LEN_CODE = 3;
49
f4ada568
GL
50#define FTP_BSIZE 1024
51
f4ada568 52IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
223d09f6 53IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), TRUE)
f4ada568
GL
54
55////////////////////////////////////////////////////////////////
56////// wxFTP constructor and destructor ////////////////////////
57////////////////////////////////////////////////////////////////
58
59wxFTP::wxFTP()
8e907a13 60 : wxProtocol()
f4ada568 61{
8e907a13
VZ
62 m_lastError = wxPROTO_NOERR;
63 m_streaming = FALSE;
f4ada568 64
8e907a13
VZ
65 m_user = wxT("anonymous");
66 m_passwd << wxGetUserId() << wxT('@') << wxGetFullHostName();
f4ada568 67
8e907a13
VZ
68 SetNotify(0);
69 SetFlags(wxSOCKET_NONE);
f4ada568
GL
70}
71
72wxFTP::~wxFTP()
73{
8e907a13 74 Close();
f4ada568
GL
75}
76
77////////////////////////////////////////////////////////////////
78////// wxFTP connect and login methods /////////////////////////
79////////////////////////////////////////////////////////////////
8a2c6ef8 80bool wxFTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
f4ada568 81{
f4ada568
GL
82 if (!wxProtocol::Connect(addr)) {
83 m_lastError = wxPROTO_NETERR;
84 return FALSE;
85 }
86
87 if (!m_user || !m_passwd) {
88 m_lastError = wxPROTO_CONNERR;
89 return FALSE;
90 }
91
92 wxString command;
93
94 if (!GetResult('2')) {
95 Close();
96 return FALSE;
97 }
98
223d09f6 99 command.sprintf(wxT("USER %s"), (const wxChar *)m_user);
f4ada568
GL
100 if (!SendCommand(command, '3')) {
101 Close();
102 return FALSE;
103 }
104
223d09f6 105 command.sprintf(wxT("PASS %s"), (const wxChar *)m_passwd);
f4ada568
GL
106 if (!SendCommand(command, '2')) {
107 Close();
108 return FALSE;
109 }
110
111 return TRUE;
112}
113
114bool wxFTP::Connect(const wxString& host)
115{
116 wxIPV4address addr;
117 wxString my_host = host;
118
119 addr.Hostname(my_host);
223d09f6 120 addr.Service(wxT("ftp"));
f4ada568
GL
121
122 return Connect(addr);
123}
124
8e907a13 125bool wxFTP::Close(bool force)
f4ada568 126{
8e907a13
VZ
127 if ( m_streaming )
128 {
129 if ( !force )
130 {
131 m_lastError = wxPROTO_STREAMING;
132 return FALSE;
133 }
134
135 (void)Abort();
136 }
137
138 if ( IsConnected() )
139 SendCommand(wxT("QUIT"), '2');
e8773bdf 140
8e907a13 141 return wxSocketClient::Close();
f4ada568
GL
142}
143
8e907a13
VZ
144// ============================================================================
145// low level methods
146// ============================================================================
147
148// ----------------------------------------------------------------------------
149// Send command to FTP server
150// ----------------------------------------------------------------------------
151
f4ada568
GL
152bool wxFTP::SendCommand(const wxString& command, char exp_ret)
153{
8e907a13 154 wxString tmp_str;
f4ada568 155
8e907a13
VZ
156 if (m_streaming)
157 {
158 m_lastError = wxPROTO_STREAMING;
159 return FALSE;
160 }
161
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())
165 {
166 m_lastError = wxPROTO_NETERR;
167 return FALSE;
168 }
169
170 wxLogTrace(_T("ftp"), _T("==> %s"), command.c_str());
171
172 return GetResult(exp_ret);
f4ada568
GL
173}
174
8e907a13
VZ
175// ----------------------------------------------------------------------------
176// Recieve servers reply
177// ----------------------------------------------------------------------------
178
f4ada568
GL
179bool wxFTP::GetResult(char exp)
180{
8e907a13
VZ
181 wxString code;
182
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
186 // xyz-...
187 // ...
188 // xyz ...
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 )
194 {
195 m_lastError = ReadLine(m_lastResult);
196 if ( m_lastError )
197 return FALSE;
198
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 )
202 {
203 if ( firstLine )
204 {
205 badReply = TRUE;
206 }
207 else
208 {
209 wxLogTrace(_T("ftp"), _T("<== %s %s"),
210 code.c_str(), m_lastResult.c_str());
211 }
212 }
213 else // line has at least 4 chars
214 {
215 // this is the char which tells us what we're dealing with
216 wxChar chMarker = m_lastResult.GetChar(LEN_CODE);
217
218 if ( firstLine )
219 {
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);
223
224 switch ( chMarker )
225 {
226 case _T(' '):
227 endOfReply = TRUE;
228 break;
229
230 case _T('-'):
231 firstLine = FALSE;
232 break;
233
234 default:
235 // unexpected
236 badReply = TRUE;
237 }
238 }
239 else // subsequent line of multiline reply
240 {
241 if ( wxStrncmp(m_lastResult, code, LEN_CODE) == 0 )
242 {
243 if ( chMarker == _T(' ') )
244 {
245 endOfReply = TRUE;
246 }
247
248 wxLogTrace(_T("ftp"), _T("<== %s %s"),
249 code.c_str(), m_lastResult.c_str() + LEN_CODE + 1);
250 }
251 else
252 {
253 // just part of reply
254 wxLogTrace(_T("ftp"), _T("<== %s %s"),
255 code.c_str(), m_lastResult.c_str());
256 }
257 }
258 }
259 }
f4ada568 260
8e907a13
VZ
261 if ( badReply )
262 {
263 wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
264 m_lastResult.c_str());
f4ada568 265
8e907a13 266 m_lastError = wxPROTO_PROTERR;
f4ada568 267
f4ada568
GL
268 return FALSE;
269 }
8e907a13
VZ
270
271 if ( code.GetChar(0) != exp )
272 {
273 m_lastError = wxPROTO_PROTERR;
274
275 return FALSE;
276 }
277
278 return TRUE;
f4ada568
GL
279}
280
281////////////////////////////////////////////////////////////////
282////// wxFTP low-level methods /////////////////////////////////
283////////////////////////////////////////////////////////////////
284bool wxFTP::ChDir(const wxString& dir)
285{
286 wxString str = dir;
287
223d09f6 288 str.Prepend(wxT("CWD "));
f4ada568
GL
289 return SendCommand(str, '2');
290}
291
292bool wxFTP::MkDir(const wxString& dir)
293{
294 wxString str = dir;
223d09f6 295 str.Prepend(wxT("MKD "));
f4ada568
GL
296 return SendCommand(str, '2');
297}
298
299bool wxFTP::RmDir(const wxString& dir)
300{
301 wxString str = dir;
302
223d09f6 303 str.Prepend(wxT("PWD "));
f4ada568
GL
304 return SendCommand(str, '2');
305}
306
307wxString wxFTP::Pwd()
308{
8e907a13
VZ
309 wxString path;
310
311 if ( SendCommand(wxT("PWD"), '2') )
312 {
313 // the result is at least that long if SendCommand() succeeded
314 const wxChar *p = m_lastResult.c_str() + LEN_CODE + 1;
315 if ( *p != _T('"') )
316 {
317 wxLogDebug(_T("Missing starting quote in reply for PWD: %s"), p);
318 }
319 else
320 {
321 for ( p++; *p; p++ )
322 {
323 if ( *p == _T('"') )
324 {
325 // check if the quote is doubled
326 p++;
327 if ( !*p || *p != _T('"') )
328 {
329 // no, this is the end
330 break;
331 }
332 //else: yes, it is: this is an embedded quote in the
333 // filename, treat as normal char
334 }
335
336 path += *p;
337 }
338
339 if ( !*p )
340 {
341 wxLogDebug(_T("Missing ending quote in reply for PWD: %s"),
342 m_lastResult.c_str() + LEN_CODE + 1);
343 }
344 }
345 }
f4ada568 346
8e907a13 347 return path;
f4ada568
GL
348}
349
350bool wxFTP::Rename(const wxString& src, const wxString& dst)
351{
352 wxString str;
353
223d09f6 354 str = wxT("RNFR ") + src;
f4ada568
GL
355 if (!SendCommand(str, '3'))
356 return FALSE;
357
223d09f6 358 str = wxT("RNTO ") + dst;
f4ada568
GL
359 return SendCommand(str, '2');
360}
361
362bool wxFTP::RmFile(const wxString& path)
363{
364 wxString str;
365
223d09f6 366 str = wxT("DELE ");
f4ada568
GL
367 str += path;
368 return SendCommand(str, '2');
369}
370
371////////////////////////////////////////////////////////////////
372////// wxFTP download*upload ///////////////////////////////////
373////////////////////////////////////////////////////////////////
374
375class wxInputFTPStream : public wxSocketInputStream {
376public:
377 wxFTP *m_ftp;
9a1b2c28 378 size_t m_ftpsize;
f4ada568
GL
379
380 wxInputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
381 : wxSocketInputStream(*sock), m_ftp(ftp_clt) {}
f61815af 382 size_t GetSize() const { return m_ftpsize; }
f4ada568
GL
383 virtual ~wxInputFTPStream(void)
384 {
a324a7bc 385 if (LastError() == wxStream_NOERROR)
f4ada568
GL
386 m_ftp->GetResult('2');
387 else
388 m_ftp->Abort();
389 delete m_i_socket;
390 }
391};
392
393class wxOutputFTPStream : public wxSocketOutputStream {
394public:
395 wxFTP *m_ftp;
396
397 wxOutputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
398 : wxSocketOutputStream(*sock), m_ftp(ftp_clt) {}
399 virtual ~wxOutputFTPStream(void)
400 {
75ed1d15 401 if (LastError() != wxStream_NOERROR)
f4ada568
GL
402 m_ftp->GetResult('2');
403 else
404 m_ftp->Abort();
405 delete m_o_socket;
406 }
407};
408
409wxSocketClient *wxFTP::GetPort()
410{
411 wxIPV4address addr;
412 wxSocketClient *client;
f4ada568
GL
413 int a[6];
414 wxString straddr;
415 int addr_pos;
a324a7bc
GL
416 wxUint16 port;
417 wxUint32 hostaddr;
f4ada568 418
223d09f6 419 if (!SendCommand(wxT("PASV"), '2'))
f4ada568
GL
420 return NULL;
421
223d09f6 422 addr_pos = m_lastResult.Find(wxT('('));
f4ada568
GL
423 if (addr_pos == -1) {
424 m_lastError = wxPROTO_PROTERR;
425 return NULL;
426 }
427 straddr = m_lastResult(addr_pos+1, m_lastResult.Length());
223d09f6 428 wxSscanf((const wxChar *)straddr,wxT("%d,%d,%d,%d,%d,%d"),&a[2],&a[3],&a[4],&a[5],&a[0],&a[1]);
f4ada568 429
a324a7bc
GL
430 hostaddr = (wxUint16)a[5] << 24 | (wxUint16)a[4] << 16 |
431 (wxUint16)a[3] << 8 | a[2];
432 addr.Hostname(hostaddr);
433
434 port = (wxUint16)a[0] << 8 | a[1];
435 addr.Service(port);
f4ada568 436
a324a7bc 437 client = new wxSocketClient();
f4ada568
GL
438 if (!client->Connect(addr)) {
439 delete client;
440 return NULL;
441 }
442 client->Notify(FALSE);
443
444 return client;
445}
446
8e907a13 447bool wxFTP::Abort()
f4ada568 448{
8e907a13
VZ
449 if ( !m_streaming )
450 return TRUE;
451
452 m_streaming = FALSE;
453 if ( !SendCommand(wxT("ABOR"), '4') )
454 return FALSE;
455
456 return GetResult('2');
f4ada568
GL
457}
458
459wxInputStream *wxFTP::GetInputStream(const wxString& path)
460{
461 wxString tmp_str;
9a1b2c28
GL
462 int pos_size;
463 wxInputFTPStream *in_stream;
f4ada568 464
223d09f6 465 if (!SendCommand(wxT("TYPE I"), '2'))
f4ada568
GL
466 return NULL;
467
468 wxSocketClient *sock = GetPort();
469
470 if (!sock) {
471 m_lastError = wxPROTO_NETERR;
472 return NULL;
473 }
474
223d09f6 475 tmp_str = wxT("RETR ") + wxURL::ConvertFromURI(path);
f4ada568
GL
476 if (!SendCommand(tmp_str, '1'))
477 return NULL;
478
9a1b2c28
GL
479 in_stream = new wxInputFTPStream(this, sock);
480
223d09f6 481 pos_size = m_lastResult.Index(wxT('('));
9a1b2c28 482 if (pos_size != wxNOT_FOUND) {
223d09f6 483 wxString str_size = m_lastResult(pos_size+1, m_lastResult.Index(wxT(')'))-1);
9a1b2c28 484
4846abaf 485 in_stream->m_ftpsize = wxAtoi(WXSTRINGCAST str_size);
9a1b2c28 486 }
e8773bdf 487 sock->SetFlags(wxSOCKET_WAITALL);
9a1b2c28
GL
488
489 return in_stream;
f4ada568
GL
490}
491
492wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
493{
494 wxString tmp_str;
495
223d09f6 496 if (!SendCommand(wxT("TYPE I"), '2'))
f4ada568
GL
497 return NULL;
498
499 wxSocketClient *sock = GetPort();
500
223d09f6 501 tmp_str = wxT("STOR ") + path;
f4ada568
GL
502 if (!SendCommand(tmp_str, '1'))
503 return FALSE;
504
505 return new wxOutputFTPStream(this, sock);
506}
507
8e907a13
VZ
508bool wxFTP::GetList(wxArrayString& files, const wxString& wildcard)
509{
510 wxSocketBase *sock = GetPort();
511 if ( !sock )
512 {
513 return FALSE;
514 }
515
516 wxString line = _T("NLST");
517 if ( !!wildcard )
518 {
519 // notice that there is no space here
520 line += wildcard;
521 }
522
523 if ( !SendCommand(line, '1') )
524 {
525 return FALSE;
526 }
527
528 files.Empty();
529
530 while ( ReadLine(sock, line) == wxPROTO_NOERR )
531 {
532 files.Add(line);
533 }
534
535 delete sock;
536
537 // the file list should be terminated by "226 Transfer complete""
538 if ( !GetResult('2') )
539 return FALSE;
540
541 return TRUE;
542}
543
f4ada568
GL
544wxList *wxFTP::GetList(const wxString& wildcard)
545{
546 wxList *file_list = new wxList;
547 wxSocketBase *sock = GetPort();
223d09f6 548 wxString tmp_str = wxT("NLST");
f4ada568
GL
549
550 if (!wildcard.IsNull())
551 tmp_str += wildcard;
552
553 if (!SendCommand(tmp_str, '1')) {
554 delete sock;
555 delete file_list;
556 return NULL;
557 }
558
559 while (GetLine(sock, tmp_str) == wxPROTO_NOERR) {
560 file_list->Append((wxObject *)(new wxString(tmp_str)));
561 }
562
563 if (!GetResult('2')) {
564 delete sock;
565 file_list->DeleteContents(TRUE);
566 delete file_list;
567 return NULL;
568 }
569
570 return file_list;
571}
35a4dab7
GL
572#endif
573 // wxUSE_SOCKETS