1//
2// FTPClientSessionTest.cpp
3//
4// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
5// and Contributors.
6//
7// SPDX-License-Identifier: BSL-1.0
8//
9
10
11#include "FTPClientSessionTest.h"
12#include "Poco/CppUnit/TestCaller.h"
13#include "Poco/CppUnit/TestSuite.h"
14#include "DialogServer.h"
15#include "Poco/Net/FTPClientSession.h"
16#include "Poco/Net/DialogSocket.h"
17#include "Poco/Net/SocketAddress.h"
18#include "Poco/Net/NetException.h"
19#include "Poco/Thread.h"
20#include "Poco/ActiveMethod.h"
21#include "Poco/StreamCopier.h"
22#include <sstream>
23
24
25using Poco::Net::FTPClientSession;
26using Poco::Net::DialogSocket;
27using Poco::Net::SocketAddress;
28using Poco::Net::FTPException;
29using Poco::ActiveMethod;
30using Poco::ActiveResult;
31using Poco::StreamCopier;
32using Poco::Thread;
33
34
35namespace
36{
37 class ActiveDownloader
38 {
39 public:
40 ActiveDownloader(FTPClientSession& session):
41 download(this, &ActiveDownloader::downloadImp),
42 _session(session)
43 {
44 }
45
46 ActiveMethod<std::string, std::string, ActiveDownloader> download;
47
48 protected:
49 std::string downloadImp(const std::string& path)
50 {
51 std::istream& istr = _session.beginDownload(path);
52 std::ostringstream ostr;
53 StreamCopier::copyStream(istr, ostr);
54 _session.endDownload();
55 return ostr.str();
56 }
57
58 private:
59 FTPClientSession& _session;
60 };
61};
62
63
64FTPClientSessionTest::FTPClientSessionTest(const std::string& name): CppUnit::TestCase(name)
65{
66}
67
68
69FTPClientSessionTest::~FTPClientSessionTest()
70{
71}
72
73
74void FTPClientSessionTest::login(DialogServer& server, FTPClientSession& session)
75{
76 server.addResponse("331 Password required");
77 server.addResponse("230 Welcome");
78 server.addResponse("200 Type set to I");
79 session.login("user", "password");
80 std::string cmd = server.popCommand();
81 assertTrue (cmd == "USER user");
82 cmd = server.popCommand();
83 assertTrue (cmd == "PASS password");
84 cmd = server.popCommand();
85 assertTrue (cmd == "TYPE I");
86
87 assertTrue (session.getFileType() == FTPClientSession::TYPE_BINARY);
88}
89
90
91void FTPClientSessionTest::testLogin1()
92{
93 DialogServer server;
94 server.addResponse("220 localhost FTP ready");
95 FTPClientSession session("127.0.0.1", server.port());
96 assertTrue (session.isOpen());
97 assertTrue (!session.isLoggedIn());
98 login(server, session);
99 assertTrue (session.isOpen());
100 assertTrue (session.isLoggedIn());
101 server.addResponse("221 Good Bye");
102 session.logout();
103 assertTrue (session.isOpen());
104 assertTrue (!session.isLoggedIn());
105
106 server.clearCommands();
107 server.clearResponses();
108 login(server, session);
109 assertTrue (session.isOpen());
110 assertTrue (session.isLoggedIn());
111 server.addResponse("221 Good Bye");
112 session.close();
113 assertTrue (!session.isOpen());
114 assertTrue (!session.isLoggedIn());
115}
116
117
118void FTPClientSessionTest::testLogin2()
119{
120 DialogServer server;
121 server.addResponse("220 localhost FTP ready");
122 server.addResponse("331 Password required");
123 server.addResponse("230 Welcome");
124 server.addResponse("200 Type set to I");
125 Poco::UInt16 serverPort = server.port();
126 FTPClientSession session("127.0.0.1", serverPort, "user", "password");
127 assertTrue (session.isOpen());
128 assertTrue (session.isLoggedIn());
129 server.addResponse("221 Good Bye");
130 session.close();
131 assertTrue (!session.isOpen());
132 assertTrue (!session.isLoggedIn());
133
134 server.clearCommands();
135 server.clearResponses();
136 server.addResponse("220 localhost FTP ready");
137 server.addResponse("331 Password required");
138 server.addResponse("230 Welcome");
139 server.addResponse("200 Type set to I");
140 session.open("127.0.0.1", serverPort, "user", "password");
141 assertTrue (session.isOpen());
142 assertTrue (session.isLoggedIn());
143 server.addResponse("221 Good Bye");
144 session.close();
145 assertTrue (!session.isOpen());
146 assertTrue (!session.isLoggedIn());
147}
148
149
150void FTPClientSessionTest::testLogin3()
151{
152 DialogServer server;
153 server.addResponse("220 localhost FTP ready");
154 server.addResponse("331 Password required");
155 server.addResponse("230 Welcome");
156 server.addResponse("200 Type set to I");
157 FTPClientSession session;
158 assertTrue (!session.isOpen());
159 assertTrue (!session.isLoggedIn());
160 session.open("127.0.0.1", server.port(), "user", "password");
161 server.addResponse("221 Good Bye");
162 session.close();
163 assertTrue (!session.isOpen());
164 assertTrue (!session.isLoggedIn());
165}
166
167
168
169void FTPClientSessionTest::testLoginFailed1()
170{
171 DialogServer server;
172 server.addResponse("421 localhost FTP not ready");
173 FTPClientSession session("127.0.0.1", server.port());
174 try
175 {
176 session.login("user", "password");
177 fail("server not ready - must throw");
178 }
179 catch (FTPException&)
180 {
181 }
182 server.addResponse("221 Good Bye");
183 session.close();
184}
185
186
187void FTPClientSessionTest::testLoginFailed2()
188{
189 DialogServer server;
190 server.addResponse("220 localhost FTP ready");
191 server.addResponse("331 Password required");
192 server.addResponse("530 Login incorrect");
193 FTPClientSession session("127.0.0.1", server.port());
194 try
195 {
196 session.login("user", "password");
197 fail("login incorrect - must throw");
198 }
199 catch (FTPException&)
200 {
201 }
202 server.addResponse("221 Good Bye");
203 session.close();
204}
205
206
207void FTPClientSessionTest::testCommands()
208{
209 DialogServer server;
210 server.addResponse("220 localhost FTP ready");
211 server.addResponse("331 Password required");
212 server.addResponse("230 Welcome");
213 server.addResponse("200 Type set to I");
214 FTPClientSession session("127.0.0.1", server.port());
215 session.login("user", "password");
216 std::string cmd = server.popCommand();
217 assertTrue (cmd == "USER user");
218 cmd = server.popCommand();
219 assertTrue (cmd == "PASS password");
220 cmd = server.popCommand();
221 assertTrue (cmd == "TYPE I");
222
223 // systemType
224 server.clearCommands();
225 server.addResponse("215 UNIX Type: L8 Version: dummyFTP 1.0");
226 std::string type = session.systemType();
227 cmd = server.popCommand();
228 assertTrue (cmd == "SYST");
229 assertTrue (type == "UNIX Type: L8 Version: dummyFTP 1.0");
230
231 // getWorkingDirectory
232 server.addResponse("257 \"/usr/test\" is current directory");
233 std::string cwd = session.getWorkingDirectory();
234 cmd = server.popCommand();
235 assertTrue (cmd == "PWD");
236 assertTrue (cwd == "/usr/test");
237
238 // getWorkingDirectory (quotes in filename)
239 server.addResponse("257 \"\"\"quote\"\"\" is current directory");
240 cwd = session.getWorkingDirectory();
241 cmd = server.popCommand();
242 assertTrue (cmd == "PWD");
243 assertTrue (cwd == "\"quote\"");
244
245 // setWorkingDirectory
246 server.addResponse("250 CWD OK");
247 session.setWorkingDirectory("test");
248 cmd = server.popCommand();
249 assertTrue (cmd == "CWD test");
250
251 server.addResponse("250 CDUP OK");
252 session.cdup();
253 cmd = server.popCommand();
254 assertTrue (cmd == "CDUP");
255
256 // rename
257 server.addResponse("350 File exists, send destination name");
258 server.addResponse("250 Rename OK");
259 session.rename("old.txt", "new.txt");
260 cmd = server.popCommand();
261 assertTrue (cmd == "RNFR old.txt");
262 cmd = server.popCommand();
263 assertTrue (cmd == "RNTO new.txt");
264
265 // rename (failing)
266 server.addResponse("550 not found");
267 try
268 {
269 session.rename("old.txt", "new.txt");
270 fail("not found - must throw");
271 }
272 catch (FTPException&)
273 {
274 }
275 server.clearCommands();
276
277 // remove
278 server.addResponse("250 delete ok");
279 session.remove("test.txt");
280 cmd = server.popCommand();
281 assertTrue (cmd == "DELE test.txt");
282
283 // remove (failing)
284 server.addResponse("550 not found");
285 try
286 {
287 session.remove("test.txt");
288 fail("not found - must throw");
289 }
290 catch (FTPException&)
291 {
292 }
293 server.clearCommands();
294
295 // createDirectory
296 server.addResponse("257 dir created");
297 session.createDirectory("foo");
298 cmd = server.popCommand();
299 assertTrue (cmd == "MKD foo");
300
301 // createDirectory (failing)
302 server.addResponse("550 exists");
303 try
304 {
305 session.createDirectory("foo");
306 fail("not found - must throw");
307 }
308 catch (FTPException&)
309 {
310 }
311 server.clearCommands();
312
313 // removeDirectory
314 server.addResponse("250 RMD OK");
315 session.removeDirectory("foo");
316 cmd = server.popCommand();
317 assertTrue (cmd == "RMD foo");
318
319 // removeDirectory (failing)
320 server.addResponse("550 not found");
321 try
322 {
323 session.removeDirectory("foo");
324 fail("not found - must throw");
325 }
326 catch (FTPException&)
327 {
328 }
329 server.clearCommands();
330
331 server.addResponse("221 Good Bye");
332 session.close();
333}
334
335
336void FTPClientSessionTest::testDownloadPORT()
337{
338 DialogServer server;
339 server.addResponse("220 localhost FTP ready");
340 server.addResponse("331 Password required");
341 server.addResponse("230 Welcome");
342 server.addResponse("200 Type set to I");
343 FTPClientSession session("127.0.0.1", server.port());
344 session.setPassive(false);
345 session.login("user", "password");
346 server.clearCommands();
347
348 server.addResponse("500 EPRT not understood");
349 server.addResponse("200 PORT OK");
350 server.addResponse("150 Sending data\r\n226 Transfer complete");
351
352 ActiveDownloader dl(session);
353 ActiveResult<std::string> result = dl.download("test.txt");
354
355 std::string cmd = server.popCommandWait();
356 assertTrue (cmd.substr(0, 4) == "EPRT");
357
358 cmd = server.popCommandWait();
359 assertTrue (cmd.substr(0, 4) == "PORT");
360
361 std::string dummy;
362 int x, lo, hi;
363 for (std::string::iterator it = cmd.begin(); it != cmd.end(); ++it)
364 {
365 if (*it == ',') *it = ' ';
366 }
367 std::istringstream istr(cmd);
368 istr >> dummy >> x >> x >> x >> x >> hi >> lo;
369 int port = hi*256 + lo;
370
371 cmd = server.popCommandWait();
372 assertTrue (cmd == "RETR test.txt");
373
374 SocketAddress sa("127.0.0.1", (Poco::UInt16) port);
375 DialogSocket dataSock;
376 dataSock.connect(sa);
377
378 std::string data("This is some data");
379 dataSock.sendString(data);
380 dataSock.close();
381
382 result.wait();
383 std::string received = result.data();
384 assertTrue (received == data);
385
386 server.addResponse("221 Good Bye");
387 session.close();
388}
389
390
391void FTPClientSessionTest::testDownloadEPRT()
392{
393 DialogServer server;
394 server.addResponse("220 localhost FTP ready");
395 server.addResponse("331 Password required");
396 server.addResponse("230 Welcome");
397 server.addResponse("200 Type set to I");
398 FTPClientSession session("127.0.0.1", server.port());
399 session.setPassive(false);
400 session.login("user", "password");
401 server.clearCommands();
402
403 server.addResponse("200 EPRT OK");
404 server.addResponse("150 Sending data\r\n226 Transfer complete");
405
406 ActiveDownloader dl(session);
407 ActiveResult<std::string> result = dl.download("test.txt");
408
409 std::string cmd = server.popCommandWait();
410 assertTrue (cmd.substr(0, 4) == "EPRT");
411
412 std::string dummy;
413 char c;
414 int d;
415 int port;
416 std::istringstream istr(cmd);
417 istr >> dummy >> c >> d >> c >> d >> c >> d >> c >> d >> c >> d >> c >> port >> c;
418
419 cmd = server.popCommandWait();
420 assertTrue (cmd == "RETR test.txt");
421
422 SocketAddress sa("127.0.0.1", (Poco::UInt16) port);
423 DialogSocket dataSock;
424 dataSock.connect(sa);
425
426 std::string data("This is some data");
427 dataSock.sendString(data);
428 dataSock.close();
429
430 result.wait();
431 std::string received = result.data();
432 assertTrue (received == data);
433
434 server.addResponse("221 Good Bye");
435 session.close();
436}
437
438
439void FTPClientSessionTest::testDownloadPASV()
440{
441 DialogServer server;
442 server.addResponse("220 localhost FTP ready");
443 server.addResponse("331 Password required");
444 server.addResponse("230 Welcome");
445 server.addResponse("200 Type set to I");
446 FTPClientSession session("127.0.0.1", server.port());
447 session.login("user", "password");
448 server.clearCommands();
449
450 server.addResponse("500 EPSV not understood");
451
452 DialogServer dataServer(false);
453 Poco::UInt16 dataServerPort = dataServer.port();
454 dataServer.addResponse("This is some data");
455 std::ostringstream pasv;
456 pasv << "227 Entering Passive Mode (127,0,0,1," << (dataServerPort/256) << "," << (dataServerPort % 256) << ")";
457 server.addResponse(pasv.str());
458 server.addResponse("150 sending data\r\n226 Transfer complete");
459
460 std::istream& istr = session.beginDownload("test.txt");
461 std::ostringstream dataStr;
462 StreamCopier::copyStream(istr, dataStr);
463 session.endDownload();
464 std::string s(dataStr.str());
465 assertTrue (s == "This is some data\r\n");
466
467 server.addResponse("221 Good Bye");
468 session.close();
469}
470
471
472void FTPClientSessionTest::testDownloadEPSV()
473{
474 DialogServer server;
475 server.addResponse("220 localhost FTP ready");
476 server.addResponse("331 Password required");
477 server.addResponse("230 Welcome");
478 server.addResponse("200 Type set to I");
479 FTPClientSession session("127.0.0.1", server.port());
480 session.login("user", "password");
481 server.clearCommands();
482
483 DialogServer dataServer(false);
484 dataServer.addResponse("This is some data");
485 std::ostringstream epsv;
486 epsv << "229 Entering Extended Passive Mode (|||" << dataServer.port() << "|)";
487 server.addResponse(epsv.str());
488 server.addResponse("150 sending data\r\n226 Transfer complete");
489
490 std::istream& istr = session.beginDownload("test.txt");
491 std::ostringstream dataStr;
492 StreamCopier::copyStream(istr, dataStr);
493 session.endDownload();
494 std::string s(dataStr.str());
495 assertTrue (s == "This is some data\r\n");
496
497 std::string cmd = server.popCommand();
498 assertTrue (cmd.substr(0, 4) == "EPSV");
499 cmd = server.popCommand();
500 assertTrue (cmd == "RETR test.txt");
501
502 server.addResponse("221 Good Bye");
503 session.close();
504}
505
506
507void FTPClientSessionTest::testUpload()
508{
509 DialogServer server;
510 server.addResponse("220 localhost FTP ready");
511 server.addResponse("331 Password required");
512 server.addResponse("230 Welcome");
513 server.addResponse("200 Type set to I");
514 FTPClientSession session("127.0.0.1", server.port());
515 session.login("user", "password");
516 server.clearCommands();
517
518 DialogServer dataServer;
519 std::ostringstream epsv;
520 epsv << "229 Entering Extended Passive Mode (|||" << dataServer.port() << "|)";
521 server.addResponse(epsv.str());
522 server.addResponse("150 send data\r\n226 Transfer complete");
523
524 std::ostream& ostr = session.beginUpload("test.txt");
525 ostr << "This is some data\r\n";
526 session.endUpload();
527 std::string s(dataServer.popCommandWait());
528 assertTrue (s == "This is some data");
529
530 std::string cmd = server.popCommand();
531 assertTrue (cmd.substr(0, 4) == "EPSV");
532 cmd = server.popCommand();
533 assertTrue (cmd == "STOR test.txt");
534
535 server.addResponse("221 Good Bye");
536 session.close();
537}
538
539
540void FTPClientSessionTest::testList()
541{
542 DialogServer server;
543 server.addResponse("220 localhost FTP ready");
544 server.addResponse("331 Password required");
545 server.addResponse("230 Welcome");
546 server.addResponse("200 Type set to I");
547 FTPClientSession session("127.0.0.1", server.port());
548 session.login("user", "password");
549 server.clearCommands();
550
551 DialogServer dataServer(false);
552 dataServer.addResponse("file1\r\nfile2");
553 std::ostringstream epsv;
554 epsv << "229 Entering Extended Passive Mode (|||" << dataServer.port() << "|)";
555 server.addResponse(epsv.str());
556 server.addResponse("150 sending data\r\n226 Transfer complete");
557
558 std::istream& istr = session.beginList();
559 std::ostringstream dataStr;
560 StreamCopier::copyStream(istr, dataStr);
561 session.endList();
562 std::string s(dataStr.str());
563 assertTrue (s == "file1\r\nfile2\r\n");
564
565 std::string cmd = server.popCommand();
566 assertTrue (cmd.substr(0, 4) == "EPSV");
567 cmd = server.popCommand();
568 assertTrue (cmd == "NLST");
569
570 server.addResponse("221 Good Bye");
571 session.close();
572}
573
574
575void FTPClientSessionTest::setUp()
576{
577}
578
579
580void FTPClientSessionTest::tearDown()
581{
582}
583
584
585CppUnit::Test* FTPClientSessionTest::suite()
586{
587 CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FTPClientSessionTest");
588
589 CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin1);
590 CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin2);
591 CppUnit_addTest(pSuite, FTPClientSessionTest, testLogin3);
592 CppUnit_addTest(pSuite, FTPClientSessionTest, testLoginFailed1);
593 CppUnit_addTest(pSuite, FTPClientSessionTest, testLoginFailed2);
594 CppUnit_addTest(pSuite, FTPClientSessionTest, testCommands);
595 CppUnit_addTest(pSuite, FTPClientSessionTest, testDownloadPORT);
596 CppUnit_addTest(pSuite, FTPClientSessionTest, testDownloadEPRT);
597 CppUnit_addTest(pSuite, FTPClientSessionTest, testDownloadPASV);
598 CppUnit_addTest(pSuite, FTPClientSessionTest, testDownloadEPSV);
599 CppUnit_addTest(pSuite, FTPClientSessionTest, testUpload);
600 CppUnit_addTest(pSuite, FTPClientSessionTest, testList);
601
602 return pSuite;
603}
604