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.
1146 lines
28 KiB
1146 lines
28 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by *
|
|
* Joris Guisson <joris.guisson@gmail.com> *
|
|
* Ivan Vasic <ivasic@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 <unistd.h>
|
|
#include <tqdir.h>
|
|
#include <klocale.h>
|
|
#include <kglobal.h>
|
|
#include <tdefiledialog.h>
|
|
#include <kprogress.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kapplication.h>
|
|
#include <tdeio/job.h>
|
|
#include <tdeio/netaccess.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <util/log.h>
|
|
#include <torrent/torrentcontrol.h>
|
|
#include <torrent/globals.h>
|
|
#include <util/error.h>
|
|
#include <util/fileops.h>
|
|
#include <util/waitjob.h>
|
|
#include <torrent/torrentcreator.h>
|
|
#include <torrent/server.h>
|
|
#include <torrent/authenticationmonitor.h>
|
|
#include <util/functions.h>
|
|
#include <torrent/ipblocklist.h>
|
|
#include <kademlia/dhtbase.h>
|
|
#include <torrent/torrentfile.h>
|
|
|
|
#include "pluginmanager.h"
|
|
#include <torrent/queuemanager.h>
|
|
|
|
#include "ktorrentcore.h"
|
|
#include "settings.h"
|
|
#include "functions.h"
|
|
#include "fileselectdlg.h"
|
|
#include "scandialog.h"
|
|
|
|
|
|
#include <util/profiler.h>
|
|
|
|
|
|
using namespace bt;
|
|
using namespace kt;
|
|
|
|
const Uint32 CORE_UPDATE_INTERVAL = 250;
|
|
|
|
|
|
|
|
KTorrentCore::KTorrentCore(kt::GUIInterface* gui) : max_downloads(0),keep_seeding(true),pman(0)
|
|
{
|
|
UpdateCurrentTime();
|
|
|
|
qman = new QueueManager();
|
|
connect(qman, TQT_SIGNAL(lowDiskSpace(kt::TorrentInterface*,bool)), TQT_TQOBJECT(this), TQT_SLOT(onLowDiskSpace(kt::TorrentInterface*,bool)));
|
|
|
|
|
|
data_dir = Settings::tempDir();
|
|
bool dd_not_exist = !bt::Exists(data_dir);
|
|
if (data_dir == TQString() || dd_not_exist)
|
|
{
|
|
data_dir = TDEGlobal::dirs()->saveLocation("data","ktorrent");
|
|
if (dd_not_exist)
|
|
{
|
|
Settings::setTempDir(data_dir);
|
|
Settings::writeConfig();
|
|
}
|
|
}
|
|
|
|
removed_bytes_up = removed_bytes_down = 0;
|
|
|
|
if (!data_dir.endsWith(bt::DirSeparator()))
|
|
data_dir += bt::DirSeparator();
|
|
// downloads.setAutoDelete(true);
|
|
|
|
connect(&update_timer,TQT_SIGNAL(timeout()),this,TQT_SLOT(update()));
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
|
|
Uint16 port = Settings::port();
|
|
if (port == 0)
|
|
{
|
|
port = 6881;
|
|
Settings::setPort(6881);
|
|
}
|
|
Uint16 i = 0;
|
|
do
|
|
{
|
|
Globals::instance().initServer(port + i);
|
|
i++;
|
|
}
|
|
while (!Globals::instance().getServer().isOK() && i < 10);
|
|
|
|
if (Globals::instance().getServer().isOK())
|
|
{
|
|
if (port != port + i - 1)
|
|
KMessageBox::information(0,
|
|
i18n("Specified port (%1) is unavailable or in"
|
|
" use by another application. KTorrent is now using port %2.")
|
|
.arg(port).arg(port + i - 1));
|
|
|
|
Out(SYS_GEN|LOG_NOTICE) << "Bound to port " << (port + i - 1) << endl;
|
|
}
|
|
else
|
|
{
|
|
KMessageBox::error(0,
|
|
i18n("KTorrent is unable to accept connections because the ports %1 to %2 are "
|
|
"already in use by another program.").arg(port).arg(port + i - 1));
|
|
Out(SYS_GEN|LOG_IMPORTANT) << "Cannot find free port" << endl;
|
|
}
|
|
|
|
|
|
pman = new kt::PluginManager(this,gui);
|
|
}
|
|
|
|
|
|
KTorrentCore::~KTorrentCore()
|
|
{
|
|
delete qman;
|
|
delete pman;
|
|
}
|
|
|
|
void KTorrentCore::loadPlugins()
|
|
{
|
|
pman->loadConfigFile(TDEGlobal::dirs()->saveLocation("data","ktorrent") + "plugins");
|
|
pman->loadPluginList();
|
|
}
|
|
|
|
bool KTorrentCore::init(TorrentControl* tc,bool silently)
|
|
{
|
|
connectSignals(tc);
|
|
qman->append(tc);
|
|
|
|
bool user = false;
|
|
bool start_torrent = false;
|
|
|
|
if (!silently)
|
|
{
|
|
FileSelectDlg dlg(gman, &user, &start_torrent);
|
|
|
|
if (dlg.execute(tc) != TQDialog::Accepted)
|
|
{
|
|
remove(tc,false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
tc->createFiles();
|
|
}
|
|
catch (bt::Error & err)
|
|
{
|
|
// if we can't create files, just remove the torrent
|
|
KMessageBox::error(0,err.toString());
|
|
remove(tc,false);
|
|
return false;
|
|
}
|
|
|
|
if (tc->hasExistingFiles())
|
|
{
|
|
ScanDialog* scan_dlg = new ScanDialog(this,true);
|
|
scan_dlg->show();
|
|
scan_dlg->execute(tc,true);
|
|
}
|
|
|
|
tc->setPreallocateDiskSpace(true);
|
|
|
|
if(Settings::maxRatio()>0)
|
|
tc->setMaxShareRatio(Settings::maxRatio());
|
|
|
|
if (Settings::maxSeedTime() > 0)
|
|
tc->setMaxSeedTime(Settings::maxSeedTime());
|
|
|
|
torrentAdded(tc);
|
|
qman->torrentAdded(tc, user, start_torrent);
|
|
|
|
|
|
//now copy torrent file to user specified dir if needed
|
|
if(Settings::useTorrentCopyDir())
|
|
{
|
|
TQString torFile = tc->getTorDir();
|
|
|
|
if(!torFile.endsWith("/"))
|
|
torFile += "/";
|
|
|
|
torFile += "torrent";
|
|
|
|
TQString destination = Settings::torrentCopyDir();
|
|
|
|
if(!destination.endsWith("/"))
|
|
destination += "/";
|
|
|
|
destination += tc->getStats().torrent_name + ".torrent";
|
|
|
|
try
|
|
{
|
|
bt::CopyFile(torFile, destination, TRUE);
|
|
}
|
|
catch(bt::Error& err)
|
|
{
|
|
Out(SYS_GEN|LOG_IMPORTANT) << "Could not copy torrent file to " << destination << endl;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KTorrentCore::load(const TQByteArray & data,const TQString & dir,bool silently, const KURL& url)
|
|
{
|
|
TQString tdir = findNewTorrentDir();
|
|
TorrentControl* tc = 0;
|
|
try
|
|
{
|
|
Out(SYS_GEN|LOG_NOTICE) << "Loading torrent from data " << endl;
|
|
tc = new TorrentControl();
|
|
tc->init(qman, data, tdir, dir,
|
|
Settings::useSaveDir() ? Settings::saveDir() : TQString());
|
|
|
|
if(!init(tc,silently))
|
|
loadingFinished(url, false, true);
|
|
else
|
|
loadingFinished(url, true, false);
|
|
|
|
return true;
|
|
}
|
|
catch (bt::Error & err)
|
|
{
|
|
KMessageBox::error(0,err.toString());
|
|
delete tc;
|
|
tc = 0;
|
|
// delete tdir if necesarry
|
|
if (bt::Exists(tdir))
|
|
bt::Delete(tdir,true);
|
|
|
|
loadingFinished(url, false, false);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool KTorrentCore::load(const TQString & target,const TQString & dir,bool silently)
|
|
{
|
|
TQString tdir = findNewTorrentDir();
|
|
TorrentControl* tc = 0;
|
|
try
|
|
{
|
|
Out(SYS_GEN|LOG_NOTICE) << "Loading file " << target << endl;
|
|
tc = new TorrentControl();
|
|
tc->init(qman, target, tdir, dir,
|
|
Settings::useSaveDir() ? Settings::saveDir() : TQString());
|
|
|
|
init(tc,silently);
|
|
return true;
|
|
}
|
|
catch (bt::Error & err)
|
|
{
|
|
KMessageBox::error(0,err.toString());
|
|
delete tc;
|
|
tc = 0;
|
|
// delete tdir if necesarry
|
|
if (bt::Exists(tdir))
|
|
bt::Delete(tdir,true);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::downloadFinished(TDEIO::Job *job)
|
|
{
|
|
TDEIO::StoredTransferJob* j = (TDEIO::StoredTransferJob*)job;
|
|
int err = j->error();
|
|
if (err == TDEIO::ERR_USER_CANCELED)
|
|
{
|
|
loadingFinished(j->url(),false,true);
|
|
return;
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
loadingFinished(j->url(),false,false);
|
|
j->showErrorDialog(0);
|
|
}
|
|
else
|
|
{
|
|
// load in the file (target is always local)
|
|
TQString dir = Settings::saveDir();
|
|
if (!Settings::useSaveDir() || dir.isNull())
|
|
dir = TQDir::homeDirPath();
|
|
|
|
if (dir != TQString() && load(j->data(),dir,false, j->url()))
|
|
loadingFinished(j->url(),true,false);
|
|
else
|
|
loadingFinished(j->url(),false,true);
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::load(const KURL& url)
|
|
{
|
|
if (url.isLocalFile())
|
|
{
|
|
TQString path = url.path();
|
|
TQString dir = Settings::saveDir();
|
|
if (!Settings::useSaveDir() || dir.isNull())
|
|
dir = TQDir::homeDirPath();
|
|
|
|
if (dir != TQString() && load(path,dir,false))
|
|
loadingFinished(url,true,false);
|
|
else
|
|
loadingFinished(url,false,true);
|
|
}
|
|
else
|
|
{
|
|
TDEIO::Job* j = TDEIO::storedGet(url,false,true);
|
|
connect(j,TQT_SIGNAL(result(TDEIO::Job*)),this,TQT_SLOT(downloadFinished( TDEIO::Job* )));
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::downloadFinishedSilently(TDEIO::Job *job)
|
|
{
|
|
TDEIO::StoredTransferJob* j = (TDEIO::StoredTransferJob*)job;
|
|
int err = j->error();
|
|
if (err == TDEIO::ERR_USER_CANCELED)
|
|
{
|
|
loadingFinished(j->url(),false,true);
|
|
}
|
|
else if (err)
|
|
{
|
|
loadingFinished(j->url(),false,false);
|
|
}
|
|
else
|
|
{
|
|
TQString dir;
|
|
if (custom_save_locations.contains(j))
|
|
{
|
|
// we have a custom save location so save to that
|
|
dir = custom_save_locations[j].path();
|
|
custom_save_locations.erase(j);
|
|
}
|
|
else if (!Settings::useSaveDir())
|
|
{
|
|
// incase save dir is not set, use home director
|
|
Out(SYS_GEN|LOG_NOTICE) << "Cannot load " << j->url() << " silently, default save location not set !" << endl;
|
|
Out(SYS_GEN|LOG_NOTICE) << "Using home directory instead !" << endl;
|
|
dir = TQDir::homeDirPath();
|
|
}
|
|
else
|
|
{
|
|
dir = Settings::saveDir();
|
|
}
|
|
|
|
|
|
if (dir != TQString() && load(j->data(),dir,true,j->url()))
|
|
loadingFinished(j->url(),true,false);
|
|
else
|
|
loadingFinished(j->url(),false,false);
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::loadSilently(const KURL& url)
|
|
{
|
|
if (url.isLocalFile())
|
|
{
|
|
TQString path = url.path();
|
|
TQString dir = Settings::saveDir();
|
|
if (!Settings::useSaveDir() || dir.isNull())
|
|
{
|
|
Out(SYS_GEN|LOG_NOTICE) << "Cannot load " << path << " silently, default save location not set !" << endl;
|
|
Out(SYS_GEN|LOG_NOTICE) << "Using home directory instead !" << endl;
|
|
dir = TQDir::homeDirPath();
|
|
}
|
|
|
|
if (dir != TQString() && load(path,dir,true))
|
|
loadingFinished(url,true,false);
|
|
else
|
|
loadingFinished(url,false,true);
|
|
}
|
|
else
|
|
{
|
|
// download to a random file in tmp
|
|
TDEIO::Job* j = TDEIO::storedGet(url,false,true);
|
|
connect(j,TQT_SIGNAL(result(TDEIO::Job*)),this,TQT_SLOT(downloadFinishedSilently( TDEIO::Job* )));
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::loadSilentlyDir(const KURL& url, const KURL& savedir)
|
|
{
|
|
if (url.isLocalFile())
|
|
{
|
|
TQString path = url.path();
|
|
TQString dir = savedir.path();
|
|
TQFileInfo fi(dir);
|
|
if (!fi.exists() || !fi.isWritable() || !fi.isDir())
|
|
{
|
|
Out(SYS_GEN|LOG_NOTICE) << "Cannot load " << path << " silently, destination directory is not OK ! Using default save directory." << endl;
|
|
dir = Settings::saveDir();
|
|
if (!Settings::useSaveDir())
|
|
{
|
|
Out(SYS_GEN|LOG_NOTICE) << "Default save directory not set, using home directory !" << endl;
|
|
dir = TQDir::homeDirPath();
|
|
}
|
|
}
|
|
|
|
if (dir != TQString() && load(path,dir,true))
|
|
loadingFinished(url,true,false);
|
|
else
|
|
loadingFinished(url,false,true);
|
|
}
|
|
else
|
|
{
|
|
// download to a random file in tmp
|
|
TDEIO::Job* j = TDEIO::storedGet(url,false,true);
|
|
custom_save_locations.insert(j,savedir); // keep track of save location
|
|
connect(j,TQT_SIGNAL(result(TDEIO::Job*)),this,TQT_SLOT(downloadFinishedSilently( TDEIO::Job* )));
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::start(kt::TorrentInterface* tc)
|
|
{
|
|
kt::TorrentStartResponse reason = qman->start(tc);
|
|
switch (reason)
|
|
{
|
|
// we can return, the question to ignore the limits will have informed the user
|
|
case MAX_SHARE_RATIO_REACHED:
|
|
case START_OK: // start OK is normal
|
|
case BUSY_WITH_DATA_CHECK: // checking data, so let the torrent be
|
|
case USER_CANCELED:
|
|
return;
|
|
case NOT_ENOUGH_DISKSPACE:
|
|
case TQM_LIMITS_REACHED:
|
|
canNotStart(tc,reason);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::stop(TorrentInterface* tc, bool user)
|
|
{
|
|
qman->stop(tc, user);
|
|
}
|
|
|
|
int KTorrentCore::getNumRunning(bool onlyDownloads, bool onlySeeds) const
|
|
{
|
|
return qman->getNumRunning(onlyDownloads, onlySeeds);
|
|
}
|
|
|
|
TQString KTorrentCore::findNewTorrentDir() const
|
|
{
|
|
int i = 0;
|
|
while (true)
|
|
{
|
|
TQDir d;
|
|
TQString dir = data_dir + TQString("tor%1/").arg(i);
|
|
if (!d.exists(dir))
|
|
{
|
|
return dir;
|
|
}
|
|
i++;
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
void KTorrentCore::loadExistingTorrent(const TQString & tor_dir)
|
|
{
|
|
TorrentControl* tc = 0;
|
|
|
|
TQString idir = tor_dir;
|
|
if (!idir.endsWith(bt::DirSeparator()))
|
|
idir += bt::DirSeparator();
|
|
|
|
if (!bt::Exists(idir + "torrent"))
|
|
return;
|
|
|
|
try
|
|
{
|
|
tc = new TorrentControl();
|
|
tc->init(qman,idir + "torrent",idir,TQString(),
|
|
Settings::useSaveDir() ? Settings::saveDir() : TQString());
|
|
|
|
qman->append(tc);
|
|
connectSignals(tc);
|
|
if (tc->getStats().autostart && tc->getStats().user_controlled)
|
|
start(tc);
|
|
torrentAdded(tc);
|
|
}
|
|
catch (bt::Error & err)
|
|
{
|
|
KMessageBox::error(0,err.toString());
|
|
delete tc;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::loadTorrents()
|
|
{
|
|
TQDir dir(data_dir);
|
|
TQStringList sl = dir.entryList("tor*",TQDir::Dirs);
|
|
for (Uint32 i = 0;i < sl.count();i++)
|
|
{
|
|
|
|
TQString idir = data_dir + *sl.at(i);
|
|
if (!idir.endsWith(DirSeparator()))
|
|
idir.append(DirSeparator());
|
|
|
|
Out(SYS_GEN|LOG_NOTICE) << "Loading " << idir << endl;
|
|
loadExistingTorrent(idir);
|
|
}
|
|
qman->orderQueue();
|
|
}
|
|
|
|
void KTorrentCore::remove(TorrentInterface* tc,bool data_to)
|
|
{
|
|
try
|
|
{
|
|
const TorrentStats & s = tc->getStats();
|
|
removed_bytes_up += s.session_bytes_uploaded;
|
|
removed_bytes_down += s.session_bytes_downloaded;
|
|
stop(tc);
|
|
|
|
TQString dir = tc->getTorDir();
|
|
|
|
try
|
|
{
|
|
if (data_to)
|
|
tc->deleteDataFiles();
|
|
}
|
|
catch (Error & e)
|
|
{
|
|
KMessageBox::error(0,e.toString());
|
|
}
|
|
|
|
torrentRemoved(tc);
|
|
qman->torrentRemoved(tc);
|
|
|
|
bt::Delete(dir,false);
|
|
}
|
|
catch (Error & e)
|
|
{
|
|
KMessageBox::error(0,e.toString());
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::setMaxDownloads(int max)
|
|
{
|
|
qman->setMaxDownloads(max);
|
|
}
|
|
|
|
void KTorrentCore::setMaxSeeds(int max)
|
|
{
|
|
qman->setMaxSeeds(max);
|
|
}
|
|
|
|
void KTorrentCore::torrentFinished(kt::TorrentInterface* tc)
|
|
{
|
|
if (!keep_seeding)
|
|
tc->stop(false);
|
|
|
|
finished(tc);
|
|
qman->torrentFinished(tc);
|
|
}
|
|
|
|
void KTorrentCore::setKeepSeeding(bool ks)
|
|
{
|
|
keep_seeding = ks;
|
|
qman->setKeepSeeding(ks);
|
|
}
|
|
|
|
void KTorrentCore::onExit()
|
|
{
|
|
// stop timer to prevent updates during wait
|
|
update_timer.stop();
|
|
// stop all authentications going on
|
|
AuthenticationMonitor::instance().clear();
|
|
// shutdown the server
|
|
Globals::instance().shutdownServer();
|
|
|
|
WaitJob* job = new WaitJob(5000);
|
|
qman->onExit(job);
|
|
// wait for completion of stopped events
|
|
if (job->needToWait())
|
|
WaitJob::execute(job);
|
|
else
|
|
delete job;
|
|
|
|
pman->unloadAll(false);
|
|
qman->clear();
|
|
}
|
|
|
|
bool KTorrentCore::changeDataDir(const TQString & new_dir)
|
|
{
|
|
try
|
|
{
|
|
update_timer.stop();
|
|
// do nothing if new and old dir are the same
|
|
if (KURL(data_dir) == KURL(new_dir) || data_dir == (new_dir + bt::DirSeparator()))
|
|
{
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
return true;
|
|
}
|
|
|
|
// safety check
|
|
if (!bt::Exists(new_dir))
|
|
bt::MakeDir(new_dir);
|
|
|
|
|
|
// make sure new_dir ends with a /
|
|
TQString nd = new_dir;
|
|
if (!nd.endsWith(DirSeparator()))
|
|
nd += DirSeparator();
|
|
|
|
Out() << "Switching to datadir " << nd << endl;
|
|
|
|
qman->setPausedState(true);
|
|
|
|
TQPtrList<kt::TorrentInterface> succes;
|
|
|
|
TQPtrList<kt::TorrentInterface>::iterator i = qman->begin();
|
|
while (i != qman->end())
|
|
{
|
|
kt::TorrentInterface* tc = *i;
|
|
if (!tc->changeDataDir(nd))
|
|
{
|
|
// failure time to roll back all the succesfull tc's
|
|
rollback(succes);
|
|
// set back the old data_dir in Settings
|
|
Settings::setTempDir(data_dir);
|
|
Settings::self()->writeConfig();
|
|
qman->setPausedState(false);
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
succes.append(tc);
|
|
}
|
|
i++;
|
|
}
|
|
data_dir = nd;
|
|
qman->setPausedState(false);
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
return true;
|
|
}
|
|
catch (bt::Error & e)
|
|
{
|
|
Out(SYS_GEN|LOG_IMPORTANT) << "Error : " << e.toString() << endl;
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::rollback(const TQPtrList<kt::TorrentInterface> & succes)
|
|
{
|
|
Out() << "Error, rolling back" << endl;
|
|
update_timer.stop();
|
|
TQPtrList<kt::TorrentInterface> ::const_iterator i = succes.begin();
|
|
while (i != succes.end())
|
|
{
|
|
(*i)->rollback();
|
|
i++;
|
|
}
|
|
update_timer.start(CORE_UPDATE_INTERVAL);
|
|
}
|
|
|
|
void KTorrentCore::startAll(int type)
|
|
{
|
|
qman->startall(type);
|
|
}
|
|
|
|
void KTorrentCore::stopAll(int type)
|
|
{
|
|
qman->stopall(type);
|
|
}
|
|
|
|
void KTorrentCore::update()
|
|
{
|
|
bt::UpdateCurrentTime();
|
|
AuthenticationMonitor::instance().update();
|
|
|
|
TQPtrList<kt::TorrentInterface>::iterator i = qman->begin();
|
|
//Uint32 down_speed = 0;
|
|
while (i != qman->end())
|
|
{
|
|
bool finished = false;
|
|
kt::TorrentInterface* tc = *i;
|
|
if (tc->isCheckingData(finished))
|
|
{
|
|
if (finished)
|
|
tc->afterDataCheck();
|
|
}
|
|
else if (tc->getStats().running)
|
|
{
|
|
// see if we need to do a auto data check
|
|
if (Settings::autoRecheck() && tc->getStats().num_corrupted_chunks >= Settings::maxCorruptedBeforeRecheck())
|
|
{
|
|
Out(SYS_GEN|LOG_IMPORTANT) << "Doing an automatic data check on "
|
|
<< tc->getStats().torrent_name << endl;
|
|
|
|
ScanDialog* scan_dlg = new ScanDialog(this,false);
|
|
scan_dlg->show();
|
|
scan_dlg->execute(tc,true);
|
|
}
|
|
else
|
|
{
|
|
tc->update();
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::makeTorrent(const TQString & file,const TQStringList & trackers,
|
|
int chunk_size,const TQString & name,
|
|
const TQString & comments,bool seed,
|
|
const TQString & output_file,bool priv_tor,KProgress* prog, bool decentralized)
|
|
{
|
|
TQString tdir;
|
|
try
|
|
{
|
|
if (chunk_size < 0)
|
|
chunk_size = 256;
|
|
|
|
bt::TorrentCreator mktor(file,trackers,chunk_size,name,comments,priv_tor, decentralized);
|
|
prog->setTotalSteps(mktor.getNumChunks());
|
|
Uint32 ns = 0;
|
|
while (!mktor.calculateHash())
|
|
{
|
|
prog->setProgress(ns);
|
|
ns++;
|
|
if (ns % 10 == 0)
|
|
TDEApplication::kApplication()->processEvents();
|
|
}
|
|
|
|
mktor.saveTorrent(output_file);
|
|
tdir = findNewTorrentDir();
|
|
kt::TorrentInterface* tc = mktor.makeTC(tdir);
|
|
if (tc)
|
|
{
|
|
connectSignals(tc);
|
|
qman->append(tc);
|
|
if (seed)
|
|
start(tc);
|
|
torrentAdded(tc);
|
|
}
|
|
}
|
|
catch (bt::Error & e)
|
|
{
|
|
// cleanup if necessary
|
|
if (bt::Exists(tdir))
|
|
bt::Delete(tdir,true);
|
|
|
|
// Show error message
|
|
KMessageBox::error(0,
|
|
i18n("Cannot create torrent: %1").arg(e.toString()),
|
|
i18n("Error"));
|
|
}
|
|
}
|
|
|
|
CurrentStats KTorrentCore::getStats()
|
|
{
|
|
CurrentStats stats;
|
|
Uint64 bytes_dl = 0, bytes_ul = 0;
|
|
Uint32 speed_dl = 0, speed_ul = 0;
|
|
|
|
|
|
for ( TQPtrList<kt::TorrentInterface>::iterator i = qman->begin(); i != qman->end(); ++i )
|
|
{
|
|
kt::TorrentInterface* tc = *i;
|
|
const TorrentStats & s = tc->getStats();
|
|
speed_dl += s.download_rate;
|
|
speed_ul += s.upload_rate;
|
|
bytes_dl += s.session_bytes_downloaded;
|
|
bytes_ul += s.session_bytes_uploaded;
|
|
}
|
|
stats.download_speed = speed_dl;
|
|
stats.upload_speed = speed_ul;
|
|
stats.bytes_downloaded = bytes_dl + removed_bytes_down;
|
|
stats.bytes_uploaded = bytes_ul + removed_bytes_up;
|
|
|
|
return stats;
|
|
}
|
|
|
|
bool KTorrentCore::changePort(Uint16 port)
|
|
{
|
|
if (getNumTorrentsRunning() == 0)
|
|
{
|
|
Globals::instance().getServer().changePort(port);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void KTorrentCore::slotStoppedByError(kt::TorrentInterface* tc, TQString msg)
|
|
{
|
|
emit torrentStoppedByError(tc, msg);
|
|
}
|
|
|
|
Uint32 KTorrentCore::getNumTorrentsRunning() const
|
|
{
|
|
return qman->getNumRunning();
|
|
}
|
|
|
|
Uint32 KTorrentCore::getNumTorrentsNotRunning() const
|
|
{
|
|
return qman->count() - qman->getNumRunning();
|
|
}
|
|
|
|
int KTorrentCore::countSeeds() const
|
|
{
|
|
return qman->countSeeds();
|
|
}
|
|
|
|
int KTorrentCore::countDownloads() const
|
|
{
|
|
return qman->countDownloads();
|
|
}
|
|
|
|
void KTorrentCore::addBlockedIP(TQString& ip)
|
|
{
|
|
IPBlocklist& filter = IPBlocklist::instance();
|
|
filter.addRange(ip);
|
|
}
|
|
|
|
void KTorrentCore::removeBlockedIP(TQString& ip)
|
|
{
|
|
IPBlocklist& filter = IPBlocklist::instance();
|
|
filter.removeRange(ip);
|
|
}
|
|
|
|
bt::QueueManager* KTorrentCore::getQueueManager()
|
|
{
|
|
return this->qman;
|
|
}
|
|
|
|
void KTorrentCore::torrentSeedAutoStopped( kt::TorrentInterface * tc,kt::AutoStopReason reason )
|
|
{
|
|
qman->startNext();
|
|
if (reason == kt::MAX_RATIO_REACHED)
|
|
maxShareRatioReached(tc);
|
|
else
|
|
maxSeedTimeReached(tc);
|
|
}
|
|
|
|
int KTorrentCore::getMaxUploadSpeed()
|
|
{
|
|
return Settings::maxUploadRate();
|
|
}
|
|
|
|
int KTorrentCore::getMaxDownloadSpeed()
|
|
{
|
|
return Settings::maxDownloadRate();
|
|
}
|
|
|
|
void KTorrentCore::setMaxUploadSpeed(int v)
|
|
{
|
|
return Settings::setMaxUploadRate(v);
|
|
}
|
|
|
|
void KTorrentCore::setMaxDownloadSpeed(int v)
|
|
{
|
|
return Settings::setMaxDownloadRate(v);
|
|
}
|
|
|
|
void KTorrentCore::setPausedState(bool pause)
|
|
{
|
|
qman->setPausedState(pause);
|
|
}
|
|
|
|
void KTorrentCore::queue(kt::TorrentInterface* tc)
|
|
{
|
|
qman->queue(tc);
|
|
}
|
|
|
|
TorrentInterface* KTorrentCore::getTorFromNumber(int tornumber)
|
|
{
|
|
TQString tordir = data_dir + "tor" + TQString("%1").arg(tornumber) + "/";
|
|
Out() << "tordir " << tordir << endl;
|
|
TQPtrList<TorrentInterface>::iterator i = qman->begin();
|
|
while(i != qman->end())
|
|
{
|
|
TorrentInterface* tc = *i;
|
|
TQString td = tc->getTorDir();
|
|
if(td == tordir)
|
|
return tc;
|
|
i++;
|
|
}
|
|
TorrentInterface* nullinterface = 0;
|
|
return nullinterface;
|
|
}
|
|
|
|
TQValueList<int> KTorrentCore::getTorrentNumbers(int type = 3)
|
|
{
|
|
TQValueList<int> tornums;
|
|
TQPtrList<TorrentInterface>::iterator i = qman->begin();
|
|
while(i != qman->end())
|
|
{
|
|
TorrentInterface* tc = *i;
|
|
if((type == 1 && tc->getStats().completed) ||
|
|
(type == 2 && !(tc->getStats().completed)))
|
|
{
|
|
Out() << "Skipping a torrent" << endl;
|
|
i++;
|
|
continue;
|
|
}
|
|
TQString td = tc->getTorDir();
|
|
Out() << td << endl;
|
|
td = td.remove(0, td.length() - 6);
|
|
td = td.remove(TQRegExp("[^0-9]*"));
|
|
Out() << td << endl;
|
|
tornums.append(td.toInt());
|
|
i++;
|
|
}
|
|
return tornums;
|
|
}
|
|
|
|
Uint32 KTorrentCore::getFileCount(int tornumber)
|
|
{
|
|
kt::TorrentInterface* tc = getTorFromNumber(tornumber);
|
|
if(tc)
|
|
return tc->getNumFiles();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
QCStringList KTorrentCore::getFileNames(int tornumber)
|
|
{
|
|
QCStringList filenames;
|
|
kt::TorrentInterface* tc = getTorFromNumber(tornumber);
|
|
if(!tc || tc->getNumFiles() == 0)
|
|
return filenames;
|
|
for(Uint32 i = 0; i < tc->getNumFiles(); i++)
|
|
{
|
|
TQCString filename = tc->getTorrentFile(i).getPath().ascii();
|
|
filenames.append(filename);
|
|
}
|
|
|
|
return filenames;
|
|
}
|
|
|
|
TQValueList<int> KTorrentCore::getFilePriorities(int tornumber)
|
|
{
|
|
TQValueList<int> priorities;
|
|
kt::TorrentInterface* tc = getTorFromNumber(tornumber);
|
|
if(!tc || tc->getNumFiles() == 0)
|
|
return priorities;
|
|
|
|
for(Uint32 i = 0; i < tc->getNumFiles(); i++)
|
|
{
|
|
bt::Priority priority = tc->getTorrentFile(i).getPriority();
|
|
int newpriority;
|
|
switch(priority)
|
|
{
|
|
case bt::FIRST_PRIORITY:
|
|
newpriority = 3;
|
|
break;
|
|
case bt::LAST_PRIORITY:
|
|
newpriority = 1;
|
|
break;
|
|
case bt::EXCLUDED:
|
|
newpriority = 0;
|
|
break;
|
|
default:
|
|
newpriority = 2;
|
|
break;
|
|
}
|
|
priorities.append(newpriority);
|
|
}
|
|
return priorities;
|
|
}
|
|
|
|
void KTorrentCore::setFilePriority(kt::TorrentInterface* tc, Uint32 index,
|
|
int priority)
|
|
{
|
|
bt::Priority newpriority;
|
|
switch(priority)
|
|
{
|
|
case 3:
|
|
newpriority = bt::FIRST_PRIORITY;
|
|
break;
|
|
case 1:
|
|
newpriority = bt::LAST_PRIORITY;
|
|
break;
|
|
case 0:
|
|
newpriority = bt::EXCLUDED;
|
|
break;
|
|
default:
|
|
newpriority = bt::NORMAL_PRIORITY;
|
|
break;
|
|
}
|
|
|
|
tc->getTorrentFile(index).setPriority(newpriority);
|
|
}
|
|
|
|
void KTorrentCore::announceByTorNum(int tornumber)
|
|
{
|
|
kt::TorrentInterface* tc = getTorFromNumber(tornumber);
|
|
if(tc)
|
|
tc->updateTracker();
|
|
}
|
|
|
|
void KTorrentCore::aboutToBeStarted(kt::TorrentInterface* tc,bool & ret)
|
|
{
|
|
ret = true;
|
|
|
|
TQStringList missing;
|
|
if (!tc->hasMissingFiles(missing))
|
|
return;
|
|
|
|
|
|
if (tc->getStats().multi_file_torrent)
|
|
{
|
|
TQString msg = i18n("Several data files of the torrent \"%1\" are missing, do you want to recreate them, or do you want to not download them?").arg(tc->getStats().torrent_name);
|
|
|
|
int ret = KMessageBox::warningYesNoCancelList(0,msg,missing,TQString(),
|
|
KGuiItem(i18n("Recreate")),KGuiItem(i18n("Do Not Download")));
|
|
if (ret == KMessageBox::Yes)
|
|
{
|
|
try
|
|
{
|
|
// recreate them
|
|
tc->recreateMissingFiles();
|
|
}
|
|
catch (bt::Error & e)
|
|
{
|
|
KMessageBox::error(0,i18n("Cannot recreate missing files: %1").arg(e.toString()));
|
|
tc->handleError(i18n("Data files are missing"));
|
|
ret = false;
|
|
}
|
|
}
|
|
else if (ret == KMessageBox::No)
|
|
{
|
|
try
|
|
{
|
|
// mark them as do not download
|
|
tc->dndMissingFiles();
|
|
}
|
|
catch (bt::Error & e)
|
|
{
|
|
KMessageBox::error(0,i18n("Cannot deselect missing files: %1").arg(e.toString()));
|
|
tc->handleError(i18n("Data files are missing"));
|
|
ret = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tc->handleError(i18n("Data files are missing"));
|
|
ret = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TQString msg = i18n("The file where the data is saved of the torrent \"%1\" is missing, do you want to recreate it?").arg(tc->getStats().torrent_name);
|
|
int ret = KMessageBox::warningYesNo(0,msg, i18n("Recreate"),KGuiItem(i18n("Recreate")),KGuiItem(i18n("Do Not Recreate")));
|
|
if (ret == KMessageBox::Yes)
|
|
{
|
|
try
|
|
{
|
|
tc->recreateMissingFiles();
|
|
}
|
|
catch (bt::Error & e)
|
|
{
|
|
KMessageBox::error(0,i18n("Cannot recreate data file: %1").arg(e.toString()));
|
|
tc->handleError(i18n("Data file is missing"));
|
|
ret = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tc->handleError(i18n("Data file is missing"));
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void KTorrentCore::emitCorruptedData(kt::TorrentInterface* tc)
|
|
{
|
|
corruptedData(tc);
|
|
|
|
}
|
|
|
|
void KTorrentCore::connectSignals(kt::TorrentInterface* tc)
|
|
{
|
|
connect(tc,TQT_SIGNAL(finished(kt::TorrentInterface*)),
|
|
this,TQT_SLOT(torrentFinished(kt::TorrentInterface* )));
|
|
connect(tc, TQT_SIGNAL(stoppedByError(kt::TorrentInterface*, TQString )),
|
|
TQT_TQOBJECT(this), TQT_SLOT(slotStoppedByError(kt::TorrentInterface*, TQString )));
|
|
connect(tc, TQT_SIGNAL(seedingAutoStopped(kt::TorrentInterface*, kt::AutoStopReason)),
|
|
TQT_TQOBJECT(this), TQT_SLOT(torrentSeedAutoStopped(kt::TorrentInterface*, kt::AutoStopReason)));
|
|
connect(tc,TQT_SIGNAL(aboutToBeStarted( kt::TorrentInterface*,bool & )),
|
|
TQT_TQOBJECT(this), TQT_SLOT(aboutToBeStarted( kt::TorrentInterface*,bool & )));
|
|
connect(tc,TQT_SIGNAL(corruptedDataFound( kt::TorrentInterface* )),
|
|
TQT_TQOBJECT(this), TQT_SLOT(emitCorruptedData( kt::TorrentInterface* )));
|
|
connect(qman, TQT_SIGNAL(queuingNotPossible(kt::TorrentInterface*)),
|
|
TQT_TQOBJECT(this), TQT_SLOT(enqueueTorrentOverMaxRatio( kt::TorrentInterface* )));
|
|
connect(qman, TQT_SIGNAL(lowDiskSpace(kt::TorrentInterface*, bool)),
|
|
TQT_TQOBJECT(this), TQT_SLOT(onLowDiskSpace(kt::TorrentInterface*, bool)));
|
|
|
|
}
|
|
|
|
float KTorrentCore::getGlobalMaxShareRatio() const
|
|
{
|
|
return Settings::maxRatio();
|
|
}
|
|
|
|
void KTorrentCore::enqueueTorrentOverMaxRatio(kt::TorrentInterface* tc)
|
|
{
|
|
emit queuingNotPossible(tc);
|
|
}
|
|
|
|
|
|
void KTorrentCore::doDataCheck(kt::TorrentInterface* tc)
|
|
{
|
|
bool dummy = false;
|
|
if (tc->isCheckingData(dummy))
|
|
return;
|
|
|
|
ScanDialog* scan_dlg = new ScanDialog(this,false);
|
|
scan_dlg->setCaption(i18n("Checking Data Integrity"));
|
|
scan_dlg->show();
|
|
scan_dlg->execute(tc,false);
|
|
}
|
|
|
|
void KTorrentCore::onLowDiskSpace(kt::TorrentInterface * tc, bool stopped)
|
|
{
|
|
emit lowDiskSpace(tc, stopped);
|
|
}
|
|
|
|
#include "ktorrentcore.moc"
|