You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
557 lines
12 KiB
557 lines
12 KiB
15 years ago
|
/***************************************************************************
|
||
|
* Copyright (C) 2005 by Joris Guisson *
|
||
|
* joris.guisson@gmail.com *
|
||
|
* *
|
||
|
* 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. *
|
||
|
***************************************************************************/
|
||
|
#include <qfile.h>
|
||
|
#include <klocale.h>
|
||
|
#include <functions.h>
|
||
|
#include <util/log.h>
|
||
|
#include <torrent/globals.h>
|
||
|
#include <kademlia/dhtbase.h>
|
||
|
#include <kademlia/dhttrackerbackend.h>
|
||
|
#include "tracker.h"
|
||
|
#include "udptracker.h"
|
||
|
#include "httptracker.h"
|
||
|
#include "torrentcontrol.h"
|
||
|
#include "torrent.h"
|
||
|
#include "peermanager.h"
|
||
|
#include "peersourcemanager.h"
|
||
|
|
||
|
namespace bt
|
||
|
{
|
||
|
const Uint32 INITIAL_WAIT_TIME = 30;
|
||
|
const Uint32 LONGER_WAIT_TIME = 300;
|
||
|
const Uint32 FINAL_WAIT_TIME = 1800;
|
||
|
|
||
|
PeerSourceManager::PeerSourceManager(TorrentControl* tor,PeerManager* pman)
|
||
|
: tor(tor),pman(pman),curr(0),m_dht(0),started(false),pending(false)
|
||
|
{
|
||
|
failures = 0;
|
||
|
trackers.setAutoDelete(true);
|
||
|
no_save_custom_trackers = false;
|
||
|
|
||
|
const TrackerTier* t = tor->getTorrent().getTrackerList();
|
||
|
int tier = 1;
|
||
|
while (t)
|
||
|
{
|
||
|
// add all standard trackers
|
||
|
const KURL::List & tr = t->urls;
|
||
|
KURL::List::const_iterator i = tr.begin();
|
||
|
while (i != tr.end())
|
||
|
{
|
||
|
addTracker(*i,false,tier);
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
tier++;
|
||
|
t = t->next;
|
||
|
}
|
||
|
|
||
|
//load custom trackers
|
||
|
loadCustomURLs();
|
||
|
|
||
|
connect(&timer,SIGNAL(timeout()),this,SLOT(updateCurrentManually()));
|
||
|
}
|
||
|
|
||
|
PeerSourceManager::~PeerSourceManager()
|
||
|
{
|
||
|
saveCustomURLs();
|
||
|
additional.setAutoDelete(true);
|
||
|
QPtrList<kt::PeerSource>::iterator itr = additional.begin();
|
||
|
while (itr != additional.end())
|
||
|
{
|
||
|
kt::PeerSource* ps = *itr;
|
||
|
ps->aboutToBeDestroyed();
|
||
|
itr++;
|
||
|
}
|
||
|
additional.clear();
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::addTracker(Tracker* trk)
|
||
|
{
|
||
|
trackers.insert(trk->trackerURL(),trk);
|
||
|
connect(trk,SIGNAL(peersReady( kt::PeerSource* )),
|
||
|
pman,SLOT(peerSourceReady( kt::PeerSource* )));
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::addPeerSource(kt::PeerSource* ps)
|
||
|
{
|
||
|
additional.append(ps);
|
||
|
connect(ps,SIGNAL(peersReady( kt::PeerSource* )),
|
||
|
pman,SLOT(peerSourceReady( kt::PeerSource* )));
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::removePeerSource(kt::PeerSource* ps)
|
||
|
{
|
||
|
disconnect(ps,SIGNAL(peersReady( kt::PeerSource* )),
|
||
|
pman,SLOT(peerSourceReady( kt::PeerSource* )));
|
||
|
additional.remove(ps);
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::start()
|
||
|
{
|
||
|
if (started)
|
||
|
return;
|
||
|
|
||
|
started = true;
|
||
|
QPtrList<kt::PeerSource>::iterator i = additional.begin();
|
||
|
while (i != additional.end())
|
||
|
{
|
||
|
(*i)->start();
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (!curr)
|
||
|
{
|
||
|
if (trackers.count() > 0)
|
||
|
{
|
||
|
switchTracker(selectTracker());
|
||
|
tor->resetTrackerStats();
|
||
|
curr->start();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tor->resetTrackerStats();
|
||
|
curr->start();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::stop(WaitJob* wjob)
|
||
|
{
|
||
|
if (!started)
|
||
|
return;
|
||
|
|
||
|
started = false;
|
||
|
QPtrList<kt::PeerSource>::iterator i = additional.begin();
|
||
|
while (i != additional.end())
|
||
|
{
|
||
|
(*i)->stop();
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (curr)
|
||
|
curr->stop(wjob);
|
||
|
|
||
|
timer.stop();
|
||
|
statusChanged(i18n("Stopped"));
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::completed()
|
||
|
{
|
||
|
QPtrList<kt::PeerSource>::iterator i = additional.begin();
|
||
|
while (i != additional.end())
|
||
|
{
|
||
|
(*i)->completed();
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (curr)
|
||
|
curr->completed();
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::manualUpdate()
|
||
|
{
|
||
|
QPtrList<kt::PeerSource>::iterator i = additional.begin();
|
||
|
while (i != additional.end())
|
||
|
{
|
||
|
(*i)->manualUpdate();
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (curr)
|
||
|
{
|
||
|
timer.stop();
|
||
|
curr->manualUpdate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
KURL PeerSourceManager::getTrackerURL() const
|
||
|
{
|
||
|
if (curr)
|
||
|
return curr->trackerURL();
|
||
|
else
|
||
|
return KURL();
|
||
|
}
|
||
|
|
||
|
KURL::List PeerSourceManager::getTrackerURLs()
|
||
|
{
|
||
|
KURL::List urls;
|
||
|
const TrackerTier* t = tor->getTorrent().getTrackerList();
|
||
|
while (t)
|
||
|
{
|
||
|
urls += t->urls;
|
||
|
t = t->next;
|
||
|
}
|
||
|
|
||
|
urls += custom_trackers;
|
||
|
return urls;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::addTracker(KURL url, bool custom,int tier)
|
||
|
{
|
||
|
if (trackers.contains(url))
|
||
|
return;
|
||
|
|
||
|
Tracker* trk = 0;
|
||
|
if (url.protocol() == "udp")
|
||
|
trk = new UDPTracker(url,tor,tor->getTorrent().getPeerID(),tier);
|
||
|
else
|
||
|
trk = new HTTPTracker(url,tor,tor->getTorrent().getPeerID(),tier);
|
||
|
|
||
|
addTracker(trk);
|
||
|
if (custom)
|
||
|
{
|
||
|
custom_trackers.append(url);
|
||
|
if (!no_save_custom_trackers)
|
||
|
saveCustomURLs();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool PeerSourceManager::removeTracker(KURL url)
|
||
|
{
|
||
|
if (!custom_trackers.contains(url))
|
||
|
return false;
|
||
|
|
||
|
custom_trackers.remove(url);
|
||
|
Tracker* trk = trackers.find(url);
|
||
|
if (curr == trk)
|
||
|
{
|
||
|
// do a timed delete on the tracker, so the stop signal
|
||
|
// has plenty of time to reach it
|
||
|
trk->stop();
|
||
|
trk->timedDelete(10 * 1000);
|
||
|
trackers.setAutoDelete(false);
|
||
|
trackers.erase(url);
|
||
|
trackers.setAutoDelete(true);
|
||
|
|
||
|
if (trackers.count() > 0)
|
||
|
{
|
||
|
switchTracker(selectTracker());
|
||
|
tor->resetTrackerStats();
|
||
|
curr->start();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// just delete if not the current one
|
||
|
trackers.erase(url);
|
||
|
}
|
||
|
saveCustomURLs();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::setTracker(KURL url)
|
||
|
{
|
||
|
Tracker* trk = trackers.find(url);
|
||
|
if (!trk)
|
||
|
return;
|
||
|
|
||
|
if (curr != trk)
|
||
|
{
|
||
|
if (curr)
|
||
|
curr->stop();
|
||
|
switchTracker(trk);
|
||
|
tor->resetTrackerStats();
|
||
|
trk->start();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::restoreDefault()
|
||
|
{
|
||
|
KURL::List::iterator i = custom_trackers.begin();
|
||
|
while (i != custom_trackers.end())
|
||
|
{
|
||
|
Tracker* t = trackers.find(*i);
|
||
|
if (t)
|
||
|
{
|
||
|
if (curr == t)
|
||
|
{
|
||
|
if (t->isStarted())
|
||
|
t->stop();
|
||
|
|
||
|
curr = 0;
|
||
|
trackers.erase(*i);
|
||
|
if (trackers.count() > 0)
|
||
|
{
|
||
|
switchTracker(trackers.begin()->second);
|
||
|
if (started)
|
||
|
{
|
||
|
tor->resetTrackerStats();
|
||
|
curr->start();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
trackers.erase(*i);
|
||
|
}
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
custom_trackers.clear();
|
||
|
saveCustomURLs();
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::saveCustomURLs()
|
||
|
{
|
||
|
QString trackers_file = tor->getTorDir() + "trackers";
|
||
|
QFile file(trackers_file);
|
||
|
if(!file.open(IO_WriteOnly))
|
||
|
return;
|
||
|
|
||
|
QTextStream stream(&file);
|
||
|
for (KURL::List::iterator i = custom_trackers.begin();i != custom_trackers.end();i++)
|
||
|
stream << (*i).prettyURL() << ::endl;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::loadCustomURLs()
|
||
|
{
|
||
|
QString trackers_file = tor->getTorDir() + "trackers";
|
||
|
QFile file(trackers_file);
|
||
|
if(!file.open(IO_ReadOnly))
|
||
|
return;
|
||
|
|
||
|
no_save_custom_trackers = true;
|
||
|
QTextStream stream(&file);
|
||
|
while (!stream.atEnd())
|
||
|
{
|
||
|
KURL url = stream.readLine();
|
||
|
addTracker(url,true);
|
||
|
}
|
||
|
no_save_custom_trackers = false;
|
||
|
}
|
||
|
|
||
|
Tracker* PeerSourceManager::selectTracker()
|
||
|
{
|
||
|
Tracker* n = 0;
|
||
|
PtrMap<KURL,Tracker>::iterator i = trackers.begin();
|
||
|
while (i != trackers.end())
|
||
|
{
|
||
|
Tracker* t = i->second;
|
||
|
if (!n)
|
||
|
n = t;
|
||
|
else if (t->failureCount() < n->failureCount())
|
||
|
n = t;
|
||
|
else if (t->failureCount() == n->failureCount())
|
||
|
n = t->getTier() < n->getTier() ? t : n;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
if (n)
|
||
|
{
|
||
|
Out(SYS_TRK|LOG_DEBUG) << "Selected tracker " << n->trackerURL().prettyURL()
|
||
|
<< " (tier = " << n->getTier() << ")" << endl;
|
||
|
}
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::onTrackerError(const QString & err)
|
||
|
{
|
||
|
failures++;
|
||
|
pending = false;
|
||
|
if (started)
|
||
|
statusChanged(err);
|
||
|
|
||
|
if (!started)
|
||
|
return;
|
||
|
|
||
|
// select an other tracker
|
||
|
Tracker* trk = selectTracker();
|
||
|
|
||
|
if (!trk)
|
||
|
{
|
||
|
if (curr->failureCount() > 5)
|
||
|
{
|
||
|
// we failed to contact the only tracker 5 times in a row, so try again in
|
||
|
// 30 minutes
|
||
|
curr->setInterval(FINAL_WAIT_TIME);
|
||
|
timer.start(FINAL_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
else if (curr->failureCount() > 2)
|
||
|
{
|
||
|
// we failed to contact the only tracker 3 times in a row, so try again in
|
||
|
// a minute or 5, no need for hammering every 30 seconds
|
||
|
curr->setInterval(LONGER_WAIT_TIME);
|
||
|
timer.start(LONGER_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// lets not hammer and wait 30 seconds
|
||
|
curr->setInterval(INITIAL_WAIT_TIME);
|
||
|
timer.start(INITIAL_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
curr->stop();
|
||
|
// switch to another one
|
||
|
switchTracker(trk);
|
||
|
if (trk->failureCount() == 0)
|
||
|
{
|
||
|
tor->resetTrackerStats();
|
||
|
curr->start();
|
||
|
}
|
||
|
else if (trk->failureCount() > 5)
|
||
|
{
|
||
|
curr->setInterval(FINAL_WAIT_TIME);
|
||
|
timer.start(FINAL_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
else if (trk->failureCount() > 2)
|
||
|
{
|
||
|
// we tried everybody 3 times and it didn't work
|
||
|
// wait 5 minutes and try again
|
||
|
curr->setInterval(LONGER_WAIT_TIME);
|
||
|
timer.start(LONGER_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// wait 30 seconds and try again
|
||
|
curr->setInterval(INITIAL_WAIT_TIME);
|
||
|
timer.start(INITIAL_WAIT_TIME * 1000,true);
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::onTrackerOK()
|
||
|
{
|
||
|
failures = 0;
|
||
|
if (started)
|
||
|
{
|
||
|
timer.start(curr->getInterval() * 1000,true);
|
||
|
curr->scrape();
|
||
|
}
|
||
|
pending = false;
|
||
|
if (started)
|
||
|
statusChanged(i18n("OK"));
|
||
|
request_time = QDateTime::currentDateTime();
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::onTrackerRequestPending()
|
||
|
{
|
||
|
if (started)
|
||
|
statusChanged(i18n("Announcing"));
|
||
|
pending = true;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::updateCurrentManually()
|
||
|
{
|
||
|
if (!curr)
|
||
|
return;
|
||
|
|
||
|
if (!curr->isStarted())
|
||
|
tor->resetTrackerStats();
|
||
|
|
||
|
curr->manualUpdate();
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::switchTracker(Tracker* trk)
|
||
|
{
|
||
|
if (curr == trk)
|
||
|
return;
|
||
|
|
||
|
if (curr)
|
||
|
{
|
||
|
disconnect(curr,SIGNAL(requestFailed( const QString& )),
|
||
|
this,SLOT(onTrackerError( const QString& )));
|
||
|
disconnect(curr,SIGNAL(requestOK()),this,SLOT(onTrackerOK()));
|
||
|
disconnect(curr,SIGNAL(requestPending()),this,SLOT(onTrackerRequestPending()));
|
||
|
curr = 0;
|
||
|
}
|
||
|
|
||
|
curr = trk;
|
||
|
if (curr)
|
||
|
{
|
||
|
Out(SYS_TRK|LOG_NOTICE) << "Switching to tracker " << trk->trackerURL() << endl;
|
||
|
QObject::connect(curr,SIGNAL(requestFailed( const QString& )),
|
||
|
this,SLOT(onTrackerError( const QString& )));
|
||
|
|
||
|
QObject::connect(curr,SIGNAL(requestOK()),
|
||
|
this,SLOT(onTrackerOK()));
|
||
|
|
||
|
QObject::connect(curr,SIGNAL(requestPending()),
|
||
|
this,SLOT(onTrackerRequestPending()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Uint32 PeerSourceManager::getTimeToNextUpdate() const
|
||
|
{
|
||
|
if (pending || !started || !curr)
|
||
|
return 0;
|
||
|
|
||
|
return curr->getInterval() - request_time.secsTo(QDateTime::currentDateTime());
|
||
|
}
|
||
|
|
||
|
Uint32 PeerSourceManager::getNumSeeders() const
|
||
|
{
|
||
|
return curr ? curr->getNumSeeders() : 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
Uint32 PeerSourceManager::getNumLeechers() const
|
||
|
{
|
||
|
return curr ? curr->getNumLeechers() : 0;
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::addDHT()
|
||
|
{
|
||
|
if(m_dht)
|
||
|
{
|
||
|
removePeerSource(m_dht);
|
||
|
delete m_dht;
|
||
|
}
|
||
|
|
||
|
m_dht = new dht::DHTTrackerBackend(Globals::instance().getDHT(),tor);
|
||
|
|
||
|
// add the DHT source
|
||
|
addPeerSource(m_dht);
|
||
|
}
|
||
|
|
||
|
void PeerSourceManager::removeDHT()
|
||
|
{
|
||
|
if(m_dht == 0)
|
||
|
{
|
||
|
removePeerSource(m_dht);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
removePeerSource(m_dht);
|
||
|
delete m_dht;
|
||
|
m_dht = 0;
|
||
|
}
|
||
|
|
||
|
bool PeerSourceManager::dhtStarted()
|
||
|
{
|
||
|
return m_dht != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
#include "peersourcemanager.moc"
|