pull/1/head
Tatsuhiro Tsujikawa 2006-11-05 15:12:36 +00:00
parent ec642ad294
commit 45463f935f
12 changed files with 62 additions and 1292 deletions

View File

@ -3,6 +3,8 @@
To divide TorrentMan into 6 classes: BtContext, BtRuntime,
PeerStorage, PieceStorage, BtAnnounce and BtProgressInfoFile
* src/TorrentMan.h: Removed.
* src/TorrentMan.cc: Removed.
* src/TrackerWatcherComand.h: Made subclass of BtContextAwareCommand.
* src/SeedCheckCommand.cc: Use pieceStorage, btRuntime
* src/PeerAbstractCommand.h: Made subclass of BtContextAwareCommand.

View File

@ -1,7 +1,9 @@
../src/TorrentMan.cc \
../src/RequestInfo.h \
../src/message.h \
../src/DownloadEngineFactory.cc \
../src/TorrentRequestInfo.cc \
../src/UrlRequestInfo.cc \
../src/main.cc
../src/main.cc \
../src/DefaultPieceStorage.cc \
../src/DefaultBtAnnounce.cc \
../src/DefaultBtProgressInfoFile.cc

View File

@ -1,7 +1,10 @@
src/TorrentMan.cc
src/RequestInfo.h
src/message.h
src/DownloadEngineFactory.cc
src/TorrentRequestInfo.cc
src/UrlRequestInfo.cc
src/main.cc
src/DefaultPieceStorage.cc
src/DefaultBtAnnounce.cc
src/DefaultBtProgressInfoFile.cc

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: http://aria2.sourceforge.net/\n"
"POT-Creation-Date: 2006-10-18 23:54+0900\n"
"POT-Creation-Date: 2006-11-06 00:12+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -16,14 +16,6 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/TorrentMan.cc:685
msgid "Download of selected files was complete."
msgstr ""
#: src/TorrentMan.cc:688
msgid "The download was complete."
msgstr ""
#: src/RequestInfo.h:97
#, c-format
msgid ""
@ -352,12 +344,12 @@ msgstr ""
msgid "Failed to peek data, cause: %s"
msgstr ""
#: src/DownloadEngineFactory.cc:110
#: src/DownloadEngineFactory.cc:139
#, c-format
msgid "Errors occurred while binding port.\n"
msgstr ""
#: src/TorrentRequestInfo.cc:90
#: src/TorrentRequestInfo.cc:91
msgid "Files:"
msgstr ""
@ -850,3 +842,11 @@ msgstr ""
#: src/main.cc:773
msgid "checksum ERROR."
msgstr ""
#: src/DefaultPieceStorage.cc:219
msgid "Download of selected files was complete."
msgstr ""
#: src/DefaultPieceStorage.cc:222
msgid "The download was complete."
msgstr ""

BIN
po/de.gmo

Binary file not shown.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: http://aria2.sourceforge.net/\n"
"POT-Creation-Date: 2006-10-18 23:54+0900\n"
"POT-Creation-Date: 2006-11-06 00:12+0900\n"
"PO-Revision-Date: 2006-05-05 19:44+0900\n"
"Last-Translator: Hermann J. Beckers <hj.beckers@onlinehome.de>\n"
"Language-Team: deutsch <de@li.org>\n"
@ -16,18 +16,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.3\n"
#: src/TorrentMan.cc:685
#, fuzzy
msgid "Download of selected files was complete."
msgstr " Nur ausgewählte Dateien abrufen:"
#: src/TorrentMan.cc:688
#, fuzzy
msgid "The download was complete."
msgstr ""
"\n"
"Abruf ist vollständig. <%s>\n"
#: src/RequestInfo.h:97
#, c-format
msgid ""
@ -369,12 +357,12 @@ msgstr "Konnte Daten nicht erhalten, Ursache: %s"
msgid "Failed to peek data, cause: %s"
msgstr "Datenermittelung fehlgeschlagen, Ursache: %s"
#: src/DownloadEngineFactory.cc:110
#: src/DownloadEngineFactory.cc:139
#, c-format
msgid "Errors occurred while binding port.\n"
msgstr "Fehler beim Binden an Port aufgetreten.\n"
#: src/TorrentRequestInfo.cc:90
#: src/TorrentRequestInfo.cc:91
msgid "Files:"
msgstr "Dateien:"
@ -1014,6 +1002,18 @@ msgstr ""
msgid "checksum ERROR."
msgstr ""
#: src/DefaultPieceStorage.cc:219
#, fuzzy
msgid "Download of selected files was complete."
msgstr " Nur ausgewählte Dateien abrufen:"
#: src/DefaultPieceStorage.cc:222
#, fuzzy
msgid "The download was complete."
msgstr ""
"\n"
"Abruf ist vollständig. <%s>\n"
#, fuzzy
#~ msgid ""
#~ " --min-segment-size=SIZE[K|M] Set minimum segment size. You can append\n"

BIN
po/ja.gmo

