]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/ftp.cpp
fixed memory allocation code of wxStreamBuffer to not realloc() new[]ed memory any...
[wxWidgets.git] / src / common / ftp.cpp
... / ...
CommitLineData
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__
13 #pragma implementation "ftp.h"
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20 #pragma hdrstop
21#endif
22
23#include "wx/setup.h"
24
25#if wxUSE_SOCKETS && wxUSE_STREAMS
26
27#ifndef __MWERKS__
28#include <memory.h>
29#endif
30#if defined(__WXMAC__)
31#include "/wx/mac/macsock.h"
32#endif
33
34#include <stdlib.h>
35#include "wx/string.h"
36#include "wx/utils.h"
37#include "wx/sckaddr.h"
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"
43#include "wx/log.h"
44
45#ifdef __BORLANDC__
46#pragma hdrstop
47#endif
48
49// the length of FTP status code (3 digits)
50static const size_t LEN_CODE = 3;
51
52#define FTP_BSIZE 1024
53
54IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
55IMPLEMENT_PROTOCOL(wxFTP, wxT("ftp"), wxT("ftp"), TRUE)
56
57////////////////////////////////////////////////////////////////
58////// wxFTP constructor and destructor ////////////////////////
59////////////////////////////////////////////////////////////////
60
61wxFTP::wxFTP()
62 : wxProtocol()
63{
64 m_lastError = wxPROTO_NOERR;
65 m_streaming = FALSE;
66
67 m_user = wxT("anonymous");
68 m_passwd << wxGetUserId() << wxT('@') << wxGetFullHostName();
69
70 SetNotify(0);
71 SetFlags(wxSOCKET_NONE);
72}
73
74wxFTP::~wxFTP()
75{
76 if ( m_streaming )
77 {
78 (void)Abort();
79 }
80
81 Close();
82}
83
84////////////////////////////////////////////////////////////////
85////// wxFTP connect and login methods /////////////////////////
86////////////////////////////////////////////////////////////////
87bool wxFTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
88{
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
106 command.sprintf(wxT("USER %s"), (const wxChar *)m_user);
107 if (!SendCommand(command, '3')) {
108 Close();
109 return FALSE;
110 }
111
112 command.sprintf(wxT("PASS %s"), (const wxChar *)m_passwd);
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);
127 addr.Service(wxT("ftp"));
128
129 return Connect(addr);
130}
131
132bool wxFTP::Close()
133{
134 if ( m_streaming )
135 {
136 m_lastError = wxPROTO_STREAMING;
137 return FALSE;
138 }
139
140 if ( IsConnected() )
141 SendCommand(wxT("QUIT"), '2');
142
143 return wxSocketClient::Close();
144}
145
146// ============================================================================
147// low level methods
148// ============================================================================
149
150// ----------------------------------------------------------------------------
151// Send command to FTP server
152// ----------------------------------------------------------------------------
153
154bool wxFTP::SendCommand(const wxString& command, char exp_ret)
155{
156 wxString tmp_str;
157
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);
175}
176
177// ----------------------------------------------------------------------------
178// Recieve servers reply
179// ----------------------------------------------------------------------------
180
181bool wxFTP::GetResult(char exp)
182{
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 }
262
263 if ( badReply )
264 {
265 wxLogDebug(_T("Broken FTP server: '%s' is not a valid reply."),
266 m_lastResult.c_str());
267
268 m_lastError = wxPROTO_PROTERR;
269
270 return FALSE;
271 }
272
273 if ( code.GetChar(0) != exp )
274 {
275 m_lastError = wxPROTO_PROTERR;
276
277 return FALSE;
278 }
279
280 return TRUE;
281}
282
283////////////////////////////////////////////////////////////////
284////// wxFTP low-level methods /////////////////////////////////
285////////////////////////////////////////////////////////////////
286bool wxFTP::ChDir(const wxString& dir)
287{
288 wxString str = dir;
289
290 str.Prepend(wxT("CWD "));
291 return SendCommand(str, '2');
292}
293
294bool wxFTP::MkDir(const wxString& dir)
295{
296 wxString str = dir;
297 str.Prepend(wxT("MKD "));
298 return SendCommand(str, '2');
299}
300
301bool wxFTP::RmDir(const wxString& dir)
302{
303 wxString str = dir;
304
305 str.Prepend(wxT("PWD "));
306 return SendCommand(str, '2');
307}
308
309wxString wxFTP::Pwd()
310{
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 }
348
349 return path;
350}
351
352bool wxFTP::Rename(const wxString& src, const wxString& dst)
353{
354 wxString str;
355
356 str = wxT("RNFR ") + src;
357 if (!SendCommand(str, '3'))
358 return FALSE;
359
360 str = wxT("RNTO ") + dst;
361 return SendCommand(str, '2');
362}
363
364bool wxFTP::RmFile(const wxString& path)
365{
366 wxString str;
367
368 str = wxT("DELE ");
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;
380 size_t m_ftpsize;
381
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)
386 {
387 if (LastError() == wxStream_NOERROR)
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 {
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 }
420 }
421};
422
423wxSocketClient *wxFTP::GetPort()
424{
425 wxIPV4address addr;
426 wxSocketClient *client;
427 int a[6];
428 wxString straddr;
429 int addr_pos;
430 wxUint16 port;
431 wxUint32 hostaddr;
432
433 if (!SendCommand(wxT("PASV"), '2'))
434 return NULL;
435
436 addr_pos = m_lastResult.Find(wxT('('));
437 if (addr_pos == -1) {
438 m_lastError = wxPROTO_PROTERR;
439 return NULL;
440 }
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]);
443
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);
450
451 client = new wxSocketClient();
452 if (!client->Connect(addr)) {
453 delete client;
454 return NULL;
455 }
456 client->Notify(FALSE);
457
458 return client;
459}
460
461bool wxFTP::Abort()
462{
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');
471}
472
473wxInputStream *wxFTP::GetInputStream(const wxString& path)
474{
475 wxString tmp_str;
476 int pos_size;
477 wxInputFTPStream *in_stream;
478
479 if (!SendCommand(wxT("TYPE I"), '2'))
480 return NULL;
481
482 wxSocketClient *sock = GetPort();
483
484 if (!sock) {
485 m_lastError = wxPROTO_NETERR;
486 return NULL;
487 }
488
489 tmp_str = wxT("RETR ") + wxURL::ConvertFromURI(path);
490 if (!SendCommand(tmp_str, '1'))
491 return NULL;
492
493 in_stream = new wxInputFTPStream(this, sock);
494
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);
498
499 in_stream->m_ftpsize = wxAtoi(WXSTRINGCAST str_size);
500 }
501 sock->SetFlags(wxSOCKET_WAITALL);
502
503 return in_stream;
504}
505
506wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
507{
508 wxString tmp_str;
509
510 if (!SendCommand(wxT("TYPE I"), '2'))
511 return NULL;
512
513 wxSocketClient *sock = GetPort();
514
515 tmp_str = wxT("STOR ") + path;
516 if (!SendCommand(tmp_str, '1'))
517 return FALSE;
518
519 return new wxOutputFTPStream(this, sock);
520}
521
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
558wxList *wxFTP::GetList(const wxString& wildcard)
559{
560 wxList *file_list = new wxList;
561 wxSocketBase *sock = GetPort();
562 wxString tmp_str = wxT("NLST");
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}
586#endif
587 // wxUSE_SOCKETS