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