mirror of https://github.com/aria2/aria2
311 lines
9.0 KiB
C++
311 lines
9.0 KiB
C++
/* <!-- copyright */
|
|
/*
|
|
* aria2 - The high speed download utility
|
|
*
|
|
* Copyright (C) 2006 Tatsuhiro Tsujikawa
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* In addition, as a special exception, the copyright holders give
|
|
* permission to link the code of portions of this program with the
|
|
* OpenSSL library under certain conditions as described in each
|
|
* individual source file, and distribute linked combinations
|
|
* including the two.
|
|
* You must obey the GNU General Public License in all respects
|
|
* for all of the code used other than OpenSSL. If you modify
|
|
* file(s) with this exception, you may extend this exception to your
|
|
* version of the file(s), but you are not obligated to do so. If you
|
|
* do not wish to do so, delete this exception statement from your
|
|
* version. If you delete this exception statement from all source
|
|
* files in the program, then also delete it here.
|
|
*/
|
|
/* copyright --> */
|
|
#include "DefaultBtAnnounce.h"
|
|
#include "LogFactory.h"
|
|
#include "Logger.h"
|
|
#include "PeerListProcessor.h"
|
|
#include "util.h"
|
|
#include "prefs.h"
|
|
#include "DlAbortEx.h"
|
|
#include "message.h"
|
|
#include "SimpleRandomizer.h"
|
|
#include "DownloadContext.h"
|
|
#include "PieceStorage.h"
|
|
#include "BtRuntime.h"
|
|
#include "PeerStorage.h"
|
|
#include "Peer.h"
|
|
#include "Option.h"
|
|
#include "StringFormat.h"
|
|
#include "A2STR.h"
|
|
#include "Request.h"
|
|
#include "bencode.h"
|
|
#include "bittorrent_helper.h"
|
|
|
|
namespace aria2 {
|
|
|
|
DefaultBtAnnounce::DefaultBtAnnounce
|
|
(const SharedHandle<DownloadContext>& downloadContext,
|
|
const Option* option):
|
|
_downloadContext(downloadContext),
|
|
trackers(0),
|
|
interval(DEFAULT_ANNOUNCE_INTERVAL),
|
|
minInterval(DEFAULT_ANNOUNCE_INTERVAL),
|
|
_userDefinedInterval(0),
|
|
complete(0),
|
|
incomplete(0),
|
|
announceList(downloadContext->getAttribute(bittorrent::BITTORRENT)[bittorrent::ANNOUNCE_LIST]),
|
|
option(option),
|
|
logger(LogFactory::getInstance()),
|
|
_randomizer(SimpleRandomizer::getInstance())
|
|
{
|
|
prevAnnounceTime.setTimeInSec(0);
|
|
generateKey();
|
|
}
|
|
|
|
DefaultBtAnnounce::~DefaultBtAnnounce() {
|
|
}
|
|
|
|
void DefaultBtAnnounce::generateKey()
|
|
{
|
|
key = util::randomAlpha(8, _randomizer);
|
|
}
|
|
|
|
bool DefaultBtAnnounce::isDefaultAnnounceReady() {
|
|
return (trackers == 0 &&
|
|
prevAnnounceTime.elapsed(_userDefinedInterval==0?
|
|
minInterval:_userDefinedInterval) &&
|
|
!announceList.allTiersFailed());
|
|
}
|
|
|
|
bool DefaultBtAnnounce::isStoppedAnnounceReady() {
|
|
return (trackers == 0 &&
|
|
btRuntime->isHalt() &&
|
|
announceList.countStoppedAllowedTier());
|
|
}
|
|
|
|
bool DefaultBtAnnounce::isCompletedAnnounceReady() {
|
|
return (trackers == 0 &&
|
|
pieceStorage->allDownloadFinished() &&
|
|
announceList.countCompletedAllowedTier());
|
|
}
|
|
|
|
bool DefaultBtAnnounce::isAnnounceReady() {
|
|
return
|
|
isStoppedAnnounceReady() ||
|
|
isCompletedAnnounceReady() ||
|
|
isDefaultAnnounceReady();
|
|
}
|
|
|
|
static bool uriHasQuery(const std::string& uri)
|
|
{
|
|
Request req;
|
|
req.setUrl(uri);
|
|
return !req.getQuery().empty();
|
|
}
|
|
|
|
std::string DefaultBtAnnounce::getAnnounceUrl() {
|
|
if(isStoppedAnnounceReady()) {
|
|
if(!announceList.currentTierAcceptsStoppedEvent()) {
|
|
announceList.moveToStoppedAllowedTier();
|
|
}
|
|
announceList.setEvent(AnnounceTier::STOPPED);
|
|
} else if(isCompletedAnnounceReady()) {
|
|
if(!announceList.currentTierAcceptsCompletedEvent()) {
|
|
announceList.moveToCompletedAllowedTier();
|
|
}
|
|
announceList.setEvent(AnnounceTier::COMPLETED);
|
|
} else if(isDefaultAnnounceReady()) {
|
|
// If download completed before "started" event is sent to a tracker,
|
|
// we change the event to something else to prevent us from
|
|
// sending "completed" event.
|
|
if(pieceStorage->allDownloadFinished() &&
|
|
announceList.getEvent() == AnnounceTier::STARTED) {
|
|
announceList.setEvent(AnnounceTier::STARTED_AFTER_COMPLETION);
|
|
}
|
|
} else {
|
|
return A2STR::NIL;
|
|
}
|
|
unsigned int numWant = 50;
|
|
if(!btRuntime->lessThanMinPeers() || btRuntime->isHalt()) {
|
|
numWant = 0;
|
|
}
|
|
TransferStat stat = peerStorage->calculateStat();
|
|
uint64_t left =
|
|
pieceStorage->getTotalLength()-pieceStorage->getCompletedLength();
|
|
std::string url = announceList.getAnnounce();
|
|
url += uriHasQuery(url) ? "&" : "?";
|
|
url += "info_hash=";
|
|
url += util::torrentUrlencode(bittorrent::getInfoHash(_downloadContext),
|
|
INFO_HASH_LENGTH);
|
|
url += "&peer_id=";
|
|
url += util::torrentUrlencode(bittorrent::getStaticPeerId(), PEER_ID_LENGTH);
|
|
url += "&uploaded=";
|
|
url += util::uitos(stat.getSessionUploadLength());
|
|
url += "&downloaded=";
|
|
url += util::uitos(stat.getSessionDownloadLength());
|
|
url += "&left=";
|
|
url += util::uitos(left);
|
|
url += "&compact=1";
|
|
url += "&key=";
|
|
url += key;
|
|
url += "&numwant=";
|
|
url += util::uitos(numWant);
|
|
url += "&no_peer_id=1";
|
|
if(btRuntime->getListenPort() > 0) {
|
|
url += "&port=";
|
|
url += util::uitos(btRuntime->getListenPort());
|
|
}
|
|
std::string event = announceList.getEventString();
|
|
if(!event.empty()) {
|
|
url += "&event=";
|
|
url += event;
|
|
}
|
|
if(!trackerId.empty()) {
|
|
url += "&trackerid="+util::torrentUrlencode(trackerId);
|
|
}
|
|
if(option->getAsBool(PREF_BT_REQUIRE_CRYPTO)) {
|
|
url += "&requirecrypto=1";
|
|
} else {
|
|
url += "&supportcrypto=1";
|
|
}
|
|
if(!option->blank(PREF_BT_EXTERNAL_IP)) {
|
|
url += "&ip=";
|
|
url += option->get(PREF_BT_EXTERNAL_IP);
|
|
}
|
|
return url;
|
|
}
|
|
|
|
void DefaultBtAnnounce::announceStart() {
|
|
trackers++;
|
|
}
|
|
|
|
void DefaultBtAnnounce::announceSuccess() {
|
|
trackers = 0;
|
|
announceList.announceSuccess();
|
|
}
|
|
|
|
void DefaultBtAnnounce::announceFailure() {
|
|
trackers = 0;
|
|
announceList.announceFailure();
|
|
}
|
|
|
|
bool DefaultBtAnnounce::isAllAnnounceFailed() {
|
|
return announceList.allTiersFailed();
|
|
}
|
|
|
|
void DefaultBtAnnounce::resetAnnounce() {
|
|
prevAnnounceTime.reset();
|
|
announceList.resetTier();
|
|
}
|
|
|
|
void
|
|
DefaultBtAnnounce::processAnnounceResponse(const unsigned char* trackerResponse,
|
|
size_t trackerResponseLength)
|
|
{
|
|
logger->debug("Now processing tracker response.");
|
|
const BDE dict =
|
|
bencode::decode(trackerResponse, trackerResponseLength);
|
|
if(!dict.isDict()) {
|
|
throw DL_ABORT_EX(MSG_NULL_TRACKER_RESPONSE);
|
|
}
|
|
const BDE& failure = dict[BtAnnounce::FAILURE_REASON];
|
|
if(failure.isString()) {
|
|
throw DL_ABORT_EX
|
|
(StringFormat(EX_TRACKER_FAILURE, failure.s().c_str()).str());
|
|
}
|
|
const BDE& warn = dict[BtAnnounce::WARNING_MESSAGE];
|
|
if(warn.isString()) {
|
|
logger->warn(MSG_TRACKER_WARNING_MESSAGE, warn.s().c_str());
|
|
}
|
|
const BDE& tid = dict[BtAnnounce::TRACKER_ID];
|
|
if(tid.isString()) {
|
|
trackerId = tid.s();
|
|
logger->debug("Tracker ID:%s", trackerId.c_str());
|
|
}
|
|
const BDE& ival = dict[BtAnnounce::INTERVAL];
|
|
if(ival.isInteger() && ival.i() > 0) {
|
|
interval = ival.i();
|
|
logger->debug("Interval:%d", interval);
|
|
}
|
|
const BDE& mival = dict[BtAnnounce::MIN_INTERVAL];
|
|
if(mival.isInteger() && mival.i() > 0) {
|
|
minInterval = mival.i();
|
|
logger->debug("Min interval:%d", minInterval);
|
|
if(minInterval > interval) {
|
|
minInterval = interval;
|
|
}
|
|
} else {
|
|
// Use interval as a minInterval if minInterval is not supplied.
|
|
minInterval = interval;
|
|
}
|
|
const BDE& comp = dict[BtAnnounce::COMPLETE];
|
|
if(comp.isInteger()) {
|
|
complete = comp.i();
|
|
logger->debug("Complete:%d", complete);
|
|
}
|
|
const BDE& incomp = dict[BtAnnounce::INCOMPLETE];
|
|
if(incomp.isInteger()) {
|
|
incomplete = incomp.i();
|
|
logger->debug("Incomplete:%d", incomplete);
|
|
}
|
|
const BDE& peerData = dict[BtAnnounce::PEERS];
|
|
if(peerData.isNone()) {
|
|
logger->info(MSG_NO_PEER_LIST_RECEIVED);
|
|
} else {
|
|
if(!btRuntime->isHalt() && btRuntime->lessThanMinPeers()) {
|
|
std::deque<SharedHandle<Peer> > peers;
|
|
PeerListProcessor().extractPeer(peerData, std::back_inserter(peers));
|
|
peerStorage->addPeer(peers);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DefaultBtAnnounce::noMoreAnnounce() {
|
|
return (trackers == 0 &&
|
|
btRuntime->isHalt() &&
|
|
!announceList.countStoppedAllowedTier());
|
|
}
|
|
|
|
void DefaultBtAnnounce::shuffleAnnounce() {
|
|
announceList.shuffle();
|
|
}
|
|
|
|
void DefaultBtAnnounce::setRandomizer(const RandomizerHandle& randomizer)
|
|
{
|
|
_randomizer = randomizer;
|
|
}
|
|
|
|
void DefaultBtAnnounce::setBtRuntime(const BtRuntimeHandle& btRuntime)
|
|
{
|
|
this->btRuntime = btRuntime;
|
|
}
|
|
|
|
void DefaultBtAnnounce::setPieceStorage(const PieceStorageHandle& pieceStorage)
|
|
{
|
|
this->pieceStorage = pieceStorage;
|
|
}
|
|
|
|
void DefaultBtAnnounce::setPeerStorage(const PeerStorageHandle& peerStorage)
|
|
{
|
|
this->peerStorage = peerStorage;
|
|
}
|
|
|
|
void DefaultBtAnnounce::overrideMinInterval(time_t interval)
|
|
{
|
|
minInterval = interval;
|
|
}
|
|
|
|
} // namespace aria2
|