Binary file not shown.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: aria2c 0.2.1\n"
"Report-Msgid-Bugs-To: http://aria2.sourceforge.net/\n"
"POT-Creation-Date: 2006-10-18 23:54+0900\n"
"POT-Creation-Date: 2006-11-06 00:12+0900\n"
"PO-Revision-Date: 2006-10-01 22:31+0900\n"
"Last-Translator: Tatsuhiro Tsujikawa <tujikawa@rednoah.com>\n"
"Language-Team: Japanese <ja@li.org>\n"
@ -16,14 +16,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/TorrentMan.cc:685
msgid "Download of selected files was complete."
msgstr "ご指定のファイルのダウンロードが完了."
#: src/TorrentMan.cc:688
msgid "The download was complete."
msgstr "ダウンロードが完了."
#: src/RequestInfo.h:97
#, c-format
msgid ""
@ -369,12 +361,12 @@ msgstr "
msgid "Failed to peek data, cause: %s"
msgstr "データの peek に失敗しました. 原因: %s"
#: src/DownloadEngineFactory.cc:110
#: src/DownloadEngineFactory.cc:139
#, c-format
msgid "Errors occurred while binding port.\n"
msgstr "ポートをバインド中にエラーが発生しました.\n"
#: src/TorrentRequestInfo.cc:90
#: src/TorrentRequestInfo.cc:91
msgid "Files:"
msgstr "ファイル:"
@ -1035,6 +1027,14 @@ msgstr "
msgid "checksum ERROR."
msgstr "チェックサム エラー."
#: src/DefaultPieceStorage.cc:219
msgid "Download of selected files was complete."
msgstr "ご指定のファイルのダウンロードが完了."
#: src/DefaultPieceStorage.cc:222
msgid "The download was complete."
msgstr "ダウンロードが完了."
#~ msgid ""
#~ " --min-segment-size=SIZE[K|M] Set minimum segment size. You can append\n"
#~ " K or M(1K = 1024, 1M = 1024K). This\n"

BIN
po/ru.gmo

Binary file not shown.

View File

@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: ru\n"
"Report-Msgid-Bugs-To: http://aria2.sourceforge.net/\n"
"POT-Creation-Date: 2006-10-18 23:54+0900\n"
"POT-Creation-Date: 2006-11-06 00:12+0900\n"
"PO-Revision-Date: 2006-07-28 18:04+0600\n"
"Last-Translator: Azamat H. Hackimov <azamat.hackimov@gmail.com>\n"
"Language-Team: <ru@li.org>\n"
@ -21,18 +21,6 @@ msgstr ""
"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: KBabel 1.11.2\n"
#: src/TorrentMan.cc:685
#, fuzzy
msgid "Download of selected files was complete."
msgstr " Скачать только выбранные файлы:"
#: src/TorrentMan.cc:688
#, fuzzy
msgid "The download was complete."
msgstr ""
"\n"
"Скачивание завершено. <%s>\n"
#: src/RequestInfo.h:97
#, c-format
msgid ""
@ -371,12 +359,12 @@ msgstr "Ошибка получения данных: %s"
msgid "Failed to peek data, cause: %s"
msgstr "Ошибка запроса данных: %s"
#: src/DownloadEngineFactory.cc:110
#: src/DownloadEngineFactory.cc:139
#, c-format
msgid "Errors occurred while binding port.\n"
msgstr "Ошибка при открытии порта.\n"
#: src/TorrentRequestInfo.cc:90
#: src/TorrentRequestInfo.cc:91
msgid "Files:"
msgstr "Файлы:"
@ -994,6 +982,18 @@ msgstr ""
msgid "checksum ERROR."
msgstr ""
#: src/DefaultPieceStorage.cc:219
#, fuzzy
msgid "Download of selected files was complete."
msgstr " Скачать только выбранные файлы:"
#: src/DefaultPieceStorage.cc:222
#, fuzzy
msgid "The download was complete."
msgstr ""
"\n"
"Скачивание завершено. <%s>\n"
#, fuzzy
#~ msgid ""
#~ " --min-segment-size=SIZE[K|M] Set minimum segment size. You can append\n"

View File

