2008-09-08 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

Implemented the ability to get timestamp from remote FTP server 
using
	MDTM command described in RFC3659.
	* src/FtpConnection.cc
	* src/FtpConnection.h
	* src/FtpNegotiationCommand.cc
	* src/FtpNegotiationCommand.h
	* test/FtpConnectionTest.cc
	* test/Makefile.am
This commit is contained in:
Tatsuhiro Tsujikawa 2008-09-08 13:06:44 +00:00
parent dbc8f5b737
commit eb652b570e
8 changed files with 212 additions and 9 deletions

View File

@ -1,3 +1,14 @@
2008-09-08 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
Implemented the ability to get timestamp from remote FTP server using
MDTM command described in RFC3659.
* src/FtpConnection.cc
* src/FtpConnection.h
* src/FtpNegotiationCommand.cc
* src/FtpNegotiationCommand.h
* test/FtpConnectionTest.cc
* test/Makefile.am
2008-09-07 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com> 2008-09-07 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
Implemented the ability to get timestamp from remote HTTP server and Implemented the ability to get timestamp from remote HTTP server and

View File

@ -47,6 +47,7 @@
#include "DlAbortEx.h" #include "DlAbortEx.h"
#include "Socket.h" #include "Socket.h"
#include "A2STR.h" #include "A2STR.h"
#include <cstring>
namespace aria2 { namespace aria2 {
@ -95,6 +96,13 @@ void FtpConnection::sendCwd() const
socket->writeData(request); socket->writeData(request);
} }
void FtpConnection::sendMdtm() const
{
std::string request = "MDTM "+Util::urlencode(req->getFile())+"\r\n";
logger->info(MSG_SENDING_REQUEST, cuid, request.c_str());
socket->writeData(request);
}
void FtpConnection::sendSize() const void FtpConnection::sendSize() const
{ {
std::string request = "SIZE "+Util::urldecode(req->getFile())+"\r\n"; std::string request = "SIZE "+Util::urldecode(req->getFile())+"\r\n";
@ -257,6 +265,26 @@ unsigned int FtpConnection::receiveSizeResponse(uint64_t& size)
} }
} }
unsigned int FtpConnection::receiveMdtmResponse(Time& time)
{
// MDTM command, specified in RFC3659.
std::pair<unsigned int, std::string> response;
if(bulkReceiveResponse(response)) {
if(response.first == 213) {
char buf[15]; // YYYYMMDDhhmmss+\0, milli second part is dropped.
sscanf(response.second.c_str(), "%*u %14s", buf);
if(strlen(buf) == 14) {
time = Time::parse(buf, "%Y%m%d%H%M%S");
} else {
time.setTimeInSec(-1);
}
}
return response.first;
} else {
return 0;
}
}
unsigned int FtpConnection::receivePasvResponse(std::pair<std::string, uint16_t>& dest) unsigned int FtpConnection::receivePasvResponse(std::pair<std::string, uint16_t>& dest)
{ {
std::pair<unsigned int, std::string> response; std::pair<unsigned int, std::string> response;

View File

@ -37,6 +37,7 @@
#include "common.h" #include "common.h"
#include "SharedHandle.h" #include "SharedHandle.h"
#include "TimeA2.h"
#include <utility> #include <utility>
#include <string> #include <string>
@ -74,6 +75,7 @@ public:
void sendPass() const; void sendPass() const;
void sendType() const; void sendType() const;
void sendCwd() const; void sendCwd() const;
void sendMdtm() const;
void sendSize() const; void sendSize() const;
void sendPasv() const; void sendPasv() const;
SharedHandle<SocketCore> sendPort() const; SharedHandle<SocketCore> sendPort() const;
@ -82,6 +84,13 @@ public:
unsigned int receiveResponse(); unsigned int receiveResponse();
unsigned int receiveSizeResponse(uint64_t& size); unsigned int receiveSizeResponse(uint64_t& size);
// Returns status code of MDTM reply. If the status code is 213, parses
// time-val and store it in time.
// If a code other than 213 is returned, time is not touched.
// Expect MDTM reply is YYYYMMDDhhmmss in GMT. If status is 213 but returned
// date cannot be parsed, then executes time.setTimeInSec(-1).
// If reply is not received yet, returns 0.
unsigned int receiveMdtmResponse(Time& time);
unsigned int receivePasvResponse(std::pair<std::string, uint16_t>& dest); unsigned int receivePasvResponse(std::pair<std::string, uint16_t>& dest);
}; };

View File

