Unicode interface (the communication itself is still in ASCII, I hope).
[wxWidgets.git] / src / common / ftp.cpp
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 #if wxUSE_SOCKETS
24
25 #ifndef __MWERKS__
26 #include <memory.h>
27 #endif
28 #if defined(__WXMAC__)
29 #include "/wx/mac/macsock.h"
30 #endif
31
32 #include <stdlib.h>
33 #include "wx/string.h"
34 #include "wx/utils.h"
35 // #include "wx/data.h"
36 #define WXSOCK_INTERNAL
37 #include "wx/sckaddr.h"
38 #undef WXSOCK_INTERNAL
39 #include "wx/socket.h"
40 #include "wx/url.h"
41 #include "wx/sckstrm.h"
42 #include "wx/protocol/protocol.h"
43 #include "wx/protocol/ftp.h"
44
45 #ifdef __BORLANDC__
46 #pragma hdrstop
47 #endif
48
49 #define FTP_BSIZE 1024
50
51 #if !USE_SHARED_LIBRARY
52 IMPLEMENT_DYNAMIC_CLASS(wxFTP, wxProtocol)
53 IMPLEMENT_PROTOCOL(wxFTP, _T("ftp"), _T("ftp"), TRUE)
54 #endif
55
56 ////////////////////////////////////////////////////////////////
57 ////// wxFTP constructor and destructor ////////////////////////
58 ////////////////////////////////////////////////////////////////
59
60 wxFTP::wxFTP()
61 : wxProtocol()
62 {
63 wxChar tmp[256];
64
65 m_lastError = wxPROTO_NOERR;
66 m_streaming = FALSE;
67
68 m_user = _T("anonymous");
69 wxGetUserName(tmp, 256);
70 m_passwd.sprintf(_T("%s@"),tmp);
71 wxGetHostName(tmp, 256);
72 m_passwd += tmp;
73
74 SetNotify(0);
75 }
76
77 wxFTP::~wxFTP()
78 {
79 SendCommand("QUIT", '2');
80 }
81
82 ////////////////////////////////////////////////////////////////
83 ////// wxFTP connect and login methods /////////////////////////
84 ////////////////////////////////////////////////////////////////
85 bool wxFTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
86 {
87 if (!m_handler) {
88 m_lastError = wxPROTO_NOHNDLR;
89 return FALSE;
90 }
91
92 if (!wxProtocol::Connect(addr)) {
93 m_lastError = wxPROTO_NETERR;
94 return FALSE;
95 }
96
97 if (!m_user || !m_passwd) {
98 m_lastError = wxPROTO_CONNERR;
99 return FALSE;
100 }
101
102 wxString command;
103
104 if (!GetResult('2')) {
105 Close();
106 return FALSE;
107 }
108
109 command.sprintf(_T("USER %s"), (const wxChar *)m_user);
110 if (!SendCommand(command, '3')) {
111 Close();
112 return FALSE;
113 }
114
115 command.sprintf(_T("PASS %s"), (const wxChar *)m_passwd);
116 if (!SendCommand(command, '2')) {
117 Close();
118 return FALSE;
119 }
120
121 return TRUE;
122 }
123
124 bool wxFTP::Connect(const wxString& host)
125 {
126 wxIPV4address addr;
127 wxString my_host = host;
128
129 addr.Hostname(my_host);
130 addr.Service(_T("ftp"));
131
132 return Connect(addr);
133 }
134
135 bool wxFTP::Close()
136 {
137 if (m_streaming) {
138 m_lastError = wxPROTO_STREAMING;
139 return FALSE;
140 }
141 if (m_connected)
142 SendCommand(wxString(_T("QUIT")), '2');
143 return wxSocketClient::Close();
144 }
145
146 ////////////////////////////////////////////////////////////////
147 ////// wxFTP low-level methods /////////////////////////////////
148 ////////////////////////////////////////////////////////////////
149 bool wxFTP::SendCommand(const wxString& command, char exp_ret)
150 {
151 wxString tmp_str;
152
153 if (m_streaming) {
154 m_lastError = wxPROTO_STREAMING;
155 return FALSE;
156 }
157 tmp_str = command + _T("\r\n");
158 wxWX2MBbuf tmp_buf = tmp_str.mb_str();
159 if (Write(MBSTRINGCAST tmp_buf, strlen(tmp_buf)).Error()) {
160 m_lastError = wxPROTO_NETERR;
161 return FALSE;
162 }
163 return GetResult(exp_ret);
164 }
165
166 bool wxFTP::GetResult(char exp)
167 {
168 if ((m_lastError = GetLine(this, m_lastResult)))
169 return FALSE;
170 if (m_lastResult.GetChar(0) != exp) {
171 m_lastError = wxPROTO_PROTERR;
172 return FALSE;
173 }
174
175 if (m_lastResult.GetChar(3) == '-') {
176 wxString key = m_lastResult.Left((size_t)3);
177
178 key += _T(' ');
179
180 while (m_lastResult.Index(key) != 0) {
181 if ((m_lastError = GetLine(this, m_lastResult)))
182 return FALSE;
183 }
184 }
185 return TRUE;
186 }
187
188 ////////////////////////////////////////////////////////////////
189 ////// wxFTP low-level methods /////////////////////////////////
190 ////////////////////////////////////////////////////////////////
191 bool wxFTP::ChDir(const wxString& dir)
192 {
193 wxString str = dir;
194
195 str.Prepend(_T("CWD "));
196 return SendCommand(str, '2');
197 }
198
199 bool wxFTP::MkDir(const wxString& dir)
200 {
201 wxString str = dir;
202 str.Prepend(_T("MKD "));
203 return SendCommand(str, '2');
204 }
205
206 bool wxFTP::RmDir(const wxString& dir)
207 {
208 wxString str = dir;
209
210 str.Prepend(_T("PWD "));
211 return SendCommand(str, '2');
212 }
213
214 wxString wxFTP::Pwd()
215 {
216 int beg, end;
217
218 if (!SendCommand(_T("PWD"), '2'))
219 return wxString((char *)NULL);
220
221 beg = m_lastResult.Find(_T('\"'),FALSE);
222 end = m_lastResult.Find(_T('\"'),TRUE);
223
224 return wxString(beg+1, end);
225 }
226
227 bool wxFTP::Rename(const wxString& src, const wxString& dst)
228 {
229 wxString str;
230
231 str = _T("RNFR ") + src;
232 if (!SendCommand(str, '3'))
233 return FALSE;
234
235 str = _T("RNTO ") + dst;
236 return SendCommand(str, '2');
237 }
238
239 bool wxFTP::RmFile(const wxString& path)
240 {
241 wxString str;
242
243 str = _T("DELE ");
244 str += path;
245 return SendCommand(str, '2');
246 }
247
248 ////////////////////////////////////////////////////////////////
249 ////// wxFTP download*upload ///////////////////////////////////
250 ////////////////////////////////////////////////////////////////
251
252 class wxInputFTPStream : public wxSocketInputStream {
253 public:
254 wxFTP *m_ftp;
255 size_t m_ftpsize;
256
257 wxInputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
258 : wxSocketInputStream(*sock), m_ftp(ftp_clt) {}
259 size_t StreamSize() const { return m_ftpsize; }
260 virtual ~wxInputFTPStream(void)
261 {
262 if (LastError() != wxStream_NOERROR)
263 m_ftp->GetResult('2');
264 else
265 m_ftp->Abort();
266 delete m_i_socket;
267 }
268 };
269
270 class wxOutputFTPStream : public wxSocketOutputStream {
271 public:
272 wxFTP *m_ftp;
273
274 wxOutputFTPStream(wxFTP *ftp_clt, wxSocketBase *sock)
275 : wxSocketOutputStream(*sock), m_ftp(ftp_clt) {}
276 virtual ~wxOutputFTPStream(void)
277 {
278 if (LastError() != wxStream_NOERROR)
279 m_ftp->GetResult('2');
280 else
281 m_ftp->Abort();
282 delete m_o_socket;
283 }
284 };
285
286 wxSocketClient *wxFTP::GetPort()
287 {
288 wxIPV4address addr;
289 wxSocketClient *client;
290 struct sockaddr sin;
291 int a[6];
292 wxString straddr;
293 int addr_pos;
294
295 if (!SendCommand(_T("PASV"), '2'))
296 return NULL;
297
298 sin.sa_family = AF_INET;
299 addr_pos = m_lastResult.Find(_T('('));
300 if (addr_pos == -1) {
301 m_lastError = wxPROTO_PROTERR;
302 return NULL;
303 }
304 straddr = m_lastResult(addr_pos+1, m_lastResult.Length());
305 wxSscanf((const wxChar *)straddr,_T("%d,%d,%d,%d,%d,%d"),&a[2],&a[3],&a[4],&a[5],&a[0],&a[1]);
306 sin.sa_data[2] = (char)a[2];
307 sin.sa_data[3] = (char)a[3];
308 sin.sa_data[4] = (char)a[4];
309 sin.sa_data[5] = (char)a[5];
310 sin.sa_data[0] = (char)a[0];
311 sin.sa_data[1] = (char)a[1];
312
313 addr.Disassemble(&sin, sizeof(sin));
314
315 client = m_handler->CreateClient();
316 if (!client->Connect(addr)) {
317 delete client;
318 return NULL;
319 }
320 client->Notify(FALSE);
321
322 return client;
323 }
324
325 bool wxFTP::Abort(void)
326 {
327 m_streaming = FALSE;
328 if (!SendCommand(_T("ABOR"), '4'))
329 return FALSE;
330 return GetResult('2');
331 }
332
333 wxInputStream *wxFTP::GetInputStream(const wxString& path)
334 {
335 wxString tmp_str;
336 int pos_size;
337 wxInputFTPStream *in_stream;
338
339 if (!SendCommand(_T("TYPE I"), '2'))
340 return NULL;
341
342 wxSocketClient *sock = GetPort();
343
344 if (!sock) {
345 m_lastError = wxPROTO_NETERR;
346 return NULL;
347 }
348
349 tmp_str = _T("RETR ") + path;
350 if (!SendCommand(tmp_str, '1'))
351 return NULL;
352
353 in_stream = new wxInputFTPStream(this, sock);
354
355 pos_size = m_lastResult.Index(_T('('));
356 if (pos_size != wxNOT_FOUND) {
357 wxString str_size = m_lastResult(pos_size+1, m_lastResult.Index(_T(')'))-1);
358
359 in_stream->m_ftpsize = wxAtoi(WXSTRINGCAST str_size);
360 }
361
362 return in_stream;
363 }
364
365 wxOutputStream *wxFTP::GetOutputStream(const wxString& path)
366 {
367 wxString tmp_str;
368
369 if (!SendCommand(_T("TYPE I"), '2'))
370 return NULL;
371
372 wxSocketClient *sock = GetPort();
373
374 tmp_str = _T("STOR ") + path;
375 if (!SendCommand(tmp_str, '1'))
376 return FALSE;
377
378 return new wxOutputFTPStream(this, sock);
379 }
380
381 wxList *wxFTP::GetList(const wxString& wildcard)
382 {
383 wxList *file_list = new wxList;
384 wxSocketBase *sock = GetPort();
385 wxString tmp_str = _T("NLST");
386
387 if (!wildcard.IsNull())
388 tmp_str += wildcard;
389
390 if (!SendCommand(tmp_str, '1')) {
391 delete sock;
392 delete file_list;
393 return NULL;
394 }
395
396 while (GetLine(sock, tmp_str) == wxPROTO_NOERR) {
397 file_list->Append((wxObject *)(new wxString(tmp_str)));
398 }
399
400 if (!GetResult('2')) {
401 delete sock;
402 file_list->DeleteContents(TRUE);
403 delete file_list;
404 return NULL;
405 }
406
407 sock->SetEventHandler(*GetNextHandler(), m_id);
408 sock->Notify(m_notifyme);
409 sock->SetNotify(m_neededreq);
410
411 return file_list;
412 }
413 #endif
414 // wxUSE_SOCKETS