@ -1,911 +0,0 @@
/* <!-- 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 "TorrentMan.h"
#include "Dictionary.h"
#include "List.h"
#include "ShaVisitor.h"
#include "Util.h"
#include "MetaFileUtil.h"
#include "DlAbortEx.h"
#include "File.h"
#include "message.h"
#include "PreAllocationDiskWriter.h"
#include "DefaultDiskWriter.h"
#include "MultiDiskWriter.h"
#include "prefs.h"
#include "CopyDiskAdaptor.h"
#include "DirectDiskAdaptor.h"
#include "MultiDiskAdaptor.h"
#include "LogFactory.h"
#include "DelegatingPeerListProcessor.h"
#include <errno.h>
#include <libgen.h>
#include <string.h>
#include <algorithm>
extern PeerHandle nullPeer;
TorrentMan::TorrentMan():bitfield(0),
peerEntryIdCounter(0),
cuidCounter(0),
downloadLength(0),
uploadLength(0),
preDownloadLength(0),
preUploadLength(0),
storeDir("."),
setupComplete(false),
halt(false),
interval(DEFAULT_ANNOUNCE_INTERVAL),
minInterval(DEFAULT_ANNOUNCE_MIN_INTERVAL),
complete(0),
incomplete(0),
connections(0),
trackers(0),
trackerNumTry(0),
diskAdaptor(0)
{
logger = LogFactory::getInstance();
// to force requesting to a tracker first time.
announceInterval.setTimeInSec(0);
}
TorrentMan::~TorrentMan() {
delete bitfield;
delete diskAdaptor;
}
// TODO do not use this method in application code
void TorrentMan::updatePeers(const Peers& peers) {
this->peers = peers;
}
void TorrentMan::addPeer(const Peers& peers) {
for(Peers::const_iterator itr = peers.begin();
itr != peers.end(); itr++) {
const PeerHandle& peer = *itr;
if(addPeer(peer)) {
logger->debug("Adding peer %s:%d",
peer->ipaddr.c_str(), peer->port);
}
}
}
bool TorrentMan::addPeer(const PeerHandle& peer) {
Peers::iterator itr = find(peers.begin(), peers.end(), peer);
if(itr == peers.end()) {
if(peers.size() > MAX_PEER_LIST_SIZE) {
deleteUnusedPeer(peers.size()-MAX_PEER_LIST_SIZE+1);
}
++peerEntryIdCounter;
peer->entryId = peerEntryIdCounter;
peers.push_back(peer);
return true;
} else {
const PeerHandle& peer = *itr;
if(peer->error >= MAX_PEER_ERROR || peer->cuid != 0) {
return false;
} else {
*itr = peer;
return true;
}
}
}
/*
void TorrentMan::updatePeer(const Peer& peer) {
for(Peers::iterator itr = peers.begin(); itr != peers.end(); itr++) {
Peer& p = *itr;
if(p.eid == peer.eid) {
p = peer;
break;
}
}
}
*/
bool TorrentMan::isPeerAvailable() const {
return getPeer() != nullPeer;
}
void TorrentMan::deleteUnusedPeer(int delSize) {
for(Peers::iterator itr = peers.begin();
itr != peers.end() && delSize > 0;) {
const PeerHandle& p = *itr;
if(p->cuid == 0) {
itr = peers.erase(itr);
delSize--;
} else {
itr++;
}
}
}
PeerHandle TorrentMan::getPeer() const {
if(connections > MIN_PEERS) {
return nullPeer;
}
for(Peers::const_iterator itr = peers.begin(); itr != peers.end(); itr++) {
const PeerHandle& p = *itr;
if(p->cuid == 0 && p->error < MAX_PEER_ERROR) {
return p;
}
}
return nullPeer;
}
bool TorrentMan::isEndGame() const {
return bitfield->countMissingBlock() <= END_GAME_PIECE_NUM;
}
bool TorrentMan::hasMissingPiece(const PeerHandle& peer) const {
return bitfield->hasMissingPiece(peer->getBitfield(),
peer->getBitfieldLength());
}
int TorrentMan::getMissingPieceIndex(const PeerHandle& peer) const {
int index = -1;
if(isEndGame()) {
index = bitfield->getMissingIndex(peer->getBitfield(),
peer->getBitfieldLength());
} else {
index = bitfield->getMissingUnusedIndex(peer->getBitfield(),
peer->getBitfieldLength());
}
return index;
}
int TorrentMan::getMissingFastPieceIndex(const PeerHandle& peer) const {
int index = -1;
if(peer->isFastExtensionEnabled() && peer->countFastSet() > 0) {
BitfieldMan tempBitfield(pieceLength, totalLength);
for(Integers::const_iterator itr = peer->getFastSet().begin();
itr != peer->getFastSet().end(); itr++) {
if(!hasPiece(*itr) && peer->hasPiece(*itr)) {
tempBitfield.setBit(*itr);
}
}
if(isEndGame()) {
index = bitfield->getMissingIndex(tempBitfield.getBitfield(),
tempBitfield.getBitfieldLength());
} else {
index = bitfield->getMissingUnusedIndex(tempBitfield.getBitfield(),
tempBitfield.getBitfieldLength());
}
}
return index;
}
Piece TorrentMan::getMissingFastPiece(const PeerHandle& peer) {
int index = getMissingFastPieceIndex(peer);
return checkOutPiece(index);
}
Piece TorrentMan::getMissingPiece(const PeerHandle& peer) {
int index = getMissingPieceIndex(peer);
return checkOutPiece(index);
}
Piece TorrentMan::checkOutPiece(int index) {
if(index == -1) {
return Piece::nullPiece;
}
bitfield->setUseBit(index);
Piece piece = findUsedPiece(index);
if(Piece::isNull(piece)) {
Piece piece(index, bitfield->getBlockLength(index));
addUsedPiece(piece);
return piece;
} else {
return piece;
}
}
int TorrentMan::deleteUsedPiecesByFillRate(int fillRate, int toDelete) {
int deleted = 0;
for(Pieces::iterator itr = usedPieces.begin();
itr != usedPieces.end() && deleted < toDelete;) {
Piece& piece = *itr;
if(!bitfield->isUseBitSet(piece.getIndex()) &&
piece.countCompleteBlock() <= piece.countBlock()*(fillRate/100.0)) {
logger->debug("Deleting used piece index=%d, fillRate(%%)=%d<=%d",
piece.getIndex(),
(piece.countCompleteBlock()*100)/piece.countBlock(),
fillRate);
itr = usedPieces.erase(itr);
deleted++;
} else {
itr++;
}
}
return deleted;
}
void TorrentMan::reduceUsedPieces(int max) {
int toDelete = usedPieces.size()-max;
if(toDelete <= 0) {
return;
}
int fillRate = 10;
while(fillRate < 50) {
int deleted = deleteUsedPiecesByFillRate(fillRate, toDelete);
if(deleted == 0) {
break;
}
toDelete -= deleted;
fillRate += 10;
}
}
void TorrentMan::addUsedPiece(const Piece& piece) {
// TODO ? if nullPiece
usedPieces.push_back(piece);
}
Piece TorrentMan::findUsedPiece(int index) const {
for(Pieces::const_iterator itr = usedPieces.begin(); itr != usedPieces.end(); itr++) {
const Piece& piece = *itr;
if(piece.getIndex() == index) {
return piece;
}
}
return Piece::nullPiece;
}
void TorrentMan::deleteUsedPiece(const Piece& piece) {
if(Piece::isNull(piece)) {
return;
}
Pieces::iterator itr = find(usedPieces.begin(), usedPieces.end(), piece);
if(itr != usedPieces.end()) {
usedPieces.erase(itr);
}
}
void TorrentMan::completePiece(const Piece& piece) {
if(Piece::isNull(piece)) {
return;
}
if(!hasPiece(piece.getIndex())) {
addDownloadLength(piece.getLength());
}
deleteUsedPiece(piece);
if(!isEndGame()) {
reduceUsedPieces(100);
}
if(downloadComplete()) {
return;
}
bitfield->setBit(piece.getIndex());
bitfield->unsetUseBit(piece.getIndex());
if(downloadComplete()) {
onDownloadComplete();
}
}
void TorrentMan::cancelPiece(const Piece& piece) {
if(Piece::isNull(piece)) {
return;
}
updatePiece(piece);
bitfield->unsetUseBit(piece.getIndex());
if(!isEndGame()) {
if(piece.countCompleteBlock() == 0) {
deleteUsedPiece(piece);
}
}
}
void TorrentMan::updatePiece(const Piece& piece) {
if(Piece::isNull(piece)) {
return;
}
Pieces::iterator itr = find(usedPieces.begin(), usedPieces.end(),
piece);
if(itr != usedPieces.end()) {
*itr = piece;
}
}
void TorrentMan::syncPiece(Piece& piece) {
if(Piece::isNull(piece)) {
return;
}
Pieces::iterator itr = find(usedPieces.begin(), usedPieces.end(),
piece);
if(itr != usedPieces.end()) {
piece = *itr;
return;
} else {
// hasPiece(piece.getIndex()) is true, then set all bit of
// piece.bitfield to 1
if(hasPiece(piece.getIndex())) {
piece.setAllBlock();
}
}
}
void TorrentMan::initBitfield() {
if(bitfield != NULL) {
delete bitfield;
}
bitfield = new BitfieldMan(pieceLength, totalLength);
}
void TorrentMan::setBitfield(unsigned char* bitfield, int bitfieldLength) {
if(this->bitfield == NULL) {
initBitfield();
}
this->bitfield->setBitfield(bitfield, bitfieldLength);
}
bool TorrentMan::downloadComplete() const {
return bitfield->isAllBitSet();
}
bool TorrentMan::hasAllPieces() const {
return bitfield->getTotalLength() == downloadLength;
}
void TorrentMan::readFileEntry(FileEntries& fileEntries, Directory** pTopDir, const Dictionary* infoDic, const string& defaultName) {
Data* topName = (Data*)infoDic->get("name");
if(topName != NULL) {
name = topName->toString();
} else {
char* basec = strdup(defaultName.c_str());
name = string(basename(basec))+".file";
free(basec);
}
List* files = (List*)infoDic->get("files");
if(files == NULL) {
// single-file mode;
setFileMode(SINGLE);
Data* length = (Data*)infoDic->get("length");
totalLength = length->toLLInt();
FileEntry fileEntry(name, totalLength, 0);
fileEntries.push_back(fileEntry);
} else {
long long int length = 0;
long long int offset = 0;
// multi-file mode
setFileMode(MULTI);
*pTopDir = new Directory(name);
const MetaList& metaList = files->getList();
for(MetaList::const_iterator itr = metaList.begin(); itr != metaList.end();
itr++) {
Dictionary* fileDic = (Dictionary*)(*itr);
Data* lengthData = (Data*)fileDic->get("length");
length += lengthData->toLLInt();
List* path = (List*)fileDic->get("path");
const MetaList& paths = path->getList();
Directory* parentDir = *pTopDir;
string filePath = name;
for(int i = 0; i < (int)paths.size()-1; i++) {
Data* subpath = (Data*)paths.at(i);
Directory* dir = new Directory(subpath->toString());
parentDir->addFile(dir);
parentDir = dir;
filePath.append("/").append(subpath->toString());
}
Data* lastpath = (Data*)paths.back();
filePath.append("/").append(lastpath->toString());
FileEntry fileEntry(filePath, lengthData->toLLInt(), offset);
fileEntries.push_back(fileEntry);
offset += fileEntry.length;
}
totalLength = length;
}
}
void TorrentMan::setupInternal1(const string& metaInfoFile) {
peerId = "-aria2-";
peerId += Util::randomAlpha(20-peerId.size());
key = Util::randomAlpha(8);
uploadLength = 0;
downloadLength = 0;
Dictionary* topDic = (Dictionary*)MetaFileUtil::parseMetaFile(metaInfoFile);
const Dictionary* infoDic = (const Dictionary*)topDic->get("info");
ShaVisitor v;
infoDic->accept(&v);
unsigned char md[20];
int len;
v.getHash(md, len);
setInfoHash(md);
FileEntries fileEntries;
Directory* topDir = NULL;
readFileEntry(fileEntries, &topDir, infoDic, metaInfoFile);
string announce = ((Data*)topDic->get("announce"))->toString();
const MetaEntry* announces = topDic->get("announce-list");
if(announces) {
announceList.reconfigure(announces);
announceList.shuffle();
}
if(!announceList.countTier()) {
announceList.reconfigure(announce);
}
pieceLength = ((Data*)infoDic->get("piece length"))->toInt();
pieces = totalLength/pieceLength+(totalLength%pieceLength ? 1 : 0);
Data* piecesHashData = (Data*)infoDic->get("pieces");
if(piecesHashData->getLen() != pieces*20) {
throw new DlAbortEx("The number of pieces is wrong.");
}
for(int index = 0; index < pieces; index++) {
string hex = Util::toHex((unsigned char*)&piecesHashData->getData()[index*20], 20);
pieceHashes.push_back(hex);
logger->debug("piece #%d, hash:%s", index, hex.c_str());
}
initBitfield();
delete topDic;
if(option->get(PREF_DIRECT_FILE_MAPPING) == V_TRUE) {
if(fileMode == SINGLE) {
diskAdaptor = new DirectDiskAdaptor(new DefaultDiskWriter(totalLength));
} else {
diskAdaptor = new MultiDiskAdaptor(new MultiDiskWriter(pieceLength));
}
} else {
diskAdaptor = new CopyDiskAdaptor(new PreAllocationDiskWriter(totalLength));
((CopyDiskAdaptor*)diskAdaptor)->setTempFilename(name+".a2tmp");
}
diskAdaptor->setStoreDir(storeDir);
diskAdaptor->setTopDir(topDir);
diskAdaptor->setFileEntries(fileEntries);
}
void TorrentMan::setupInternal2() {
if(segmentFileExists()) {
load();
diskAdaptor->openExistingFile();
} else {
diskAdaptor->initAndOpenFile();
}
setupComplete = true;
}
void TorrentMan::setup(const string& metaInfoFile, const Integers& targetFileIndexes) {
setupInternal1(metaInfoFile);
Strings targetFilePaths;
const FileEntries& entries = diskAdaptor->getFileEntries();
for(int i = 0; i < (int)entries.size(); i++) {
if(find(targetFileIndexes.begin(), targetFileIndexes.end(), i+1) != targetFileIndexes.end()) {
logger->debug("index=%d is %s", i+1, entries.at(i).path.c_str());
targetFilePaths.push_back(entries.at(i).path);
}
}
setFileFilter(targetFilePaths);
setupInternal2();
}
void TorrentMan::setup(const string& metaInfoFile, const Strings& targetFilePaths) {
setupInternal1(metaInfoFile);
setFileFilter(targetFilePaths);
setupInternal2();
}
void TorrentMan::setFileFilter(const Strings& filePaths) {
if(fileMode != MULTI || filePaths.empty()) {
return;
}
diskAdaptor->removeAllDownloadEntry();
for(Strings::const_iterator pitr = filePaths.begin();
pitr != filePaths.end(); pitr++) {
if(!diskAdaptor->addDownloadEntry(*pitr)) {
throw new DlAbortEx("No such file entry %s", (*pitr).c_str());
}
FileEntry fileEntry = diskAdaptor->getFileEntryFromPath(*pitr);
bitfield->addFilter(fileEntry.offset, fileEntry.length);
}
bitfield->enableFilter();
}
FileEntries TorrentMan::readFileEntryFromMetaInfoFile(const string& metaInfoFile) {
Dictionary* topDic = (Dictionary*)MetaFileUtil::parseMetaFile(metaInfoFile);
const Dictionary* infoDic = (const Dictionary*)topDic->get("info");
FileEntries fileEntries;
Directory* topDir = NULL;
readFileEntry(fileEntries, &topDir, infoDic, metaInfoFile);
if(topDir != NULL) {
delete topDir;
}
return fileEntries;
}
string TorrentMan::getName() const {
return name;
}
bool TorrentMan::hasPiece(int index) const {
return bitfield->isBitSet(index);
}
string TorrentMan::getPieceHash(int index) const {
return pieceHashes.at(index);
}
string TorrentMan::getSegmentFilePath() const {
return storeDir+"/"+name+".aria2";
}
bool TorrentMan::segmentFileExists() const {
string segFilename = getSegmentFilePath();
File f(segFilename);
if(f.isFile()) {
logger->info(MSG_SEGMENT_FILE_EXISTS, segFilename.c_str());
return true;
} else {
logger->info(MSG_SEGMENT_FILE_DOES_NOT_EXIST, segFilename.c_str());
return false;
}
}
FILE* TorrentMan::openSegFile(const string& segFilename, const string& mode) const {
FILE* segFile = fopen(segFilename.c_str(), mode.c_str());
if(segFile == NULL) {
throw new DlAbortEx(EX_SEGMENT_FILE_OPEN,
segFilename.c_str(), strerror(errno));
}
return segFile;
}
void TorrentMan::load() {
string segFilename = getSegmentFilePath();
logger->info(MSG_LOADING_SEGMENT_FILE, segFilename.c_str());
FILE* segFile = openSegFile(segFilename, "r+");
try {
read(segFile);
fclose(segFile);
} catch(string ex) {
fclose(segFile);
throw new DlAbortEx(EX_SEGMENT_FILE_READ,
segFilename.c_str(), strerror(errno));
}
logger->info(MSG_LOADED_SEGMENT_FILE);
}
void TorrentMan::read(FILE* file) {
assert(file != NULL);
unsigned char savedInfoHash[INFO_HASH_LENGTH];
if(fread(savedInfoHash, INFO_HASH_LENGTH, 1, file) < 1) {
throw string("readError");
}
if(Util::toHex(savedInfoHash, INFO_HASH_LENGTH) != Util::toHex(infoHash, INFO_HASH_LENGTH)) {
throw new DlAbortEx("Incorrect infoHash.");
}
unsigned char* savedBitfield = new unsigned char[bitfield->getBitfieldLength()];
try {
if(fread(savedBitfield, bitfield->getBitfieldLength(), 1, file) < 1) {
throw string("readError");
}
setBitfield(savedBitfield, bitfield->getBitfieldLength());
if(fread(&downloadLength, sizeof(downloadLength), 1, file) < 1) {
throw string("readError");
}
if(fread(&uploadLength, sizeof(uploadLength), 1, file) < 1) {
throw string("readError");
}
preDownloadLength = downloadLength;
preUploadLength = uploadLength;
delete [] savedBitfield;
} catch(...) {
delete [] savedBitfield;
throw;
}
}
void TorrentMan::save() const {
if(!setupComplete) {
return;
}
string segFilename = getSegmentFilePath();
logger->info(MSG_SAVING_SEGMENT_FILE, segFilename.c_str());
FILE* file = openSegFile(segFilename, "w");
try {
if(fwrite(infoHash, INFO_HASH_LENGTH, 1, file) < 1) {
throw string("writeError");
}
if(fwrite(bitfield->getBitfield(), bitfield->getBitfieldLength(), 1, file) < 1) {
throw string("writeError");
}
if(fwrite(&downloadLength, sizeof(downloadLength), 1, file) < 1) {
throw string("writeError");
}
if(fwrite(&uploadLength, sizeof(uploadLength), 1, file) < 1) {
throw string("writeError");
}
fclose(file);
logger->info(MSG_SAVED_SEGMENT_FILE);
} catch(string ex) {
fclose(file);
throw new DlAbortEx(EX_SEGMENT_FILE_WRITE,
segFilename.c_str(), strerror(errno));
}
}
void TorrentMan::remove() const {
if(segmentFileExists()) {
File f(getSegmentFilePath());
f.remove();
}
}
bool TorrentMan::isSelectiveDownloadingMode() const {
return bitfield->isFilterEnabled();
}
void TorrentMan::finishSelectiveDownloadingMode() {
bitfield->clearFilter();
diskAdaptor->addAllDownloadEntry();
}
long long int TorrentMan::getCompletedLength() const {
return bitfield->getCompletedLength();
}
long long int TorrentMan::getSelectedTotalLength() const {
return bitfield->getFilteredTotalLength();
}
void TorrentMan::onDownloadComplete() {
save();
diskAdaptor->onDownloadComplete();
if(isSelectiveDownloadingMode()) {
logger->notice(_("Download of selected files was complete."));
finishSelectiveDownloadingMode();
} else {
logger->info(_("The download was complete."));
}
}
void TorrentMan::advertisePiece(int cuid, int index) {
HaveEntry entry(cuid, index);
haves.push_front(entry);
};
PieceIndexes
TorrentMan::getAdvertisedPieceIndexes(int myCuid,
const Time& lastCheckTime
) const
{
PieceIndexes indexes;
for(Haves::const_iterator itr = haves.begin(); itr != haves.end(); itr++) {
const Haves::value_type& have = *itr;
if(have.cuid == myCuid) {
continue;
}
if(lastCheckTime.isNewer(have.registeredTime)) {
break;
}
indexes.push_back(have.index);
}
return indexes;
}
class FindElapsedHave
{
private:
int elapsed;
public:
FindElapsedHave(int elapsed):elapsed(elapsed) {}
bool operator()(const HaveEntry& have) {
if(have.registeredTime.elapsed(elapsed)) {
return true;
} else {
return false;
}
}
};
void
TorrentMan::removeAdvertisedPiece(int elapsed)
{
Haves::iterator itr = find_if(haves.begin(), haves.end(), FindElapsedHave(elapsed));
if(itr != haves.end()) {
logger->debug("Removed %d have entries.", haves.end()-itr);
haves.erase(itr, haves.end());
}
}
TransferStat TorrentMan::calculateStat() {
TransferStat stat;
for(Peers::iterator itr = activePeers.begin();
itr != activePeers.end(); itr++) {
PeerHandle& peer = *itr;
stat.downloadSpeed += peer->calculateDownloadSpeed();
stat.uploadSpeed += peer->calculateUploadSpeed();
stat.sessionDownloadLength += peer->getSessionDownloadLength();
stat.sessionUploadLength += peer->getSessionUploadLength();
}
return stat;
}
bool TorrentMan::isStoppedAnnounceReady() const {
return (trackers == 0 &&
isHalt() &&
announceList.countStoppedAllowedTier());
}
bool TorrentMan::isCompletedAnnounceReady() const {
return (trackers == 0 &&
downloadComplete() &&
announceList.countCompletedAllowedTier());
}
bool TorrentMan::isDefaultAnnounceReady() const {
return (trackers == 0 &&
announceInterval.elapsed(minInterval));
}
bool TorrentMan::isAnnounceReady() const {
return
isStoppedAnnounceReady() ||
isCompletedAnnounceReady() ||
isDefaultAnnounceReady();
}
string TorrentMan::getAnnounceUrl() {
if(isStoppedAnnounceReady()) {
announceList.moveToStoppedAllowedTier();
announceList.setEvent(AnnounceTier::STOPPED);
} else if(isCompletedAnnounceReady()) {
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(downloadComplete() &&
announceList.getEvent() == AnnounceTier::STARTED) {
announceList.setEvent(AnnounceTier::STARTED_AFTER_COMPLETION);
}
}
int numWant = 50;
if(connections >= MIN_PEERS || isHalt()) {
numWant = 0;
}
string url = announceList.getAnnounce()+"?"+
"info_hash="+Util::torrentUrlencode(getInfoHash(), 20)+"&"+
"peer_id="+peerId+"&"+
"port="+Util::itos(getPort())+"&"+
"uploaded="+Util::llitos(getSessionUploadLength())+"&"+
"downloaded="+Util::llitos(getSessionDownloadLength())+"&"+
"left="+(getTotalLength()-getDownloadLength() <= 0
? "0" : Util::llitos(getTotalLength()-getDownloadLength()))+"&"+
"compact=1"+"&"+
"key="+key+"&"+
"numwant="+Util::itos(numWant)+"&"+
"no_peer_id=1";
string event = announceList.getEventString();
if(!event.empty()) {
url += string("&")+"event="+event;
}
if(!trackerId.empty()) {
url += string("&")+"trackerid="+trackerId;
}
return url;
}
void TorrentMan::announceStart() {
trackers++;
}
void TorrentMan::announceFailure() {
trackers = 0;
trackerNumTry++;
announceList.announceFailure();
}
void TorrentMan::announceSuccess() {
trackers = 0;
announceList.announceSuccess();
}
bool TorrentMan::isAllAnnounceFailed() const {
return
trackerNumTry >= option->getAsInt(PREF_TRACKER_MAX_TRIES);
}
void TorrentMan::resetAnnounce() {
announceInterval.reset();
trackerNumTry = 0;
}
void TorrentMan::processAnnounceResponse(const char* trackerResponse,
size_t trackerResponseLength) {
SharedHandle<MetaEntry> entry(MetaFileUtil::bdecoding(trackerResponse,
trackerResponseLength));
Dictionary* response = (Dictionary*)entry.get();
Data* failureReasonData = (Data*)response->get("failure reason");
if(failureReasonData) {
throw new DlAbortEx("Tracker returned failure reason: %s",
failureReasonData->toString().c_str());
}
Data* warningMessageData = (Data*)response->get("warning message");
if(warningMessageData) {
logger->warn(MSG_TRACKER_WARNING_MESSAGE,
warningMessageData->toString().c_str());
}
Data* trackerIdData = (Data*)response->get("tracker id");
if(trackerIdData) {
trackerId = trackerIdData->toString();
logger->debug("Tracker ID:%s", trackerId.c_str());
}
Data* intervalData = (Data*)response->get("interval");
if(intervalData) {
interval = intervalData->toInt();
logger->debug("Interval:%d", interval);
}
Data* minIntervalData = (Data*)response->get("min interval");
if(minIntervalData) {
minInterval = minIntervalData->toInt();
logger->debug("Min interval:%d", minInterval);
}
if(minInterval > interval) {
minInterval = interval;
}
Data* completeData = (Data*)response->get("complete");
if(completeData) {
complete = completeData->toInt();
logger->debug("Complete:%d", complete);
}
Data* incompleteData = (Data*)response->get("incomplete");
if(incompleteData) {
incomplete = incompleteData->toInt();
logger->debug("Incomplete:%d", incomplete);
}
const MetaEntry* peersEntry = response->get("peers");
if(peersEntry && !isHalt() && connections < MIN_PEERS) {
DelegatingPeerListProcessor proc(pieceLength, getTotalLength());
Peers peers = proc.extractPeer(peersEntry);
addPeer(peers);
}
if(!peersEntry) {
logger->info("No peer list received.");
}
}
bool TorrentMan::needMorePeerConnection() const {
return isPeerAvailable() && connections < MIN_PEERS;
}
bool TorrentMan::noMoreAnnounce() const {
return (trackers == 0 &&
isHalt() &&
!announceList.countStoppedAllowedTier());
}