@ -202,6 +202,45 @@ bool FtpNegotiationCommand::recvCwd() {
poolConnection(); poolConnection();
throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str()); throw DlAbortEx(StringFormat(EX_BAD_STATUS, status).str());
} }
if(e->option->getAsBool(PREF_REMOTE_TIME)) {
sequence = SEQ_SEND_MDTM;
} else {
sequence = SEQ_SEND_SIZE;
}
return true;
}
bool FtpNegotiationCommand::sendMdtm()
{
ftp->sendMdtm();
sequence = SEQ_RECV_MDTM;
return false;
}
bool FtpNegotiationCommand::recvMdtm()
{
Time lastModifiedTime(-1);
unsigned int status = ftp->receiveMdtmResponse(lastModifiedTime);
if(status == 0) {
return false;
}
if(status == 213) {
if(lastModifiedTime.good()) {
_requestGroup->updateLastModifiedTime(lastModifiedTime);
time_t t = lastModifiedTime.getTime();
struct tm* tms = gmtime(&t); // returned struct is statically allocated.
if(tms) {
logger->debug("MDTM result was parsed as: %s GMT", asctime(tms));
} else {
logger->debug("gmtime() failed for MDTM result.");
}
} else {
logger->debug("MDTM response was returned, but it seems not to be a time"
" value as in specified in RFC3659.");
}
} else {
logger->info("CUID#%d - MDTM command failed.", cuid);
}
sequence = SEQ_SEND_SIZE; sequence = SEQ_SEND_SIZE;
return true; return true;
} }
@ -445,6 +484,10 @@ bool FtpNegotiationCommand::processSequence(const SegmentHandle& segment) {
return sendCwd(); return sendCwd();
case SEQ_RECV_CWD: case SEQ_RECV_CWD:
return recvCwd(); return recvCwd();
case SEQ_SEND_MDTM:
return sendMdtm();
case SEQ_RECV_MDTM:
return recvMdtm();
case SEQ_SEND_SIZE: case SEQ_SEND_SIZE:
return sendSize(); return sendSize();
case SEQ_RECV_SIZE: case SEQ_RECV_SIZE:

View File

@ -54,6 +54,8 @@ public:
SEQ_RECV_TYPE, SEQ_RECV_TYPE,
SEQ_SEND_CWD, SEQ_SEND_CWD,
SEQ_RECV_CWD, SEQ_RECV_CWD,
SEQ_SEND_MDTM,
SEQ_RECV_MDTM,
SEQ_SEND_SIZE, SEQ_SEND_SIZE,
SEQ_RECV_SIZE, SEQ_RECV_SIZE,
SEQ_SEND_PORT, SEQ_SEND_PORT,
@ -82,6 +84,8 @@ private:
bool recvType(); bool recvType();
bool sendCwd(); bool sendCwd();
bool recvCwd(); bool recvCwd();
bool sendMdtm();
bool recvMdtm();
bool sendSize(); bool sendSize();
bool recvSize(); bool recvSize();
bool sendPort(); bool sendPort();

105
test/FtpConnectionTest.cc Normal file
View File

@ -0,0 +1,105 @@
#include "FtpConnection.h"
#include "Exception.h"
#include "Util.h"
#include "SocketCore.h"
#include "Request.h"
#include "Option.h"
#include <iostream>
#include <cppunit/extensions/HelperMacros.h>
namespace aria2 {
class FtpConnectionTest:public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(FtpConnectionTest);
CPPUNIT_TEST(testSendMdtm);
CPPUNIT_TEST(testReceiveMdtmResponse);
CPPUNIT_TEST_SUITE_END();
private:
SharedHandle<SocketCore> _serverSocket;
uint16_t _listenPort;
SharedHandle<FtpConnection> _ftp;
Option _option;
public:
void setUp()
{
//_ftpServerSocket.reset(new SocketCore());
SharedHandle<SocketCore> listenSocket(new SocketCore());
listenSocket->bind(0);
listenSocket->beginListen();
std::pair<std::string, uint16_t> addrinfo;
listenSocket->getAddrInfo(addrinfo);
_listenPort = addrinfo.second;
SharedHandle<Request> req(new Request());
req->setUrl("ftp://localhost/dir/file.img");
SharedHandle<SocketCore> clientSocket(new SocketCore());
clientSocket->establishConnection("127.0.0.1", _listenPort);
while(!clientSocket->isWritable(0));
clientSocket->setBlockingMode();
_serverSocket.reset(listenSocket->acceptConnection());
_ftp.reset(new FtpConnection(1, clientSocket, req, &_option));
}
void tearDown() {}
void testSendMdtm();
void testReceiveMdtmResponse();
};
CPPUNIT_TEST_SUITE_REGISTRATION(FtpConnectionTest);
void FtpConnectionTest::testSendMdtm()
{
_ftp->sendMdtm();
char data[32];
size_t len = sizeof(data);
_serverSocket->readData(data, len);
CPPUNIT_ASSERT_EQUAL((size_t)15, len);
data[len] = '\0';
CPPUNIT_ASSERT_EQUAL(std::string("MDTM file.img\r\n"), std::string(data));
}
void FtpConnectionTest::testReceiveMdtmResponse()
{
{
Time t;
_serverSocket->writeData("213 20080908124312");
CPPUNIT_ASSERT_EQUAL((unsigned int)0, _ftp->receiveMdtmResponse(t));
_serverSocket->writeData("\r\n");
CPPUNIT_ASSERT_EQUAL((unsigned int)213, _ftp->receiveMdtmResponse(t));
CPPUNIT_ASSERT_EQUAL((time_t)1220877792, t.getTime());
}
{
// see milli second part is ignored
Time t;
_serverSocket->writeData("213 20080908124312.014\r\n");
CPPUNIT_ASSERT_EQUAL((unsigned int)213, _ftp->receiveMdtmResponse(t));
CPPUNIT_ASSERT_EQUAL((time_t)1220877792, t.getTime());
}
{
// hhmmss part is missing
Time t;
_serverSocket->writeData("213 20080908\r\n");
CPPUNIT_ASSERT_EQUAL((unsigned int)213, _ftp->receiveMdtmResponse(t));
CPPUNIT_ASSERT_EQUAL((time_t)-1, t.getTime());
}
{
// invalid month: 19
Time t;
_serverSocket->writeData("213 20081908124312\r\n");
CPPUNIT_ASSERT_EQUAL((unsigned int)213, _ftp->receiveMdtmResponse(t));
CPPUNIT_ASSERT_EQUAL((time_t)-1, t.getTime());
}
{
Time t;
_serverSocket->writeData("550 File Not Found\r\n");
CPPUNIT_ASSERT_EQUAL((unsigned int)550, _ftp->receiveMdtmResponse(t));
}
}
} // namespace aria2

