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