View File

@ -1,326 +0,0 @@
/* <!-- 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 --> */
#ifndef _D_TORRENT_MAN_H_
#define _D_TORRENT_MAN_H_
#include "Peer.h"
#include "common.h"
#include "Logger.h"
#include "BitfieldMan.h"
#include "DiskWriter.h"
#include "Piece.h"
#include "Dictionary.h"
#include "Option.h"
#include "FileEntry.h"
#include "DiskAdaptor.h"
#include "AnnounceList.h"
#include "TimeA2.h"
#include "PeerListProcessor.h"
#include <deque>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define INFO_HASH_LENGTH 20
#define PEER_ID_LENGTH 20
#define DEFAULT_ANNOUNCE_INTERVAL 1800
#define DEFAULT_ANNOUNCE_MIN_INTERVAL 1800
#define MAX_PEERS 55
#define MIN_PEERS 15
#define MAX_PEER_LIST_SIZE 100
#define END_GAME_PIECE_NUM 20
#define MAX_PEER_ERROR 5
class TransferStat {
public:
int downloadSpeed;
int uploadSpeed;
long long int sessionDownloadLength;
long long int sessionUploadLength;
public:
TransferStat():downloadSpeed(0), uploadSpeed(0),
sessionDownloadLength(0), sessionUploadLength(0) {}
};
class HaveEntry {
public:
int cuid;
int index;
Time registeredTime;
HaveEntry(int cuid, int index):
cuid(cuid),
index(index) {}
};
typedef deque<HaveEntry> Haves;
typedef deque<int> PieceIndexes;
typedef deque<Piece> Pieces;
class TorrentMan {
private:
Peers peers;
BitfieldMan* bitfield;
unsigned char infoHash[INFO_HASH_LENGTH];
deque<string> pieceHashes;
int peerEntryIdCounter;
int cuidCounter;
long long int totalLength;
long long int downloadLength;
long long int uploadLength;
long long int preDownloadLength;
long long int preUploadLength;
int fileMode;
string storeDir;
int port;
Haves haves;
Pieces usedPieces;
bool setupComplete;
const Logger* logger;
Peers activePeers;
bool halt;
FILE* openSegFile(const string& segFilename, const string& mode) const;
void read(FILE* file);
Piece findUsedPiece(int index) const;
void addUsedPiece(const Piece& piece);
void deleteUsedPiece(const Piece& piece);
int deleteUsedPiecesByFillRate(int fillRate, int toDelete);
void reduceUsedPieces(int max);
void readFileEntry(FileEntries& fileEntries, Directory** pTopDir, const Dictionary* infoDic, const string& defaultName);
void setFileFilter(const Strings& filePaths);
void setupInternal1(const string& metaInfoFile);
void setupInternal2();
Piece checkOutPiece(int index);
bool isStoppedAnnounceReady() const;
bool isCompletedAnnounceReady() const;
bool isDefaultAnnounceReady() const;
public:
int pieceLength;
int pieces;
// TODO type char* would be better
string peerId;
string key;
string announce;
string trackerId;
string name;
int interval;
int minInterval;
int complete;
int incomplete;
int connections;
// The number of tracker request command currently in the command queue.
int trackers;
int trackerNumTry;
Time announceInterval;
// tracker request
AnnounceList announceList;
public:
TorrentMan();
~TorrentMan();
DiskAdaptor* diskAdaptor;
const Option* option;
int getNewCuid() { return ++cuidCounter; }
// TODO do not use this method
void updatePeers(const Peers& peers);
bool addPeer(const PeerHandle& peer);
void addPeer(const Peers& peers);
//void updatePeer(const Peer* peer);
const Peers& getPeers() const { return peers; }
PeerHandle getPeer() const;
bool isPeerAvailable() const;
void deleteUnusedPeer(int delSize);
bool hasMissingPiece(const PeerHandle& peer) const;
int getMissingPieceIndex(const PeerHandle& peer) const;
int getMissingFastPieceIndex(const PeerHandle& peer) const;
Piece getMissingPiece(const PeerHandle& peer);
Piece getMissingFastPiece(const PeerHandle& peer);
void completePiece(const Piece& piece);
void cancelPiece(const Piece& piece);
void updatePiece(const Piece& piece);
void syncPiece(Piece& piece);
bool hasPiece(int index) const;
void initBitfield();
/**
* Returns true if the number of missing block is less than or equal to
* END_GAME_PIECE_NUM.
* If file filter is enabled, only a range specified by the filter is
* concerned.
*/
bool isEndGame() const;
/**
* Returns true if download has completed. If file filter is enabled,
* returns true if download of a range specified by the filter has completed.
*/
bool downloadComplete() const;
bool hasAllPieces() const;
void setBitfield(unsigned char* bitfield, int len);
const unsigned char* getBitfield() const {
return bitfield->getBitfield();
}
int getBitfieldLength() const { return bitfield->getBitfieldLength(); }
int getPieceLength(int index) const {
return bitfield->getBlockLength(index);
}
int getPieceLength() const { return bitfield->getBlockLength(); }
void setInfoHash(const unsigned char* infoHash) {
memcpy(this->infoHash, infoHash, INFO_HASH_LENGTH);
}
const unsigned char* getInfoHash() const {
return infoHash;
}
void setup(const string& metaInfoFile, const Strings& targetFilePaths);
void setup(const string& metaInfoFile, const Integers& targetFileIndexes);
string getPieceHash(int index) const;
// Adds piece index to advertise to other commands. They send have message
// based on this information.
void advertisePiece(int cuid, int index);
// Returns piece index which is not advertised by the caller command and
// newer than lastCheckTime.
PieceIndexes getAdvertisedPieceIndexes(int myCuid, const Time& lastCheckTime) const;
// Removes have entry if specified seconds have elapsed since its registration.
void removeAdvertisedPiece(int elapsed);
long long int getTotalLength() const { return totalLength; }
void setTotalLength(long long int length) { totalLength = length; }
void addDownloadLength(int deltaLength) { downloadLength += deltaLength; }
long long int getDownloadLength() const { return downloadLength; }
void setDownloadLength(long long int length) { downloadLength = length; }
void addUploadLength(int deltaLength) { uploadLength += deltaLength; }
long long int getUploadLength() const { return uploadLength; }
void setUploadLength(long long int length) { uploadLength = length; }
long long int getSessionDownloadLength() const {
return downloadLength-preDownloadLength;
}
long long int getSessionUploadLength() const {
return uploadLength-preUploadLength;
}
void setFileMode(int mode) {
fileMode = mode;
}
int getFileMode() const {
return fileMode;
}
string getStoreDir() const { return storeDir; }
void setStoreDir(const string& dir) { storeDir = dir; }
string getSegmentFilePath() const;
bool segmentFileExists() const;
void load();
void save() const;
void remove() const;
void copySingleFile() const;
void splitMultiFile();
void fixFilename();
void deleteTempFile() const;
void setPort(int port) { this->port = port; }
int getPort() const { return port; }
int countUsedPiece() const { return usedPieces.size(); }
int countAdvertisedPiece() const { return haves.size(); }
FileEntries readFileEntryFromMetaInfoFile(const string& metaInfoFile);
string getName() const;
void finishSelectiveDownloadingMode();
bool isSelectiveDownloadingMode() const;
long long int getCompletedLength() const;
long long int getSelectedTotalLength() const;
void onDownloadComplete();
void addActivePeer(const PeerHandle& peer) {
peer->activate();
activePeers.push_back(peer);
}
Peers& getActivePeers() { return this->activePeers; }
void deleteActivePeer(const PeerHandle& peer) {
Peers::iterator itr = find(activePeers.begin(), activePeers.end(), peer);
if(itr != activePeers.end()) {
peer->deactivate();
activePeers.erase(itr);
}
}
bool isHalt() const { return halt; }
void setHalt(bool halt) {
this->halt = halt;
}
// announce related methods.
bool isAnnounceReady() const;
string getAnnounceUrl();
void announceStart();
void announceSuccess();
void announceFailure();
bool isAllAnnounceFailed() const;
void resetAnnounce();
void processAnnounceResponse(const char* trackerResponse,
size_t trackerResponseLength);
bool needMorePeerConnection() const;
bool noMoreAnnounce() const;
enum FILE_MODE {
SINGLE,
MULTI
};
TransferStat calculateStat();
};
#endif // _D_TORRENT_MAN_H_