1//
2// FTPClientSession.cpp
3//
4// Library: Net
5// Package: FTP
6// Module: FTPClientSession
7//
8// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/FTPClientSession.h"
16#include "Poco/Net/SocketAddress.h"
17#include "Poco/Net/SocketStream.h"
18#include "Poco/Net/ServerSocket.h"
19#include "Poco/Net/NetException.h"
20#include "Poco/NumberFormatter.h"
21#include "Poco/Ascii.h"
22
23
24using Poco::NumberFormatter;
25
26
27namespace Poco {
28namespace Net {
29
30
31FTPClientSession::FTPClientSession():
32 _pControlSocket(0),
33 _pDataStream(0),
34 _port(0),
35 _passiveMode(true),
36 _fileType(TYPE_BINARY),
37 _supports1738(true),
38 _serverReady(false),
39 _isLoggedIn(false),
40 _timeout(DEFAULT_TIMEOUT)
41{
42}
43
44
45FTPClientSession::FTPClientSession(const StreamSocket& socket):
46 _pControlSocket(new DialogSocket(socket)),
47 _pDataStream(0),
48 _host(socket.address().host().toString()),
49 _port(socket.address().port()),
50 _passiveMode(true),
51 _fileType(TYPE_BINARY),
52 _supports1738(true),
53 _serverReady(false),
54 _isLoggedIn(false),
55 _timeout(DEFAULT_TIMEOUT)
56{
57 _pControlSocket->setReceiveTimeout(_timeout);
58}
59
60
61FTPClientSession::FTPClientSession(const std::string& host,
62 Poco::UInt16 port,
63 const std::string& username,
64 const std::string& password):
65 _pControlSocket(new DialogSocket(SocketAddress(host, port))),
66 _pDataStream(0),
67 _host(host),
68 _port(port),
69 _passiveMode(true),
70 _fileType(TYPE_BINARY),
71 _supports1738(true),
72 _serverReady(false),
73 _isLoggedIn(false),
74 _timeout(DEFAULT_TIMEOUT)
75{
76 _pControlSocket->setReceiveTimeout(_timeout);
77 if (!username.empty())
78 login(username, password);
79}
80
81
82FTPClientSession::~FTPClientSession()
83{
84 try
85 {
86 close();
87 }
88 catch (...)
89 {
90 }
91}
92
93void FTPClientSession::setTimeout(const Poco::Timespan& timeout)
94{
95 if (!isOpen())
96 throw FTPException("Connection is closed.");
97
98 _timeout = timeout;
99 _pControlSocket->setReceiveTimeout(timeout);
100}
101
102
103Poco::Timespan FTPClientSession::getTimeout() const
104{
105 return _timeout;
106}
107
108
109void FTPClientSession::setPassive(bool flag, bool useRFC1738)
110{
111 _passiveMode = flag;
112 _supports1738 = useRFC1738;
113}
114
115
116bool FTPClientSession::getPassive() const
117{
118 return _passiveMode;
119}
120
121
122void FTPClientSession::open(const std::string& host,
123 Poco::UInt16 port,
124 const std::string& username,
125 const std::string& password)
126{
127 _host = host;
128 _port = port;
129 if (!username.empty())
130 {
131 login(username, password);
132 }
133 else
134 {
135 if (!_pControlSocket)
136 {
137 _pControlSocket = new DialogSocket(SocketAddress(_host, _port));
138 _pControlSocket->setReceiveTimeout(_timeout);
139 }
140 receiveServerReadyReply();
141 }
142}
143
144void FTPClientSession::receiveServerReadyReply()
145{
146 if (_serverReady)
147 return;
148 std::string response;
149 int status = _pControlSocket->receiveStatusMessage(response);
150 if (!isPositiveCompletion(status))
151 throw FTPException("Cannot receive status message", response, status);
152
153 _serverReady = true;
154}
155
156void FTPClientSession::login(const std::string& username, const std::string& password)
157{
158 if (_isLoggedIn) logout();
159
160 int status = FTP_POSITIVE_COMPLETION * 100;
161 std::string response;
162 if (!_pControlSocket)
163 {
164 _pControlSocket = new DialogSocket(SocketAddress(_host, _port));
165 _pControlSocket->setReceiveTimeout(_timeout);
166 }
167 receiveServerReadyReply();
168
169 status = sendCommand("USER", username, response);
170 if (isPositiveIntermediate(status))
171 status = sendCommand("PASS", password, response);
172 if (!isPositiveCompletion(status))
173 throw FTPException("Login denied", response, status);
174
175 setFileType(_fileType);
176 _isLoggedIn = true;
177}
178
179
180void FTPClientSession::logout()
181{
182 if (!isOpen())
183 throw FTPException("Connection is closed.");
184
185 if (_isLoggedIn)
186 {
187 try { endTransfer(); }
188 catch (...) { }
189 _isLoggedIn = false;
190 std::string response;
191 sendCommand("QUIT", response);
192 }
193}
194
195
196void FTPClientSession::close()
197{
198 try { logout(); }
199 catch(...){ }
200 _serverReady = false;
201 if (_pControlSocket)
202 {
203 _pControlSocket->close();
204 delete _pControlSocket;
205 _pControlSocket = 0;
206 }
207}
208
209
210void FTPClientSession::setFileType(FTPClientSession::FileType type)
211{
212 std::string response;
213 int status = sendCommand("TYPE", (type == TYPE_TEXT ? "A" : "I"), response);
214 if (!isPositiveCompletion(status)) throw FTPException("Cannot set file type", response, status);
215 _fileType = type;
216}
217
218
219FTPClientSession::FileType FTPClientSession::getFileType() const
220{
221 return _fileType;
222}
223
224
225std::string FTPClientSession::systemType()
226{
227 std::string response;
228 int status = sendCommand("SYST", response);
229 if (isPositiveCompletion(status))
230 return response.substr(4);
231 else
232 throw FTPException("Cannot get remote system type", response, status);
233}
234
235
236void FTPClientSession::setWorkingDirectory(const std::string& path)
237{
238 std::string response;
239 int status = sendCommand("CWD", path, response);
240 if (!isPositiveCompletion(status))
241 throw FTPException("Cannot change directory", response, status);
242}
243
244
245std::string FTPClientSession::getWorkingDirectory()
246{
247 std::string response;
248 int status = sendCommand("PWD", response);
249 if (isPositiveCompletion(status))
250 return extractPath(response);
251 else
252 throw FTPException("Cannot get current working directory", response, status);
253}
254
255
256void FTPClientSession::cdup()
257{
258 std::string response;
259 int status = sendCommand("CDUP", response);
260 if (!isPositiveCompletion(status))
261 throw FTPException("Cannot change directory", response, status);
262}
263
264
265void FTPClientSession::rename(const std::string& oldName, const std::string& newName)
266{
267 std::string response;
268 int status = sendCommand("RNFR", oldName, response);
269 if (!isPositiveIntermediate(status))
270 throw FTPException(std::string("Cannot rename ") + oldName, response, status);
271 status = sendCommand("RNTO", newName, response);
272 if (!isPositiveCompletion(status))
273 throw FTPException(std::string("Cannot rename to ") + newName, response, status);
274}
275
276
277void FTPClientSession::remove(const std::string& path)
278{
279 std::string response;
280 int status = sendCommand("DELE", path, response);
281 if (!isPositiveCompletion(status))
282 throw FTPException(std::string("Cannot remove " + path), response, status);
283}
284
285
286void FTPClientSession::createDirectory(const std::string& path)
287{
288 std::string response;
289 int status = sendCommand("MKD", path, response);
290 if (!isPositiveCompletion(status))
291 throw FTPException(std::string("Cannot create directory ") + path, response, status);
292}
293
294
295void FTPClientSession::removeDirectory(const std::string& path)
296{
297 std::string response;
298 int status = sendCommand("RMD", path, response);
299 if (!isPositiveCompletion(status))
300 throw FTPException(std::string("Cannot remove directory ") + path, response, status);
301}
302
303
304std::istream& FTPClientSession::beginDownload(const std::string& path)
305{
306 if (!isOpen())
307 throw FTPException("Connection is closed.");
308
309 delete _pDataStream;
310 _pDataStream = 0;
311 _pDataStream = new SocketStream(establishDataConnection("RETR", path));
312 return *_pDataStream;
313}
314
315
316void FTPClientSession::endDownload()
317{
318 endTransfer();
319}
320
321
322std::ostream& FTPClientSession::beginUpload(const std::string& path)
323{
324 if (!isOpen())
325 throw FTPException("Connection is closed.");
326
327 delete _pDataStream;
328 _pDataStream = 0;
329 _pDataStream = new SocketStream(establishDataConnection("STOR", path));
330 return *_pDataStream;
331}
332
333
334void FTPClientSession::endUpload()
335{
336 endTransfer();
337}
338
339
340std::istream& FTPClientSession::beginList(const std::string& path, bool extended)
341{
342 if (!isOpen())
343 throw FTPException("Connection is closed.");
344
345 delete _pDataStream;
346 _pDataStream = 0;
347 _pDataStream = new SocketStream(establishDataConnection(extended ? "LIST" : "NLST", path));
348 return *_pDataStream;
349}
350
351
352void FTPClientSession::endList()
353{
354 endTransfer();
355}
356
357
358void FTPClientSession::abort()
359{
360 if (!isOpen())
361 throw FTPException("Connection is closed.");
362
363 _pControlSocket->sendByte(DialogSocket::TELNET_IP);
364 _pControlSocket->synch();
365 std::string response;
366 int status = sendCommand("ABOR", response);
367 if (status == 426)
368 status = _pControlSocket->receiveStatusMessage(response);
369 if (status != 226)
370 throw FTPException("Cannot abort transfer", response, status);
371}
372
373
374int FTPClientSession::sendCommand(const std::string& command, std::string& response)
375{
376 if (!isOpen())
377 throw FTPException("Connection is closed.");
378
379 _pControlSocket->sendMessage(command);
380 return _pControlSocket->receiveStatusMessage(response);
381}
382
383
384int FTPClientSession::sendCommand(const std::string& command, const std::string& arg, std::string& response)
385{
386 if (!isOpen())
387 throw FTPException("Connection is closed.");
388
389 _pControlSocket->sendMessage(command, arg);
390 return _pControlSocket->receiveStatusMessage(response);
391}
392
393
394std::string FTPClientSession::extractPath(const std::string& response)
395{
396 std::string path;
397 std::string::const_iterator it = response.begin();
398 std::string::const_iterator end = response.end();
399 while (it != end && *it != '"') ++it;
400 if (it != end)
401 {
402 ++it;
403 while (it != end)
404 {
405 if (*it == '"')
406 {
407 ++it;
408 if (it == end || (it != end && *it != '"')) break;
409 }
410 path += *it++;
411 }
412 }
413 return path;
414}
415
416
417StreamSocket FTPClientSession::establishDataConnection(const std::string& command, const std::string& arg)
418{
419 if (_passiveMode)
420 return passiveDataConnection(command, arg);
421 else
422 return activeDataConnection(command, arg);
423}
424
425
426StreamSocket FTPClientSession::activeDataConnection(const std::string& command, const std::string& arg)
427{
428 if (!isOpen())
429 throw FTPException("Connection is closed.");
430
431 ServerSocket server(SocketAddress(_pControlSocket->address().host(), 0));
432 sendPortCommand(server.address());
433 std::string response;
434 int status = sendCommand(command, arg, response);
435 if (!isPositivePreliminary(status))
436 throw FTPException(command + " command failed", response, status);
437 if (server.poll(_timeout, Socket::SELECT_READ))
438 return server.acceptConnection();
439 else
440 throw FTPException("The server has not initiated a data connection");
441}
442
443
444StreamSocket FTPClientSession::passiveDataConnection(const std::string& command, const std::string& arg)
445{
446 SocketAddress sa(sendPassiveCommand());
447 StreamSocket sock(sa);
448 std::string response;
449 int status = sendCommand(command, arg, response);
450 if (!isPositivePreliminary(status))
451 throw FTPException(command + " command failed", response, status);
452 return sock;
453}
454
455
456void FTPClientSession::sendPortCommand(const SocketAddress& addr)
457{
458 if (_supports1738)
459 {
460 if (sendEPRT(addr))
461 return;
462 else
463 _supports1738 = false;
464 }
465 sendPORT(addr);
466}
467
468
469SocketAddress FTPClientSession::sendPassiveCommand()
470{
471 SocketAddress addr;
472 if (_supports1738)
473 {
474 if (sendEPSV(addr))
475 return addr;
476 else
477 _supports1738 = false;
478 }
479 sendPASV(addr);
480 return addr;
481}
482
483
484bool FTPClientSession::sendEPRT(const SocketAddress& addr)
485{
486 std::string arg("|");
487 arg += addr.af() == AF_INET ? '1' : '2';
488 arg += '|';
489 arg += addr.host().toString();
490 arg += '|';
491 arg += NumberFormatter::format(addr.port());
492 arg += '|';
493 std::string response;
494 int status = sendCommand("EPRT", arg, response);
495 if (isPositiveCompletion(status))
496 return true;
497 else if (isPermanentNegative(status))
498 return false;
499 else
500 throw FTPException("EPRT command failed", response, status);
501}
502
503
504void FTPClientSession::sendPORT(const SocketAddress& addr)
505{
506 std::string arg(addr.host().toString());
507 for (std::string::iterator it = arg.begin(); it != arg.end(); ++it)
508 {
509 if (*it == '.') *it = ',';
510 }
511 arg += ',';
512 Poco::UInt16 port = addr.port();
513 arg += NumberFormatter::format(port/256);
514 arg += ',';
515 arg += NumberFormatter::format(port % 256);
516 std::string response;
517 int status = sendCommand("PORT", arg, response);
518 if (!isPositiveCompletion(status))
519 throw FTPException("PORT command failed", response, status);
520}
521
522
523bool FTPClientSession::sendEPSV(SocketAddress& addr)
524{
525 std::string response;
526 int status = sendCommand("EPSV", response);
527 if (isPositiveCompletion(status))
528 {
529 parseExtAddress(response, addr);
530 return true;
531 }
532 else if (isPermanentNegative(status))
533 {
534 return false;
535 }
536 else throw FTPException("EPSV command failed", response, status);
537}
538
539
540void FTPClientSession::sendPASV(SocketAddress& addr)
541{
542 std::string response;
543 int status = sendCommand("PASV", response);
544 if (!isPositiveCompletion(status))
545 throw FTPException("PASV command failed", response, status);
546 parseAddress(response, addr);
547}
548
549
550void FTPClientSession::parseAddress(const std::string& str, SocketAddress& addr)
551{
552 std::string::const_iterator it = str.begin();
553 std::string::const_iterator end = str.end();
554 while (it != end && *it != '(') ++it;
555 if (it != end) ++it;
556 std::string host;
557 while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
558 if (it != end && *it == ',') { host += '.'; ++it; }
559 while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
560 if (it != end && *it == ',') { host += '.'; ++it; }
561 while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
562 if (it != end && *it == ',') { host += '.'; ++it; }
563 while (it != end && Poco::Ascii::isDigit(*it)) host += *it++;
564 if (it != end && *it == ',') ++it;
565 Poco::UInt16 portHi = 0;
566 while (it != end && Poco::Ascii::isDigit(*it)) { portHi *= 10; portHi += *it++ - '0'; }
567 if (it != end && *it == ',') ++it;
568 Poco::UInt16 portLo = 0;
569 while (it != end && Poco::Ascii::isDigit(*it)) { portLo *= 10; portLo += *it++ - '0'; }
570 addr = SocketAddress(host, portHi*256 + portLo);
571}
572
573
574void FTPClientSession::parseExtAddress(const std::string& str, SocketAddress& addr)
575{
576 std::string::const_iterator it = str.begin();
577 std::string::const_iterator end = str.end();
578 while (it != end && *it != '(') ++it;
579 if (it != end) ++it;
580 char delim = '|';
581 if (it != end) delim = *it++;
582 if (it != end && *it == delim) ++it;
583 if (it != end && *it == delim) ++it;
584 Poco::UInt16 port = 0;
585 while (it != end && Poco::Ascii::isDigit(*it)) { port *= 10; port += *it++ - '0'; }
586 addr = SocketAddress(_pControlSocket->peerAddress().host(), port);
587}
588
589
590void FTPClientSession::endTransfer()
591{
592 if (_pDataStream)
593 {
594 delete _pDataStream;
595 _pDataStream = 0;
596 std::string response;
597 int status = _pControlSocket->receiveStatusMessage(response);
598 if (!isPositiveCompletion(status))
599 throw FTPException("Data transfer failed", response, status);
600 }
601}
602
603} } // namespace Poco::Net
604