View File

@ -62,7 +62,8 @@ aria2c_SOURCES = AllTest.cc\
CookieTest.cc\ CookieTest.cc\
CookieStorageTest.cc\ CookieStorageTest.cc\
TimeTest.cc\ TimeTest.cc\
CopyDiskAdaptorTest.cc CopyDiskAdaptorTest.cc\
FtpConnectionTest.cc
if HAVE_LIBZ if HAVE_LIBZ
aria2c_SOURCES += GZipDecoderTest.cc aria2c_SOURCES += GZipDecoderTest.cc

View File

@ -194,8 +194,9 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \ ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \
ServerStatTest.cc NsCookieParserTest.cc \ ServerStatTest.cc NsCookieParserTest.cc \
DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \ DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \
TimeTest.cc CopyDiskAdaptorTest.cc GZipDecoderTest.cc \ TimeTest.cc CopyDiskAdaptorTest.cc FtpConnectionTest.cc \
Sqlite3MozCookieParserTest.cc MessageDigestHelperTest.cc \ GZipDecoderTest.cc Sqlite3MozCookieParserTest.cc \
MessageDigestHelperTest.cc \
IteratableChunkChecksumValidatorTest.cc \ IteratableChunkChecksumValidatorTest.cc \
IteratableChecksumValidatorTest.cc BtAllowedFastMessageTest.cc \ IteratableChecksumValidatorTest.cc BtAllowedFastMessageTest.cc \
BtBitfieldMessageTest.cc BtCancelMessageTest.cc \ BtBitfieldMessageTest.cc BtCancelMessageTest.cc \
@ -367,8 +368,8 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
NsCookieParserTest.$(OBJEXT) DirectDiskAdaptorTest.$(OBJEXT) \ NsCookieParserTest.$(OBJEXT) DirectDiskAdaptorTest.$(OBJEXT) \
CookieTest.$(OBJEXT) CookieStorageTest.$(OBJEXT) \ CookieTest.$(OBJEXT) CookieStorageTest.$(OBJEXT) \
TimeTest.$(OBJEXT) CopyDiskAdaptorTest.$(OBJEXT) \ TimeTest.$(OBJEXT) CopyDiskAdaptorTest.$(OBJEXT) \
$(am__objects_1) $(am__objects_2) $(am__objects_3) \ FtpConnectionTest.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
$(am__objects_4) $(am__objects_5) $(am__objects_3) $(am__objects_4) $(am__objects_5)
aria2c_OBJECTS = $(am_aria2c_OBJECTS) aria2c_OBJECTS = $(am_aria2c_OBJECTS)
am__DEPENDENCIES_1 = am__DEPENDENCIES_1 =
aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1) aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@ -590,9 +591,9 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \ ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \
ServerStatTest.cc NsCookieParserTest.cc \ ServerStatTest.cc NsCookieParserTest.cc \
DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \ DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \
TimeTest.cc CopyDiskAdaptorTest.cc $(am__append_1) \ TimeTest.cc CopyDiskAdaptorTest.cc FtpConnectionTest.cc \
$(am__append_2) $(am__append_3) $(am__append_4) \ $(am__append_1) $(am__append_2) $(am__append_3) \
$(am__append_5) $(am__append_4) $(am__append_5)
#aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
#aria2c_LDFLAGS = ${CPPUNIT_LIBS} #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@ -759,6 +760,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeatureConfigTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeatureConfigTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileEntryTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileEntryTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FtpConnectionTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GZipDecoderTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GZipDecoderTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrowSegmentTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GrowSegmentTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HandshakeExtensionMessageTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/HandshakeExtensionMessageTest.Po@am__quote@