diff --git a/src/ChecksumCheckIntegrityEntry.cc b/src/ChecksumCheckIntegrityEntry.cc index 93c4f130..b02ae884 100644 --- a/src/ChecksumCheckIntegrityEntry.cc +++ b/src/ChecksumCheckIntegrityEntry.cc @@ -69,9 +69,7 @@ void ChecksumCheckIntegrityEntry::initValidator() void ChecksumCheckIntegrityEntry::onDownloadFinished (std::vector& commands, DownloadEngine* e) -{ - getRequestGroup()->getDownloadContext()->setChecksumVerified(true); -} +{} void ChecksumCheckIntegrityEntry::onDownloadIncomplete diff --git a/src/FtpNegotiationCommand.cc b/src/FtpNegotiationCommand.cc index 37980f58..0f39db13 100644 --- a/src/FtpNegotiationCommand.cc +++ b/src/FtpNegotiationCommand.cc @@ -74,6 +74,9 @@ #include "CheckIntegrityEntry.h" #include "error_code.h" #include "SocketRecvBuffer.h" +#ifdef ENABLE_MESSAGE_DIGEST +# include "ChecksumCheckIntegrityEntry.h" +#endif // ENABLE_MESSAGE_DIGEST namespace aria2 { @@ -398,21 +401,32 @@ bool FtpNegotiationCommand::onFileSizeDetermined(off_t totalLength) if(getDownloadContext()->knowsTotalLength() && getRequestGroup()->downloadFinishedByFileLength()) { - // TODO If metalink file does not contain size and it contains - // hash and file is not zero length, but remote server says the - // file size is 0, no hash check is performed in the current - // implementation. See also +#ifdef ENABLE_MESSAGE_DIGEST + // TODO Known issue: if .aria2 file exists, it will not be + // deleted on successful verification, because .aria2 file is + // not loaded. See also // HttpResponseCommand::handleOtherEncoding() getRequestGroup()->initPieceStorage(); - getPieceStorage()->markAllPiecesDone(); - getDownloadContext()->setChecksumVerified(true); - sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; - A2_LOG_NOTICE - (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, - getRequestGroup()->getGID(), - getRequestGroup()->getFirstFilePath().c_str())); + if(getDownloadContext()->isChecksumVerificationNeeded()) { + A2_LOG_DEBUG("Zero length file exists. Verify checksum."); + SharedHandle entry + (new ChecksumCheckIntegrityEntry(getRequestGroup())); + entry->initValidator(); + getPieceStorage()->getDiskAdaptor()->openExistingFile(); + getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry); + sequence_ = SEQ_EXIT; + } else +#endif // ENABLE_MESSAGE_DIGEST + { + getPieceStorage()->markAllPiecesDone(); + getDownloadContext()->setChecksumVerified(true); + sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; + A2_LOG_NOTICE + (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, + getRequestGroup()->getGID(), + getRequestGroup()->getFirstFilePath().c_str())); + } poolConnection(); - return false; } @@ -421,8 +435,25 @@ bool FtpNegotiationCommand::onFileSizeDetermined(off_t totalLength) getPieceStorage()->getDiskAdaptor()->initAndOpenFile(); if(getDownloadContext()->knowsTotalLength()) { - sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; - getPieceStorage()->markAllPiecesDone(); + A2_LOG_DEBUG("File length becomes zero and it means download completed."); +#ifdef ENABLE_MESSAGE_DIGEST + // TODO Known issue: if .aria2 file exists, it will not be + // deleted on successful verification, because .aria2 file is + // not loaded. See also + // HttpResponseCommand::handleOtherEncoding() + if(getDownloadContext()->isChecksumVerificationNeeded()) { + A2_LOG_DEBUG("Verify checksum for zero-length file"); + SharedHandle entry + (new ChecksumCheckIntegrityEntry(getRequestGroup())); + entry->initValidator(); + getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry); + sequence_ = SEQ_EXIT; + } else +#endif // ENABLE_MESSAGE_DIGEST + { + sequence_ = SEQ_DOWNLOAD_ALREADY_COMPLETED; + getPieceStorage()->markAllPiecesDone(); + } poolConnection(); return false; } diff --git a/src/HttpResponseCommand.cc b/src/HttpResponseCommand.cc index 70af2e89..f8711d12 100644 --- a/src/HttpResponseCommand.cc +++ b/src/HttpResponseCommand.cc @@ -74,7 +74,8 @@ #include "SocketRecvBuffer.h" #include "MetalinkHttpEntry.h" #ifdef ENABLE_MESSAGE_DIGEST -#include "Checksum.h" +# include "Checksum.h" +# include "ChecksumCheckIntegrityEntry.h" #endif // ENABLE_MESSAGE_DIGEST #ifdef HAVE_ZLIB # include "GZipDecodingStreamFilter.h" @@ -431,18 +432,28 @@ bool HttpResponseCommand::handleOtherEncoding // For zero-length file, check existing file comparing its size if(!chunkedUsed && getDownloadContext()->knowsTotalLength() && getRequestGroup()->downloadFinishedByFileLength()) { - // TODO If metalink file does not contain size and it contains - // hash and file is not zero length, but remote server says the - // file size is 0, no hash check is performed in the current - // implementation. See also - // FtpNegotiationCommand::onFileSizeDetermined() getRequestGroup()->initPieceStorage(); - getPieceStorage()->markAllPiecesDone(); - getDownloadContext()->setChecksumVerified(true); - A2_LOG_NOTICE - (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, - getRequestGroup()->getGID(), - getRequestGroup()->getFirstFilePath().c_str())); +#ifdef ENABLE_MESSAGE_DIGEST + // TODO Known issue: if .aria2 file exists, it will not be deleted + // on successful verification, because .aria2 file is not loaded. + // See also FtpNegotiationCommand::onFileSizeDetermined() + if(getDownloadContext()->isChecksumVerificationNeeded()) { + A2_LOG_DEBUG("Zero length file exists. Verify checksum."); + SharedHandle entry + (new ChecksumCheckIntegrityEntry(getRequestGroup())); + entry->initValidator(); + getPieceStorage()->getDiskAdaptor()->openExistingFile(); + getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry); + } else +#endif // ENABLE_MESSAGE_DIGEST + { + getPieceStorage()->markAllPiecesDone(); + getDownloadContext()->setChecksumVerified(true); + A2_LOG_NOTICE + (fmt(MSG_DOWNLOAD_ALREADY_COMPLETED, + getRequestGroup()->getGID(), + getRequestGroup()->getFirstFilePath().c_str())); + } poolConnection(); return true; } @@ -455,7 +466,22 @@ bool HttpResponseCommand::handleOtherEncoding // is called. So zero-length file is complete if chunked encoding is // not used. if(!chunkedUsed && getDownloadContext()->knowsTotalLength()) { - getRequestGroup()->getPieceStorage()->markAllPiecesDone(); + A2_LOG_DEBUG("File length becomes zero and it means download completed."); + // TODO Known issue: if .aria2 file exists, it will not be deleted + // on successful verification, because .aria2 file is not loaded. + // See also FtpNegotiationCommand::onFileSizeDetermined() +#ifdef ENABLE_MESSAGE_DIGEST + if(getDownloadContext()->isChecksumVerificationNeeded()) { + A2_LOG_DEBUG("Verify checksum for zero-length file"); + SharedHandle entry + (new ChecksumCheckIntegrityEntry(getRequestGroup())); + entry->initValidator(); + getDownloadEngine()->getCheckIntegrityMan()->pushEntry(entry); + } else +#endif // ENABLE_MESSAGE_DIGEST + { + getRequestGroup()->getPieceStorage()->markAllPiecesDone(); + } poolConnection(); return true; } diff --git a/src/IteratableChecksumValidator.cc b/src/IteratableChecksumValidator.cc index c086522b..4c489efa 100644 --- a/src/IteratableChecksumValidator.cc +++ b/src/IteratableChecksumValidator.cc @@ -61,23 +61,24 @@ IteratableChecksumValidator::~IteratableChecksumValidator() {} void IteratableChecksumValidator::validateChunk() { - if(!finished()) { - unsigned char buf[4096]; - size_t length = pieceStorage_->getDiskAdaptor()->readData - (buf, sizeof(buf), currentOffset_); - ctx_->update(buf, length); - currentOffset_ += length; - if(finished()) { - std::string actualDigest = ctx_->digest(); - if(dctx_->getDigest() == actualDigest) { - pieceStorage_->markAllPiecesDone(); - } else { - A2_LOG_INFO(fmt("Checksum validation failed. expected=%s, actual=%s", - util::toHex(dctx_->getDigest()).c_str(), - util::toHex(actualDigest).c_str())); - BitfieldMan bitfield(dctx_->getPieceLength(), dctx_->getTotalLength()); - pieceStorage_->setBitfield(bitfield.getBitfield(), bitfield.getBitfieldLength()); - } + // Don't guard with !finished() to allow zero-length file to be + // verified. + unsigned char buf[4096]; + size_t length = pieceStorage_->getDiskAdaptor()->readData + (buf, sizeof(buf), currentOffset_); + ctx_->update(buf, length); + currentOffset_ += length; + if(finished()) { + std::string actualDigest = ctx_->digest(); + if(dctx_->getDigest() == actualDigest) { + pieceStorage_->markAllPiecesDone(); + dctx_->setChecksumVerified(true); + } else { + A2_LOG_INFO(fmt("Checksum validation failed. expected=%s, actual=%s", + util::toHex(dctx_->getDigest()).c_str(), + util::toHex(actualDigest).c_str())); + BitfieldMan bitfield(dctx_->getPieceLength(), dctx_->getTotalLength()); + pieceStorage_->setBitfield(bitfield.getBitfield(), bitfield.getBitfieldLength()); } } } diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index 9d2597e3..931bf552 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -167,10 +167,11 @@ RequestGroup::RequestGroup(const SharedHandle