mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-17 22:18:05 -06:00
Merge pull request #2892 from glassez/bittorrent
Core classes redesign (Issue #2433).
This commit is contained in:
@@ -32,8 +32,10 @@
|
|||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
|
#include "gui/guiiconprovider.h"
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
@@ -46,26 +48,38 @@
|
|||||||
#endif // Q_OS_MAC
|
#endif // Q_OS_MAC
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "addnewtorrentdialog.h"
|
#include "addnewtorrentdialog.h"
|
||||||
|
#include "shutdownconfirm.h"
|
||||||
#else // DISABLE_GUI
|
#else // DISABLE_GUI
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
#include "../webui/webui.h"
|
#include "webui/webui.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "logger.h"
|
#include "core/logger.h"
|
||||||
#include "preferences.h"
|
#include "core/preferences.h"
|
||||||
#include "qbtsession.h"
|
#include "core/utils/misc.h"
|
||||||
#include "torrentpersistentdata.h"
|
#include "core/iconprovider.h"
|
||||||
|
#include "core/scanfoldersmodel.h"
|
||||||
|
#include "core/net/smtp.h"
|
||||||
|
#include "core/net/downloadmanager.h"
|
||||||
|
#include "core/bittorrent/session.h"
|
||||||
|
#include "core/bittorrent/torrenthandle.h"
|
||||||
|
|
||||||
static const char PARAMS_SEPARATOR[] = "|";
|
static const char PARAMS_SEPARATOR[] = "|";
|
||||||
|
|
||||||
Application::Application(const QString &id, int &argc, char **argv)
|
Application::Application(const QString &id, int &argc, char **argv)
|
||||||
: BaseApplication(id, argc, argv)
|
: BaseApplication(id, argc, argv)
|
||||||
, m_running(false)
|
, m_running(false)
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
, m_shutdownAct(ShutdownAction::None)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
|
Logger::initInstance();
|
||||||
|
Preferences::initInstance();
|
||||||
|
|
||||||
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
|
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
|
||||||
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
|
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
|
||||||
// fix Mac OS X 10.9 (mavericks) font issue
|
// fix Mac OS X 10.9 (mavericks) font issue
|
||||||
@@ -85,6 +99,8 @@ Application::Application(const QString &id, int &argc, char **argv)
|
|||||||
|
|
||||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
||||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
||||||
|
|
||||||
|
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processMessage(const QString &message)
|
void Application::processMessage(const QString &message)
|
||||||
@@ -98,6 +114,87 @@ void Application::processMessage(const QString &message)
|
|||||||
m_paramsQueue.append(params);
|
m_paramsQueue.append(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent)
|
||||||
|
{
|
||||||
|
// Prepare mail content
|
||||||
|
QString content = QObject::tr("Torrent name: %1").arg(torrent->name()) + "\n";
|
||||||
|
content += QObject::tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + "\n";
|
||||||
|
content += QObject::tr("Save path: %1").arg(torrent->savePath()) + "\n\n";
|
||||||
|
content += QObject::tr("The torrent was downloaded in %1.",
|
||||||
|
"The torrent was downloaded in 1 hour and 20 seconds")
|
||||||
|
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n";
|
||||||
|
content += QObject::tr("Thank you for using qBittorrent.") + "\n";
|
||||||
|
|
||||||
|
// Send the notification email
|
||||||
|
Net::Smtp *sender = new Net::Smtp;
|
||||||
|
sender->sendMail("notification@qbittorrent.org",
|
||||||
|
Preferences::instance()->getMailNotificationEmail(),
|
||||||
|
QObject::tr("[qBittorrent] %1 has finished downloading").arg(torrent->name()),
|
||||||
|
content);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent)
|
||||||
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
|
||||||
|
// AutoRun program
|
||||||
|
if (pref->isAutoRunEnabled()) {
|
||||||
|
QString program = pref->getAutoRunProgram().trimmed();
|
||||||
|
// Replace %f by torrent path
|
||||||
|
program.replace("%f", torrent->savePathParsed());
|
||||||
|
// Replace %n by torrent name
|
||||||
|
program.replace("%n", torrent->name());
|
||||||
|
QProcess::startDetached(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mail notification
|
||||||
|
if (pref->isMailNotificationEnabled())
|
||||||
|
sendNotificationEmail(torrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::allTorrentsFinished()
|
||||||
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
bool will_shutdown = (pref->shutdownWhenDownloadsComplete()
|
||||||
|
|| pref->shutdownqBTWhenDownloadsComplete()
|
||||||
|
|| pref->suspendWhenDownloadsComplete()
|
||||||
|
|| pref->hibernateWhenDownloadsComplete());
|
||||||
|
|
||||||
|
// Auto-Shutdown
|
||||||
|
if (will_shutdown) {
|
||||||
|
bool suspend = pref->suspendWhenDownloadsComplete();
|
||||||
|
bool hibernate = pref->hibernateWhenDownloadsComplete();
|
||||||
|
bool shutdown = pref->shutdownWhenDownloadsComplete();
|
||||||
|
|
||||||
|
// Confirm shutdown
|
||||||
|
ShutdownAction action = ShutdownAction::None;
|
||||||
|
if (suspend)
|
||||||
|
action = ShutdownAction::Suspend;
|
||||||
|
else if (hibernate)
|
||||||
|
action = ShutdownAction::Hibernate;
|
||||||
|
else if (shutdown)
|
||||||
|
action = ShutdownAction::Shutdown;
|
||||||
|
|
||||||
|
if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
|
||||||
|
|
||||||
|
// Actually shut down
|
||||||
|
if (suspend || hibernate || shutdown) {
|
||||||
|
qDebug("Preparing for auto-shutdown because all downloads are complete!");
|
||||||
|
// Disabling it for next time
|
||||||
|
pref->setShutdownWhenDownloadsComplete(false);
|
||||||
|
pref->setSuspendWhenDownloadsComplete(false);
|
||||||
|
pref->setHibernateWhenDownloadsComplete(false);
|
||||||
|
// Make sure preferences are synced before exiting
|
||||||
|
m_shutdownAct = action;
|
||||||
|
}
|
||||||
|
qDebug("Exiting the application");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
#endif // DISABLE_GUI
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::sendParams(const QStringList ¶ms)
|
bool Application::sendParams(const QStringList ¶ms)
|
||||||
{
|
{
|
||||||
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
|
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
|
||||||
@@ -114,45 +211,32 @@ void Application::processParams(const QStringList ¶ms)
|
|||||||
m_window->activate(); // show UI
|
m_window->activate(); // show UI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
foreach (QString param, params) {
|
foreach (QString param, params) {
|
||||||
param = param.trimmed();
|
param = param.trimmed();
|
||||||
if (misc::isUrl(param)) {
|
|
||||||
QBtSession::instance()->downloadFromUrl(param);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
|
||||||
qDebug("Converting bc link to magnet link");
|
|
||||||
param = misc::bcLinkToMagnet(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (param.startsWith("magnet:", Qt::CaseInsensitive)) {
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
if (useTorrentAdditionDialog)
|
if (Preferences::instance()->useAdditionDialog())
|
||||||
AddNewTorrentDialog::showMagnet(param, m_window);
|
AddNewTorrentDialog::show(param, m_window);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
QBtSession::instance()->addMagnetUri(param);
|
BitTorrent::Session::instance()->addTorrent(param);
|
||||||
}
|
|
||||||
else {
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
if (useTorrentAdditionDialog)
|
|
||||||
AddNewTorrentDialog::showTorrent(param, QString(), m_window);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
QBtSession::instance()->addTorrent(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Application::exec(const QStringList ¶ms)
|
int Application::exec(const QStringList ¶ms)
|
||||||
{
|
{
|
||||||
// Resume unfinished torrents
|
Net::DownloadManager::initInstance();
|
||||||
QBtSession::instance()->startUpTorrents();
|
#ifdef DISABLE_GUI
|
||||||
|
IconProvider::initInstance();
|
||||||
|
#else
|
||||||
|
GuiIconProvider::initInstance();
|
||||||
|
#endif
|
||||||
|
BitTorrent::Session::initInstance();
|
||||||
|
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
|
||||||
|
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
|
||||||
|
|
||||||
|
ScanFoldersModel::initInstance(this);
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
m_webui = new WebUI;
|
m_webui = new WebUI;
|
||||||
@@ -293,6 +377,8 @@ void Application::initializeTranslation()
|
|||||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||||
void Application::shutdownCleanup(QSessionManager &manager)
|
void Application::shutdownCleanup(QSessionManager &manager)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(manager);
|
||||||
|
|
||||||
// This is only needed for a special case on Windows XP.
|
// This is only needed for a special case on Windows XP.
|
||||||
// (but is called for every Windows version)
|
// (but is called for every Windows version)
|
||||||
// If a process takes too much time to exit during OS
|
// If a process takes too much time to exit during OS
|
||||||
@@ -356,10 +442,13 @@ void Application::cleanup()
|
|||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
delete m_webui;
|
delete m_webui;
|
||||||
#endif
|
#endif
|
||||||
QBtSession::drop();
|
|
||||||
TorrentPersistentData::drop();
|
ScanFoldersModel::freeInstance();
|
||||||
Preferences::drop();
|
BitTorrent::Session::freeInstance();
|
||||||
Logger::drop();
|
Preferences::freeInstance();
|
||||||
|
Logger::freeInstance();
|
||||||
|
IconProvider::freeInstance();
|
||||||
|
Net::DownloadManager::freeInstance();
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
||||||
@@ -369,6 +458,9 @@ void Application::cleanup()
|
|||||||
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
delete m_window;
|
delete m_window;
|
||||||
|
if (m_shutdownAct != ShutdownAction::None) {
|
||||||
|
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
|
||||||
|
Utils::Misc::shutdownComputer(m_shutdownAct);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,10 +50,17 @@ QT_END_NAMESPACE
|
|||||||
typedef QtSingleCoreApplication BaseApplication;
|
typedef QtSingleCoreApplication BaseApplication;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
class WebUI;
|
class WebUI;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TorrentHandle;
|
||||||
|
}
|
||||||
|
|
||||||
class Application : public BaseApplication
|
class Application : public BaseApplication
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -77,6 +84,8 @@ protected:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void processMessage(const QString &message);
|
void processMessage(const QString &message);
|
||||||
|
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void allTorrentsFinished();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||||
void shutdownCleanup(QSessionManager &manager);
|
void shutdownCleanup(QSessionManager &manager);
|
||||||
@@ -87,6 +96,7 @@ private:
|
|||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
QPointer<MainWindow> m_window;
|
QPointer<MainWindow> m_window;
|
||||||
|
ShutdownAction m_shutdownAct;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
@@ -99,6 +109,7 @@ private:
|
|||||||
|
|
||||||
void initializeTranslation();
|
void initializeTranslation();
|
||||||
void processParams(const QStringList ¶ms);
|
void processParams(const QStringList ¶ms);
|
||||||
|
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // APPLICATION_H
|
#endif // APPLICATION_H
|
||||||
|
|||||||
@@ -66,9 +66,8 @@ Q_IMPORT_PLUGIN(qico)
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "misc.h"
|
#include "core/utils/misc.h"
|
||||||
#include "preferences.h"
|
#include "core/preferences.h"
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
// Signal handlers
|
// Signal handlers
|
||||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||||
@@ -120,13 +119,11 @@ QBtCommandLineParameters parseCommandLine();
|
|||||||
// Main
|
// Main
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
//Initialize logger singleton here to avoid threading issues
|
|
||||||
Logger::instance()->addMessage(QObject::tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
|
|
||||||
// We must save it here because QApplication constructor may change it
|
// We must save it here because QApplication constructor may change it
|
||||||
bool isOneArg = (argc == 2);
|
bool isOneArg = (argc == 2);
|
||||||
|
|
||||||
// Create Application
|
// Create Application
|
||||||
QString appId = QLatin1String("qBittorrent-") + misc::getUserIDString();
|
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
|
||||||
QScopedPointer<Application> app(new Application(appId, argc, argv));
|
QScopedPointer<Application> app(new Application(appId, argc, argv));
|
||||||
|
|
||||||
const QBtCommandLineParameters params = parseCommandLine();
|
const QBtCommandLineParameters params = parseCommandLine();
|
||||||
@@ -191,7 +188,7 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
qDebug("qBittorrent is already running for this user.");
|
qDebug("qBittorrent is already running for this user.");
|
||||||
|
|
||||||
misc::msleep(300);
|
Utils::Misc::msleep(300);
|
||||||
app->sendParams(params.torrents);
|
app->sendParams(params.torrents);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -398,7 +395,7 @@ void displayUsage(const QString& prg_name)
|
|||||||
#else
|
#else
|
||||||
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prg_name), QMessageBox::Ok);
|
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prg_name), QMessageBox::Ok);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -410,7 +407,7 @@ void displayBadArgMessage(const QString& message)
|
|||||||
QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
|
QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
|
||||||
message + QLatin1Char('\n') + help, QMessageBox::Ok);
|
message + QLatin1Char('\n') + help, QMessageBox::Ok);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
#else
|
#else
|
||||||
std::cerr << qPrintable(QObject::tr("Bad command line: "));
|
std::cerr << qPrintable(QObject::tr("Bad command line: "));
|
||||||
@@ -442,7 +439,7 @@ bool userAgreesWithLegalNotice()
|
|||||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||||
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
if (msgBox.clickedButton() == agree_button) {
|
if (msgBox.clickedButton() == agree_button) {
|
||||||
// Save the answer
|
// Save the answer
|
||||||
|
|||||||
64
src/core/bittorrent/cachestatus.cpp
Normal file
64
src/core/bittorrent/cachestatus.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cachestatus.h"
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
CacheStatus::CacheStatus(const libtorrent::cache_status &nativeStatus)
|
||||||
|
: m_nativeStatus(nativeStatus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int CacheStatus::totalUsedBuffers() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.total_used_buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal CacheStatus::readRatio() const
|
||||||
|
{
|
||||||
|
if (m_nativeStatus.blocks_read > 0)
|
||||||
|
return (static_cast<qreal>(m_nativeStatus.blocks_read_hit) / m_nativeStatus.blocks_read);
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CacheStatus::jobQueueLength() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.job_queue_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CacheStatus::averageJobTime() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.average_job_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong CacheStatus::queuedBytes() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.queued_bytes;
|
||||||
|
}
|
||||||
53
src/core/bittorrent/cachestatus.h
Normal file
53
src/core/bittorrent/cachestatus.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_CACHESTATUS_H
|
||||||
|
#define BITTORRENT_CACHESTATUS_H
|
||||||
|
|
||||||
|
#include <libtorrent/disk_io_thread.hpp>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class CacheStatus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CacheStatus(const libtorrent::cache_status &nativeStatus);
|
||||||
|
|
||||||
|
int totalUsedBuffers() const;
|
||||||
|
qreal readRatio() const;
|
||||||
|
int jobQueueLength() const;
|
||||||
|
int averageJobTime() const;
|
||||||
|
qlonglong queuedBytes() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
libtorrent::cache_status m_nativeStatus;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_CACHESTATUS_H
|
||||||
98
src/core/bittorrent/infohash.cpp
Normal file
98
src/core/bittorrent/infohash.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include "infohash.h"
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
InfoHash::InfoHash()
|
||||||
|
: m_valid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoHash::InfoHash(const libtorrent::sha1_hash &nativeHash)
|
||||||
|
: m_valid(true)
|
||||||
|
, m_nativeHash(nativeHash)
|
||||||
|
{
|
||||||
|
char out[(libtorrent::sha1_hash::size * 2) + 1];
|
||||||
|
libtorrent::to_hex((char const*)&m_nativeHash[0], libtorrent::sha1_hash::size, out);
|
||||||
|
m_hashString = QString(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoHash::InfoHash(const QString &hashString)
|
||||||
|
: m_valid(false)
|
||||||
|
, m_hashString(hashString)
|
||||||
|
{
|
||||||
|
QByteArray raw = m_hashString.toLatin1();
|
||||||
|
if (raw.size() == 40)
|
||||||
|
m_valid = libtorrent::from_hex(raw.constData(), 40, (char*)&m_nativeHash[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InfoHash::InfoHash(const InfoHash &other)
|
||||||
|
: m_valid(other.m_valid)
|
||||||
|
, m_nativeHash(other.m_nativeHash)
|
||||||
|
, m_hashString(other.m_hashString)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool InfoHash::isValid() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InfoHash::operator libtorrent::sha1_hash() const
|
||||||
|
{
|
||||||
|
return m_nativeHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InfoHash::operator QString() const
|
||||||
|
{
|
||||||
|
return m_hashString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool InfoHash::operator==(const InfoHash &other) const
|
||||||
|
{
|
||||||
|
return (m_nativeHash == other.m_nativeHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool InfoHash::operator!=(const InfoHash &other) const
|
||||||
|
{
|
||||||
|
return (m_nativeHash != other.m_nativeHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const InfoHash &key, uint seed)
|
||||||
|
{
|
||||||
|
return qHash(static_cast<QString>(key), seed);
|
||||||
|
}
|
||||||
67
src/core/bittorrent/infohash.h
Normal file
67
src/core/bittorrent/infohash.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_INFOHASH_H
|
||||||
|
#define BITTORRENT_INFOHASH_H
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
#include <libtorrent/peer_id.hpp>
|
||||||
|
#else
|
||||||
|
#include <libtorrent/sha1_hash.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class InfoHash
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InfoHash();
|
||||||
|
InfoHash(const libtorrent::sha1_hash &nativeHash);
|
||||||
|
InfoHash(const QString &hashString);
|
||||||
|
InfoHash(const InfoHash &other);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
operator libtorrent::sha1_hash() const;
|
||||||
|
operator QString() const;
|
||||||
|
bool operator==(const InfoHash &other) const;
|
||||||
|
bool operator!=(const InfoHash &other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_valid;
|
||||||
|
libtorrent::sha1_hash m_nativeHash;
|
||||||
|
QString m_hashString;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const BitTorrent::InfoHash &key, uint seed);
|
||||||
|
|
||||||
|
#endif // BITTORRENT_INFOHASH_H
|
||||||
95
src/core/bittorrent/magneturi.cpp
Normal file
95
src/core/bittorrent/magneturi.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libtorrent/bencode.hpp>
|
||||||
|
#include <libtorrent/error_code.hpp>
|
||||||
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
|
|
||||||
|
#include "core/utils/string.h"
|
||||||
|
#include "magneturi.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
MagnetUri::MagnetUri(const QString &url)
|
||||||
|
: m_valid(false)
|
||||||
|
, m_url(url)
|
||||||
|
{
|
||||||
|
if (url.isEmpty()) return;
|
||||||
|
|
||||||
|
libt::error_code ec;
|
||||||
|
libt::parse_magnet_uri(url.toUtf8().constData(), m_addTorrentParams, ec);
|
||||||
|
if (ec) return;
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
m_hash = m_addTorrentParams.info_hash;
|
||||||
|
m_name = Utils::String::fromStdString(m_addTorrentParams.name);
|
||||||
|
|
||||||
|
foreach (const std::string &tracker, m_addTorrentParams.trackers)
|
||||||
|
m_trackers.append(Utils::String::fromStdString(tracker));
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||||
|
foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds)
|
||||||
|
m_urlSeeds.append(QUrl(urlSeed.c_str()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MagnetUri::isValid() const
|
||||||
|
{
|
||||||
|
return m_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoHash MagnetUri::hash() const
|
||||||
|
{
|
||||||
|
return m_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MagnetUri::name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TrackerEntry> MagnetUri::trackers() const
|
||||||
|
{
|
||||||
|
return m_trackers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QUrl> MagnetUri::urlSeeds() const
|
||||||
|
{
|
||||||
|
return m_urlSeeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MagnetUri::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
libtorrent::add_torrent_params MagnetUri::addTorrentParams() const
|
||||||
|
{
|
||||||
|
return m_addTorrentParams;
|
||||||
|
}
|
||||||
68
src/core/bittorrent/magneturi.h
Normal file
68
src/core/bittorrent/magneturi.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_MAGNETURI_H
|
||||||
|
#define BITTORRENT_MAGNETURI_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <libtorrent/add_torrent_params.hpp>
|
||||||
|
|
||||||
|
#include "infohash.h"
|
||||||
|
#include "trackerentry.h"
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class MagnetUri
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MagnetUri(const QString &url = QString());
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
InfoHash hash() const;
|
||||||
|
QString name() const;
|
||||||
|
QList<TrackerEntry> trackers() const;
|
||||||
|
QList<QUrl> urlSeeds() const;
|
||||||
|
QString url() const;
|
||||||
|
|
||||||
|
libtorrent::add_torrent_params addTorrentParams() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_valid;
|
||||||
|
QString m_url;
|
||||||
|
InfoHash m_hash;
|
||||||
|
QString m_name;
|
||||||
|
QList<TrackerEntry> m_trackers;
|
||||||
|
QList<QUrl> m_urlSeeds;
|
||||||
|
libtorrent::add_torrent_params m_addTorrentParams;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_MAGNETURI_H
|
||||||
271
src/core/bittorrent/peerinfo.cpp
Normal file
271
src/core/bittorrent/peerinfo.cpp
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include "core/utils/string.h"
|
||||||
|
#include "peerinfo.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
// PeerAddress
|
||||||
|
|
||||||
|
PeerAddress::PeerAddress()
|
||||||
|
: port(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerAddress::PeerAddress(QHostAddress ip, ushort port)
|
||||||
|
: ip(ip)
|
||||||
|
, port(port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerInfo
|
||||||
|
|
||||||
|
PeerInfo::PeerInfo(const libt::peer_info &nativeInfo)
|
||||||
|
: m_nativeInfo(nativeInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::fromDHT() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.source & libt::peer_info::dht);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::fromPeX() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.source & libt::peer_info::pex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::fromLSD() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.source & libt::peer_info::lsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PeerInfo::country() const
|
||||||
|
{
|
||||||
|
return QString(QByteArray(m_nativeInfo.country, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isInteresting() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::interesting);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isChocked() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::choked);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isRemoteInterested() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::remote_interested);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isRemoteChocked() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::remote_choked);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isSupportsExtensions() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::supports_extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isLocalConnection() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::local_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isHandshake() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::handshake);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isConnecting() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::connecting);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isQueued() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::queued);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isOnParole() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::on_parole);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isSeed() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::optimisticUnchoke() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::optimistic_unchoke);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isSnubbed() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::snubbed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isUploadOnly() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::upload_only);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isEndgameMode() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::endgame_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PeerInfo::isHolepunched() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::holepunched);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::useI2PSocket() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::i2p_socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::useUTPSocket() const
|
||||||
|
{
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
return (m_nativeInfo.connection_type & libt::peer_info::bittorrent_utp);
|
||||||
|
#else
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::utp_socket);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::useSSLSocket() const
|
||||||
|
{
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::ssl_socket);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isRC4Encrypted() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::rc4_encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeerInfo::isPlaintextEncrypted() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo.flags & libt::peer_info::plaintext_encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PeerAddress PeerInfo::address() const
|
||||||
|
{
|
||||||
|
return PeerAddress(QHostAddress(QString::fromStdString(m_nativeInfo.ip.address().to_string())),
|
||||||
|
m_nativeInfo.ip.port());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PeerInfo::client() const
|
||||||
|
{
|
||||||
|
return Utils::String::fromStdString(m_nativeInfo.client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
qreal PeerInfo::progress() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo.progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PeerInfo::payloadUpSpeed() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo.payload_up_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int PeerInfo::payloadDownSpeed() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo.payload_down_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong PeerInfo::totalUpload() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo.total_upload;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
qlonglong PeerInfo::totalDownload() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo.total_download;
|
||||||
|
}
|
||||||
|
|
||||||
|
QBitArray PeerInfo::pieces() const
|
||||||
|
{
|
||||||
|
QBitArray result(m_nativeInfo.pieces.size());
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
typedef size_t pieces_size_t;
|
||||||
|
#else
|
||||||
|
typedef int pieces_size_t;
|
||||||
|
#endif
|
||||||
|
for (pieces_size_t i = 0; i < m_nativeInfo.pieces.size(); ++i)
|
||||||
|
result.setBit(i, m_nativeInfo.pieces.get_bit(i));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PeerInfo::connectionType() const
|
||||||
|
{
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
if (m_nativeInfo.connection_type & libt::peer_info::bittorrent_utp)
|
||||||
|
#else
|
||||||
|
if (m_nativeInfo.flags & libt::peer_info::utp_socket)
|
||||||
|
#endif
|
||||||
|
return QString::fromUtf8("μTP");
|
||||||
|
|
||||||
|
QString connection;
|
||||||
|
switch(m_nativeInfo.connection_type) {
|
||||||
|
case libt::peer_info::http_seed:
|
||||||
|
case libt::peer_info::web_seed:
|
||||||
|
connection = "Web";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
connection = "BT";
|
||||||
|
}
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
99
src/core/bittorrent/peerinfo.h
Normal file
99
src/core/bittorrent/peerinfo.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_PEERINFO_H
|
||||||
|
#define BITTORRENT_PEERINFO_H
|
||||||
|
|
||||||
|
#include <libtorrent/peer_info.hpp>
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QBitArray>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct PeerAddress
|
||||||
|
{
|
||||||
|
QHostAddress ip;
|
||||||
|
ushort port;
|
||||||
|
|
||||||
|
PeerAddress();
|
||||||
|
PeerAddress(QHostAddress ip, ushort port);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PeerInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PeerInfo(const libtorrent::peer_info &nativeInfo);
|
||||||
|
|
||||||
|
bool fromDHT() const;
|
||||||
|
bool fromPeX() const;
|
||||||
|
bool fromLSD() const;
|
||||||
|
|
||||||
|
bool isInteresting() const;
|
||||||
|
bool isChocked() const;
|
||||||
|
bool isRemoteInterested() const;
|
||||||
|
bool isRemoteChocked() const;
|
||||||
|
bool isSupportsExtensions() const;
|
||||||
|
bool isLocalConnection() const;
|
||||||
|
|
||||||
|
bool isHandshake() const;
|
||||||
|
bool isConnecting() const;
|
||||||
|
bool isQueued() const;
|
||||||
|
bool isOnParole() const;
|
||||||
|
bool isSeed() const;
|
||||||
|
|
||||||
|
bool optimisticUnchoke() const;
|
||||||
|
bool isSnubbed() const;
|
||||||
|
bool isUploadOnly() const;
|
||||||
|
bool isEndgameMode() const;
|
||||||
|
bool isHolepunched() const;
|
||||||
|
|
||||||
|
bool useI2PSocket() const;
|
||||||
|
bool useUTPSocket() const;
|
||||||
|
bool useSSLSocket() const;
|
||||||
|
|
||||||
|
bool isRC4Encrypted() const;
|
||||||
|
bool isPlaintextEncrypted() const;
|
||||||
|
|
||||||
|
PeerAddress address() const;
|
||||||
|
QString client() const;
|
||||||
|
qreal progress() const;
|
||||||
|
int payloadUpSpeed() const;
|
||||||
|
int payloadDownSpeed() const;
|
||||||
|
qlonglong totalUpload() const;
|
||||||
|
qlonglong totalDownload() const;
|
||||||
|
QBitArray pieces() const;
|
||||||
|
QString connectionType() const;
|
||||||
|
QString country() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
libtorrent::peer_info m_nativeInfo;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_PEERINFO_H
|
||||||
96
src/core/bittorrent/private/bandwidthscheduler.cpp
Normal file
96
src/core/bittorrent/private/bandwidthscheduler.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QTime>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "core/preferences.h"
|
||||||
|
#include "bandwidthscheduler.h"
|
||||||
|
|
||||||
|
BandwidthScheduler::BandwidthScheduler(QObject *parent)
|
||||||
|
: QTimer(parent)
|
||||||
|
{
|
||||||
|
Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
|
||||||
|
// Single shot, we call start() again manually
|
||||||
|
setSingleShot(true);
|
||||||
|
// Connect Signals/Slots
|
||||||
|
connect(this, SIGNAL(timeout()), this, SLOT(start()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BandwidthScheduler::start()
|
||||||
|
{
|
||||||
|
const Preferences* const pref = Preferences::instance();
|
||||||
|
Q_ASSERT(pref->isSchedulerEnabled());
|
||||||
|
bool alt_bw_enabled = pref->isAltBandwidthEnabled();
|
||||||
|
|
||||||
|
QTime start = pref->getSchedulerStartTime();
|
||||||
|
QTime end = pref->getSchedulerEndTime();
|
||||||
|
QTime now = QTime::currentTime();
|
||||||
|
int sched_days = pref->getSchedulerDays();
|
||||||
|
int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek();
|
||||||
|
bool new_mode = false;
|
||||||
|
bool reverse = false;
|
||||||
|
|
||||||
|
if (start > end) {
|
||||||
|
QTime temp = start;
|
||||||
|
start = end;
|
||||||
|
end = temp;
|
||||||
|
reverse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((start <= now) && (end >= now)) {
|
||||||
|
switch(sched_days) {
|
||||||
|
case EVERY_DAY:
|
||||||
|
new_mode = true;
|
||||||
|
break;
|
||||||
|
case WEEK_ENDS:
|
||||||
|
if ((day == 6) || (day == 7))
|
||||||
|
new_mode = true;
|
||||||
|
break;
|
||||||
|
case WEEK_DAYS:
|
||||||
|
if ((day != 6) && (day != 7))
|
||||||
|
new_mode = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (day == (sched_days - 2))
|
||||||
|
new_mode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reverse)
|
||||||
|
new_mode = !new_mode;
|
||||||
|
|
||||||
|
if (new_mode != alt_bw_enabled)
|
||||||
|
emit switchToAlternativeMode(new_mode);
|
||||||
|
|
||||||
|
// Timeout regularly to accomodate for external system clock changes
|
||||||
|
// eg from the user or from a timesync utility
|
||||||
|
QTimer::start(1500);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 sledgehammer999
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,24 +25,26 @@
|
|||||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
*
|
||||||
* Contact : hammered999@gmail.com
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TORRENTFILTERENUM_H
|
#ifndef BANDWIDTHSCHEDULER_H
|
||||||
#define TORRENTFILTERENUM_H
|
#define BANDWIDTHSCHEDULER_H
|
||||||
|
|
||||||
namespace TorrentFilter
|
#include <QTimer>
|
||||||
|
|
||||||
|
class BandwidthScheduler : public QTimer
|
||||||
{
|
{
|
||||||
enum TorrentFilter
|
Q_OBJECT
|
||||||
{
|
|
||||||
ALL,
|
public:
|
||||||
DOWNLOADING,
|
BandwidthScheduler(QObject *parent = 0);
|
||||||
SEEDING,
|
|
||||||
COMPLETED,
|
public slots:
|
||||||
RESUMED,
|
void start();
|
||||||
PAUSED,
|
|
||||||
ACTIVE,
|
signals:
|
||||||
INACTIVE
|
void switchToAlternativeMode(bool alternative);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
#endif // TORRENTFILTERENUM_H
|
#endif // BANDWIDTHSCHEDULER_H
|
||||||
439
src/core/bittorrent/private/filterparserthread.cpp
Normal file
439
src/core/bittorrent/private/filterparserthread.cpp
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libt.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <libtorrent/session.hpp>
|
||||||
|
#include <libtorrent/ip_filter.hpp>
|
||||||
|
|
||||||
|
#include "core/logger.h"
|
||||||
|
#include "filterparserthread.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
|
||||||
|
FilterParserThread::FilterParserThread(libt::session *s, QObject *parent)
|
||||||
|
: QThread(parent)
|
||||||
|
, m_session(s)
|
||||||
|
, m_abort(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FilterParserThread::~FilterParserThread()
|
||||||
|
{
|
||||||
|
m_abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for eMule ip filter in DAT format
|
||||||
|
int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||||
|
{
|
||||||
|
int ruleCount = 0;
|
||||||
|
QFile file(m_filePath);
|
||||||
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int nbLine = 0;
|
||||||
|
while (!file.atEnd() && !m_abort) {
|
||||||
|
++nbLine;
|
||||||
|
QByteArray line = file.readLine();
|
||||||
|
// Ignoring empty lines
|
||||||
|
line = line.trimmed();
|
||||||
|
if (line.isEmpty()) continue;
|
||||||
|
// Ignoring commented lines
|
||||||
|
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||||
|
|
||||||
|
// Line should be splitted by commas
|
||||||
|
QList<QByteArray> partsList = line.split(',');
|
||||||
|
const uint nbElem = partsList.size();
|
||||||
|
|
||||||
|
// IP Range should be splitted by a dash
|
||||||
|
QList<QByteArray> IPs = partsList.first().split('-');
|
||||||
|
if (IPs.size() != 2) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Line was %s", line.constData());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
const QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||||
|
if (strStartIP.isEmpty()) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec);
|
||||||
|
if (ec) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||||
|
if (strEndIP.isEmpty()) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec);
|
||||||
|
if (ec) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
qDebug("One IP is IPv4 and the other is IPv6!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there is an access value (apparently not mandatory)
|
||||||
|
int nbAccess = 0;
|
||||||
|
if (nbElem > 1) {
|
||||||
|
// There is possibly one
|
||||||
|
nbAccess = partsList.at(1).trimmed().toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbAccess > 127) {
|
||||||
|
// Ignoring this rule because access value is too high
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now Add to the filter
|
||||||
|
try {
|
||||||
|
filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked);
|
||||||
|
++ruleCount;
|
||||||
|
}
|
||||||
|
catch(std::exception &) {
|
||||||
|
qDebug("Bad line in filter file, avoided crash...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
|
int FilterParserThread::parseP2PFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||||
|
{
|
||||||
|
int ruleCount = 0;
|
||||||
|
QFile file(m_filePath);
|
||||||
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int nbLine = 0;
|
||||||
|
while (!file.atEnd() && !m_abort) {
|
||||||
|
++nbLine;
|
||||||
|
QByteArray line = file.readLine().trimmed();
|
||||||
|
if (line.isEmpty()) continue;
|
||||||
|
// Ignoring commented lines
|
||||||
|
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||||
|
|
||||||
|
// Line is splitted by :
|
||||||
|
QList<QByteArray> partsList = line.split(':');
|
||||||
|
if (partsList.size() < 2) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IP range
|
||||||
|
QList<QByteArray> IPs = partsList.last().split('-');
|
||||||
|
if (IPs.size() != 2) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("line was: %s", line.constData());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||||
|
if (strStartIP.isEmpty()) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec);
|
||||||
|
if (ec) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||||
|
if (strEndIP.isEmpty()) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec);
|
||||||
|
if (ec) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Line was: %s", line.constData());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked);
|
||||||
|
++ruleCount;
|
||||||
|
}
|
||||||
|
catch(std::exception &) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
qDebug("Line was: %s", line.constData());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int total_read = 0;
|
||||||
|
int read;
|
||||||
|
do {
|
||||||
|
read = stream.readRawData(&c, 1);
|
||||||
|
total_read += read;
|
||||||
|
if (read > 0) {
|
||||||
|
if (c != delim) {
|
||||||
|
name += c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Delim found
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(read > 0);
|
||||||
|
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
|
int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||||
|
{
|
||||||
|
int ruleCount = 0;
|
||||||
|
QFile file(m_filePath);
|
||||||
|
if (!file.exists()) return ruleCount;
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream stream(&file);
|
||||||
|
// Read header
|
||||||
|
char buf[7];
|
||||||
|
unsigned char version;
|
||||||
|
if (!stream.readRawData(buf, sizeof(buf))
|
||||||
|
|| memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7)
|
||||||
|
|| !stream.readRawData((char*)&version, sizeof(version))) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((version == 1) || (version == 2)) {
|
||||||
|
qDebug ("p2b version 1 or 2");
|
||||||
|
unsigned int start, end;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
while(getlineInStream(stream, name, '\0') && !m_abort) {
|
||||||
|
if (!stream.readRawData((char*)&start, sizeof(start))
|
||||||
|
|| !stream.readRawData((char*)&end, sizeof(end))) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network byte order to Host byte order
|
||||||
|
// asio address_v4 contructor expects it
|
||||||
|
// that way
|
||||||
|
libt::address_v4 first(ntohl(start));
|
||||||
|
libt::address_v4 last(ntohl(end));
|
||||||
|
// Apply to bittorrent session
|
||||||
|
try {
|
||||||
|
filter.add_rule(first, last, libt::ip_filter::blocked);
|
||||||
|
++ruleCount;
|
||||||
|
}
|
||||||
|
catch(std::exception &) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (version == 3) {
|
||||||
|
qDebug ("p2b version 3");
|
||||||
|
unsigned int namecount;
|
||||||
|
if (!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
namecount = ntohl(namecount);
|
||||||
|
// Reading names although, we don't really care about them
|
||||||
|
for (unsigned int i = 0; i < namecount; ++i) {
|
||||||
|
std::string name;
|
||||||
|
if (!getlineInStream(stream, name, '\0')) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_abort) return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading the ranges
|
||||||
|
unsigned int rangecount;
|
||||||
|
if (!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
rangecount = ntohl(rangecount);
|
||||||
|
unsigned int name, start, end;
|
||||||
|
for (unsigned int i = 0; i < rangecount; ++i) {
|
||||||
|
if (!stream.readRawData((char*)&name, sizeof(name))
|
||||||
|
|| !stream.readRawData((char*)&start, sizeof(start))
|
||||||
|
|| !stream.readRawData((char*)&end, sizeof(end))) {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network byte order to Host byte order
|
||||||
|
// asio address_v4 contructor expects it
|
||||||
|
// that way
|
||||||
|
libt::address_v4 first(ntohl(start));
|
||||||
|
libt::address_v4 last(ntohl(end));
|
||||||
|
// Apply to bittorrent session
|
||||||
|
try {
|
||||||
|
filter.add_rule(first, last, libt::ip_filter::blocked);
|
||||||
|
++ruleCount;
|
||||||
|
}
|
||||||
|
catch(std::exception &) {}
|
||||||
|
|
||||||
|
if (m_abort) return ruleCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
return ruleCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process ip filter file
|
||||||
|
// Supported formats:
|
||||||
|
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
||||||
|
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
||||||
|
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
|
||||||
|
void FilterParserThread::processFilterFile(QString _filePath)
|
||||||
|
{
|
||||||
|
if (isRunning()) {
|
||||||
|
// Already parsing a filter, m_abort first
|
||||||
|
m_abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_abort = false;
|
||||||
|
m_filePath = _filePath;
|
||||||
|
// Run it
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilterParserThread::processFilterList(libt::session *s, const QStringList &IPs)
|
||||||
|
{
|
||||||
|
// First, import current filter
|
||||||
|
libt::ip_filter filter = s->get_ip_filter();
|
||||||
|
foreach (const QString &ip, IPs) {
|
||||||
|
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
|
||||||
|
boost::system::error_code ec;
|
||||||
|
libt::address addr = libt::address::from_string(ip.toLocal8Bit().constData(), ec);
|
||||||
|
Q_ASSERT(!ec);
|
||||||
|
if (!ec)
|
||||||
|
filter.add_rule(addr, addr, libt::ip_filter::blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->set_ip_filter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FilterParserThread::cleanupIPAddress(QString _ip)
|
||||||
|
{
|
||||||
|
QHostAddress ip(_ip.trimmed());
|
||||||
|
if (ip.isNull()) return QString();
|
||||||
|
|
||||||
|
return ip.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilterParserThread::run()
|
||||||
|
{
|
||||||
|
qDebug("Processing filter file");
|
||||||
|
libt::ip_filter filter = m_session->get_ip_filter();
|
||||||
|
int ruleCount = 0;
|
||||||
|
if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
||||||
|
// PeerGuardian p2p file
|
||||||
|
ruleCount = parseP2PFilterFile(m_filePath, filter);
|
||||||
|
}
|
||||||
|
else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
|
||||||
|
// PeerGuardian p2b file
|
||||||
|
ruleCount = parseP2BFilterFile(m_filePath, filter);
|
||||||
|
}
|
||||||
|
else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive)) {
|
||||||
|
// eMule DAT format
|
||||||
|
ruleCount = parseDATFilterFile(m_filePath, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_abort) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_session->set_ip_filter(filter);
|
||||||
|
emit IPFilterParsed(ruleCount);
|
||||||
|
}
|
||||||
|
catch(std::exception &) {
|
||||||
|
emit IPFilterError();
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug("IP Filter thread: finished parsing, filter applied");
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -32,38 +32,30 @@
|
|||||||
#define FILTERPARSERTHREAD_H
|
#define FILTERPARSERTHREAD_H
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QDataStream>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
namespace libtorrent {
|
class QDataStream;
|
||||||
class session;
|
class QStringList;
|
||||||
struct ip_filter;
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class session;
|
||||||
|
struct ip_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace std;
|
class FilterParserThread : public QThread
|
||||||
|
{
|
||||||
// P2B Stuff
|
|
||||||
#include <string.h>
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <Winsock2.h>
|
|
||||||
#else
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#endif
|
|
||||||
// End of P2B stuff
|
|
||||||
|
|
||||||
class FilterParserThread : public QThread {
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FilterParserThread(QObject* parent, libtorrent::session *s);
|
FilterParserThread(libtorrent::session *s, QObject *parent = 0);
|
||||||
~FilterParserThread();
|
~FilterParserThread();
|
||||||
|
|
||||||
int parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
int parseDATFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||||
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||||
int getlineInStream(QDataStream& stream, string& name, char delim);
|
int getlineInStream(QDataStream &stream, std::string &name, char delim);
|
||||||
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||||
void processFilterFile(QString _filePath);
|
void processFilterFile(QString _filePath);
|
||||||
static void processFilterList(libtorrent::session *s, const QStringList& IPs);
|
static void processFilterList(libtorrent::session *s, const QStringList &IPs);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void IPFilterParsed(int ruleCount);
|
void IPFilterParsed(int ruleCount);
|
||||||
@@ -74,9 +66,9 @@ protected:
|
|||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
libtorrent::session *s;
|
libtorrent::session *m_session;
|
||||||
bool abort;
|
bool m_abort;
|
||||||
QString filePath;
|
QString m_filePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // BITTORRENT_FILTERPARSERTHREAD_H
|
||||||
83
src/core/bittorrent/private/sessionprivate.h
Normal file
83
src/core/bittorrent/private/sessionprivate.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SESSIONPRIVATE_H
|
||||||
|
#define SESSIONPRIVATE_H
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QUrl;
|
||||||
|
template<typename T> class QList;
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TorrentHandle;
|
||||||
|
class TrackerEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SessionPrivate
|
||||||
|
{
|
||||||
|
virtual bool isQueueingEnabled() const = 0;
|
||||||
|
virtual bool isTempPathEnabled() const = 0;
|
||||||
|
virtual bool isAppendExtensionEnabled() const = 0;
|
||||||
|
virtual bool useAppendLabelToSavePath() const = 0;
|
||||||
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
|
virtual bool isResolveCountriesEnabled() const = 0;
|
||||||
|
#endif
|
||||||
|
virtual QString defaultSavePath() const = 0;
|
||||||
|
virtual QString tempPath() const = 0;
|
||||||
|
virtual qreal globalMaxRatio() const = 0;
|
||||||
|
|
||||||
|
virtual void handleTorrentRatioLimitChanged(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentSavePathChanged(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentMetadataReceived(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentPaused(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentResumed(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentChecked(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentFinished(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentTrackersAdded(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &newTrackers) = 0;
|
||||||
|
virtual void handleTorrentTrackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &deletedTrackers) = 0;
|
||||||
|
virtual void handleTorrentTrackersChanged(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentUrlSeedsAdded(BitTorrent::TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds) = 0;
|
||||||
|
virtual void handleTorrentUrlSeedsRemoved(BitTorrent::TorrentHandle *const torrent, const QList<QUrl> &urlSeeds) = 0;
|
||||||
|
virtual void handleTorrentResumeDataReady(BitTorrent::TorrentHandle *const torrent, const libtorrent::entry &data) = 0;
|
||||||
|
virtual void handleTorrentResumeDataFailed(BitTorrent::TorrentHandle *const torrent) = 0;
|
||||||
|
virtual void handleTorrentTrackerReply(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0;
|
||||||
|
virtual void handleTorrentTrackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0;
|
||||||
|
virtual void handleTorrentTrackerError(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0;
|
||||||
|
virtual void handleTorrentTrackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~SessionPrivate() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SESSIONPRIVATE_H
|
||||||
56
src/core/bittorrent/private/speedmonitor.cpp
Normal file
56
src/core/bittorrent/private/speedmonitor.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include "speedmonitor.h"
|
||||||
|
|
||||||
|
void SpeedMonitor::addSample(const SpeedSample &sample)
|
||||||
|
{
|
||||||
|
m_speedSamples.push_back(sample);
|
||||||
|
m_sum += sample;
|
||||||
|
if (m_speedSamples.size() > MAX_SAMPLES) {
|
||||||
|
m_sum -= m_speedSamples.front();
|
||||||
|
m_speedSamples.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpeedSampleAvg SpeedMonitor::average() const
|
||||||
|
{
|
||||||
|
if (m_speedSamples.empty())
|
||||||
|
return SpeedSampleAvg();
|
||||||
|
|
||||||
|
qreal k = qreal(1.) / m_speedSamples.size();
|
||||||
|
return SpeedSampleAvg(m_sum.download * k, m_sum.upload * k);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeedMonitor::reset()
|
||||||
|
{
|
||||||
|
m_sum = SpeedSample();
|
||||||
|
m_speedSamples.clear();
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,42 +27,58 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef QTORRENTFILTER_H
|
#ifndef SPEEDMONITOR_H
|
||||||
#define QTORRENTFILTER_H
|
#define SPEEDMONITOR_H
|
||||||
|
|
||||||
#include "qtorrenthandle.h"
|
template<typename T> class QList;
|
||||||
|
|
||||||
class QTorrentFilter
|
template<typename T>
|
||||||
|
struct Sample
|
||||||
{
|
{
|
||||||
public:
|
Sample()
|
||||||
enum
|
: download()
|
||||||
|
, upload()
|
||||||
{
|
{
|
||||||
All,
|
}
|
||||||
Downloading,
|
|
||||||
Seeding,
|
|
||||||
Completed,
|
|
||||||
Paused,
|
|
||||||
Resumed,
|
|
||||||
Active,
|
|
||||||
Inactive
|
|
||||||
};
|
|
||||||
|
|
||||||
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
Sample(T dl, T ul)
|
||||||
QTorrentFilter(QString filter, QString label = QString());
|
: download(dl)
|
||||||
bool apply(const QTorrentHandle& h) const;
|
, upload(ul)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
Sample<T> &operator+=(const Sample<T> &other)
|
||||||
int type_;
|
{
|
||||||
QString label_;
|
download += other.download;
|
||||||
|
upload += other.upload;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
bool isTorrentDownloading(const QTorrentHandle &h) const;
|
Sample<T> &operator-=(const Sample<T> &other)
|
||||||
bool isTorrentSeeding(const QTorrentHandle &h) const;
|
{
|
||||||
bool isTorrentCompleted(const QTorrentHandle &h) const;
|
download -= other.download;
|
||||||
bool isTorrentPaused(const QTorrentHandle &h) const;
|
upload -= other.upload;
|
||||||
bool isTorrentResumed(const QTorrentHandle &h) const;
|
return *this;
|
||||||
bool isTorrentActive(const QTorrentHandle &h) const;
|
}
|
||||||
bool isTorrentInactive(const QTorrentHandle &h) const;
|
|
||||||
bool torrentHasLabel(const QTorrentHandle &h) const;
|
T download;
|
||||||
|
T upload;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QTORRENTFILTER_H
|
typedef Sample<qlonglong> SpeedSample;
|
||||||
|
typedef Sample<qreal> SpeedSampleAvg;
|
||||||
|
|
||||||
|
class SpeedMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void addSample(const SpeedSample &sample);
|
||||||
|
SpeedSampleAvg average() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int MAX_SAMPLES = 30;
|
||||||
|
QList<SpeedSample> m_speedSamples;
|
||||||
|
SpeedSample m_sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPEEDMONITOR_H
|
||||||
@@ -1,67 +1,81 @@
|
|||||||
#include "torrentstatistics.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
|
|
||||||
#include "qbtsession.h"
|
#include "core/qinisettings.h"
|
||||||
#include "qinisettings.h"
|
#include "core/preferences.h"
|
||||||
#include "preferences.h"
|
#include "core/bittorrent/sessionstatus.h"
|
||||||
|
#include "core/bittorrent/session.h"
|
||||||
|
#include "statistics.h"
|
||||||
|
|
||||||
TorrentStatistics::TorrentStatistics(QBtSession* session, QObject* parent)
|
static const qint64 SAVE_INTERVAL = 15 * 60 * 1000;
|
||||||
: QObject(parent)
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
Statistics::Statistics(Session *session)
|
||||||
|
: QObject(session)
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_sessionUL(0)
|
, m_sessionUL(0)
|
||||||
, m_sessionDL(0)
|
, m_sessionDL(0)
|
||||||
, m_lastWrite(0)
|
, m_lastWrite(0)
|
||||||
, m_dirty(false)
|
, m_dirty(false)
|
||||||
{
|
{
|
||||||
loadStats();
|
load();
|
||||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gatherStats()));
|
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
|
||||||
m_timer.start(60 * 1000);
|
m_timer.start(60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentStatistics::~TorrentStatistics() {
|
Statistics::~Statistics()
|
||||||
|
{
|
||||||
if (m_dirty)
|
if (m_dirty)
|
||||||
m_lastWrite = 0;
|
m_lastWrite = 0;
|
||||||
saveStats();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 TorrentStatistics::getAlltimeDL() const {
|
quint64 Statistics::getAlltimeDL() const
|
||||||
|
{
|
||||||
return m_alltimeDL + m_sessionDL;
|
return m_alltimeDL + m_sessionDL;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint64 TorrentStatistics::getAlltimeUL() const {
|
quint64 Statistics::getAlltimeUL() const
|
||||||
|
{
|
||||||
return m_alltimeUL + m_sessionUL;
|
return m_alltimeUL + m_sessionUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentStatistics::gatherStats() {
|
void Statistics::gather()
|
||||||
libtorrent::session_status ss = m_session->getSessionStatus();
|
{
|
||||||
if (ss.total_download > m_sessionDL) {
|
SessionStatus ss = m_session->status();
|
||||||
m_sessionDL = ss.total_download;
|
if (ss.totalDownload() > m_sessionDL) {
|
||||||
|
m_sessionDL = ss.totalDownload();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
if (ss.total_upload > m_sessionUL) {
|
if (ss.totalUpload() > m_sessionUL) {
|
||||||
m_sessionUL = ss.total_upload;
|
m_sessionUL = ss.totalUpload();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStats();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentStatistics::saveStats() const {
|
void Statistics::save() const
|
||||||
if (!(m_dirty && (QDateTime::currentMSecsSinceEpoch() - m_lastWrite >= 15*60*1000) ))
|
{
|
||||||
|
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
|
if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||||
QVariantHash v;
|
QVariantHash v;
|
||||||
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL);
|
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL);
|
||||||
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL);
|
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL);
|
||||||
s.setValue("Stats/AllStats", v);
|
s.setValue("Stats/AllStats", v);
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
m_lastWrite = QDateTime::currentMSecsSinceEpoch();
|
m_lastWrite = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentStatistics::loadStats() {
|
void Statistics::load()
|
||||||
|
{
|
||||||
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
||||||
// This code reads the data from there, writes it to the new file, and removes the keys
|
// This code reads the data from there, writes it to the new file, and removes the keys
|
||||||
// from the old file. This code should be removed after some time has passed.
|
// from the old file. This code should be removed after some time has passed.
|
||||||
@@ -69,7 +83,7 @@ void TorrentStatistics::loadStats() {
|
|||||||
// Don't forget to remove:
|
// Don't forget to remove:
|
||||||
// 1. Preferences::getStats()
|
// 1. Preferences::getStats()
|
||||||
// 2. Preferences::removeStats()
|
// 2. Preferences::removeStats()
|
||||||
// 3. #include "preferences.h"
|
// 3. #include "core/preferences.h"
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences* const pref = Preferences::instance();
|
||||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||||
QVariantHash v = pref->getStats();
|
QVariantHash v = pref->getStats();
|
||||||
@@ -87,14 +101,15 @@ void TorrentStatistics::loadStats() {
|
|||||||
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
|
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
v = s.value("Stats/AllStats").toHash();
|
v = s.value("Stats/AllStats").toHash();
|
||||||
|
}
|
||||||
|
|
||||||
m_alltimeDL = v["AlltimeDL"].toULongLong();
|
m_alltimeDL = v["AlltimeDL"].toULongLong();
|
||||||
m_alltimeUL = v["AlltimeUL"].toULongLong();
|
m_alltimeUL = v["AlltimeUL"].toULongLong();
|
||||||
|
|
||||||
if (m_dirty) {
|
if (m_dirty) {
|
||||||
saveStats();
|
save();
|
||||||
pref->removeStats();
|
pref->removeStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,32 @@
|
|||||||
#ifndef TORRENTSTATISTICS_H
|
#ifndef STATISTICS_H
|
||||||
#define TORRENTSTATISTICS_H
|
#define STATISTICS_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
class QBtSession;
|
namespace BitTorrent { class Session; }
|
||||||
|
|
||||||
class TorrentStatistics : QObject
|
class Statistics : QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(TorrentStatistics)
|
Q_DISABLE_COPY(Statistics)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentStatistics(QBtSession* session, QObject* parent = 0);
|
Statistics(BitTorrent::Session *session);
|
||||||
~TorrentStatistics();
|
~Statistics();
|
||||||
|
|
||||||
quint64 getAlltimeDL() const;
|
quint64 getAlltimeDL() const;
|
||||||
quint64 getAlltimeUL() const;
|
quint64 getAlltimeUL() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void gatherStats();
|
void gather();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void saveStats() const;
|
void save() const;
|
||||||
void loadStats();
|
void load();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QBtSession* m_session;
|
BitTorrent::Session *m_session;
|
||||||
// Will overflow at 15.9 EiB
|
// Will overflow at 15.9 EiB
|
||||||
quint64 m_alltimeUL;
|
quint64 m_alltimeUL;
|
||||||
quint64 m_alltimeDL;
|
quint64 m_alltimeDL;
|
||||||
@@ -38,4 +38,4 @@ private:
|
|||||||
QTimer m_timer;
|
QTimer m_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TORRENTSTATISTICS_H
|
#endif // STATISTICS_H
|
||||||
53
src/core/bittorrent/private/torrenthandleprivate.h
Normal file
53
src/core/bittorrent/private/torrenthandleprivate.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TORRENTHANDLEPRIVATE_H
|
||||||
|
#define TORRENTHANDLEPRIVATE_H
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class alert;
|
||||||
|
struct torrent_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TorrentHandlePrivate
|
||||||
|
{
|
||||||
|
virtual void handleAlert(libtorrent::alert *) = 0;
|
||||||
|
virtual void handleStateUpdate(const libtorrent::torrent_status &) = 0;
|
||||||
|
virtual void handleDefaultSavePathChanged() = 0;
|
||||||
|
virtual void handleTempPathChanged() = 0;
|
||||||
|
virtual void handleAppendExtensionToggled() = 0;
|
||||||
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
|
virtual void handleResolveCountriesToggled() = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~TorrentHandlePrivate() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TORRENTHANDLEPRIVATE_H
|
||||||
2440
src/core/bittorrent/session.cpp
Normal file
2440
src/core/bittorrent/session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
365
src/core/bittorrent/session.h
Normal file
365
src/core/bittorrent/session.h
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_SESSION_H
|
||||||
|
#define BITTORRENT_SESSION_H
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
|
#include "core/tristatebool.h"
|
||||||
|
#include "core/types.h"
|
||||||
|
#include "private/sessionprivate.h"
|
||||||
|
#include "torrentinfo.h"
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class session;
|
||||||
|
struct add_torrent_params;
|
||||||
|
struct pe_settings;
|
||||||
|
struct proxy_settings;
|
||||||
|
struct session_settings;
|
||||||
|
struct session_status;
|
||||||
|
|
||||||
|
class alert;
|
||||||
|
struct torrent_alert;
|
||||||
|
struct state_update_alert;
|
||||||
|
struct stats_alert;
|
||||||
|
struct add_torrent_alert;
|
||||||
|
struct torrent_checked_alert;
|
||||||
|
struct torrent_finished_alert;
|
||||||
|
struct torrent_removed_alert;
|
||||||
|
struct torrent_deleted_alert;
|
||||||
|
struct torrent_paused_alert;
|
||||||
|
struct torrent_resumed_alert;
|
||||||
|
struct save_resume_data_alert;
|
||||||
|
struct save_resume_data_failed_alert;
|
||||||
|
struct file_renamed_alert;
|
||||||
|
struct storage_moved_alert;
|
||||||
|
struct storage_moved_failed_alert;
|
||||||
|
struct metadata_received_alert;
|
||||||
|
struct file_error_alert;
|
||||||
|
struct file_completed_alert;
|
||||||
|
struct tracker_error_alert;
|
||||||
|
struct tracker_reply_alert;
|
||||||
|
struct tracker_warning_alert;
|
||||||
|
struct portmap_error_alert;
|
||||||
|
struct portmap_alert;
|
||||||
|
struct peer_blocked_alert;
|
||||||
|
struct peer_ban_alert;
|
||||||
|
struct fastresume_rejected_alert;
|
||||||
|
struct url_seed_alert;
|
||||||
|
struct listen_succeeded_alert;
|
||||||
|
struct listen_failed_alert;
|
||||||
|
struct external_ip_alert;
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QTimer;
|
||||||
|
class QStringList;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class FilterParserThread;
|
||||||
|
class BandwidthScheduler;
|
||||||
|
class Statistics;
|
||||||
|
|
||||||
|
typedef QPair<QString, QString> QStringPair;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class InfoHash;
|
||||||
|
class CacheStatus;
|
||||||
|
class SessionStatus;
|
||||||
|
class TorrentHandle;
|
||||||
|
class Tracker;
|
||||||
|
class MagnetUri;
|
||||||
|
class TrackerEntry;
|
||||||
|
struct AddTorrentData;
|
||||||
|
|
||||||
|
struct AddTorrentParams
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString label;
|
||||||
|
QString savePath;
|
||||||
|
bool disableTempPath; // e.g. for imported torrents
|
||||||
|
bool sequential;
|
||||||
|
TriStateBool addPaused;
|
||||||
|
QVector<int> filePriorities; // used if TorrentInfo is set
|
||||||
|
bool ignoreShareRatio;
|
||||||
|
bool skipChecking;
|
||||||
|
|
||||||
|
AddTorrentParams();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TorrentStatusReport
|
||||||
|
{
|
||||||
|
uint nbDownloading;
|
||||||
|
uint nbSeeding;
|
||||||
|
uint nbCompleted;
|
||||||
|
uint nbActive;
|
||||||
|
uint nbInactive;
|
||||||
|
uint nbPaused;
|
||||||
|
uint nbResumed;
|
||||||
|
|
||||||
|
TorrentStatusReport();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Session : public QObject, public SessionPrivate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Session)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void initInstance();
|
||||||
|
static void freeInstance();
|
||||||
|
static Session *instance();
|
||||||
|
|
||||||
|
bool isDHTEnabled() const;
|
||||||
|
bool isLSDEnabled() const;
|
||||||
|
bool isPexEnabled() const;
|
||||||
|
bool isQueueingEnabled() const;
|
||||||
|
qreal globalMaxRatio() const;
|
||||||
|
|
||||||
|
TorrentHandle *findTorrent(const InfoHash &hash) const;
|
||||||
|
QHash<InfoHash, TorrentHandle *> torrents() const;
|
||||||
|
bool hasActiveTorrents() const;
|
||||||
|
bool hasUnfinishedTorrents() const;
|
||||||
|
SessionStatus status() const;
|
||||||
|
CacheStatus cacheStatus() const;
|
||||||
|
quint64 getAlltimeDL() const;
|
||||||
|
quint64 getAlltimeUL() const;
|
||||||
|
int downloadRateLimit() const;
|
||||||
|
int uploadRateLimit() const;
|
||||||
|
bool isListening() const;
|
||||||
|
|
||||||
|
void changeSpeedLimitMode(bool alternative);
|
||||||
|
void setDownloadRateLimit(int rate);
|
||||||
|
void setUploadRateLimit(int rate);
|
||||||
|
void setGlobalMaxRatio(qreal ratio);
|
||||||
|
void enableIPFilter(const QString &filterPath, bool force = false);
|
||||||
|
void disableIPFilter();
|
||||||
|
void banIP(const QString &ip);
|
||||||
|
|
||||||
|
bool isKnownTorrent(const InfoHash &hash) const;
|
||||||
|
bool addTorrent(QString source, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||||
|
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||||
|
bool deleteTorrent(const QString &hash, bool deleteLocalFiles = false);
|
||||||
|
bool loadMetadata(const QString &magnetUri);
|
||||||
|
bool cancelLoadMetadata(const InfoHash &hash);
|
||||||
|
|
||||||
|
void recursiveTorrentDownload(const InfoHash &hash);
|
||||||
|
void increaseTorrentsPriority(const QStringList &hashes);
|
||||||
|
void decreaseTorrentsPriority(const QStringList &hashes);
|
||||||
|
void topTorrentsPriority(const QStringList &hashes);
|
||||||
|
void bottomTorrentsPriority(const QStringList &hashes);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void torrentsUpdated(const BitTorrent::TorrentStatusReport &torrentStatusReport = BitTorrent::TorrentStatusReport());
|
||||||
|
void addTorrentFailed(const QString &error);
|
||||||
|
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentStatusUpdated(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentPaused(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentResumed(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void allTorrentsFinished();
|
||||||
|
void metadataLoaded(const BitTorrent::TorrentInfo &info);
|
||||||
|
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg);
|
||||||
|
void trackerSuccess(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||||
|
void trackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||||
|
void trackerError(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||||
|
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void speedLimitModeChanged(bool alternative);
|
||||||
|
void ipFilterParsed(bool error, int ruleCount);
|
||||||
|
void trackersAdded(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
|
||||||
|
void trackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
|
||||||
|
void trackersChanged(BitTorrent::TorrentHandle *const torrent);
|
||||||
|
void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless);
|
||||||
|
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
||||||
|
void downloadFromUrlFinished(const QString &url);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void configure();
|
||||||
|
void readAlerts();
|
||||||
|
void refresh();
|
||||||
|
void processBigRatios();
|
||||||
|
void generateResumeData(bool final = false);
|
||||||
|
void handleIPFilterParsed(int ruleCount);
|
||||||
|
void handleIPFilterError();
|
||||||
|
void handleDownloadFinished(const QString &url, const QString &filePath);
|
||||||
|
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||||
|
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit Session(QObject *parent = 0);
|
||||||
|
~Session();
|
||||||
|
|
||||||
|
bool hasPerTorrentRatioLimit() const;
|
||||||
|
|
||||||
|
void initResumeFolder();
|
||||||
|
void loadState();
|
||||||
|
void saveState();
|
||||||
|
|
||||||
|
// Session configuration
|
||||||
|
void setSessionSettings();
|
||||||
|
void setProxySettings(libtorrent::proxy_settings proxySettings);
|
||||||
|
void adjustLimits();
|
||||||
|
void adjustLimits(libtorrent::session_settings &sessionSettings);
|
||||||
|
void setListeningPort(int port);
|
||||||
|
void setDefaultSavePath(const QString &path);
|
||||||
|
void setDefaultTempPath(const QString &path = QString());
|
||||||
|
void preAllocateAllFiles(bool b);
|
||||||
|
void setMaxConnectionsPerTorrent(int max);
|
||||||
|
void setMaxUploadsPerTorrent(int max);
|
||||||
|
void enableLSD(bool enable);
|
||||||
|
void enableDHT(bool enable);
|
||||||
|
|
||||||
|
void setAppendLabelToSavePath(bool append);
|
||||||
|
void setAppendExtension(bool append);
|
||||||
|
|
||||||
|
void startUpTorrents();
|
||||||
|
bool addTorrent_impl(const AddTorrentData &addData, const MagnetUri &magnetUri,
|
||||||
|
const TorrentInfo &torrentInfo = TorrentInfo(),
|
||||||
|
const QByteArray &fastresumeData = QByteArray());
|
||||||
|
|
||||||
|
void updateRatioTimer();
|
||||||
|
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
||||||
|
void exportTorrentFiles(QString path);
|
||||||
|
void saveTorrentResumeData(TorrentHandle *const torrent);
|
||||||
|
|
||||||
|
void handleAlert(libtorrent::alert *a);
|
||||||
|
void dispatchTorrentAlert(libtorrent::alert *a);
|
||||||
|
void handleAddTorrentAlert(libtorrent::add_torrent_alert *p);
|
||||||
|
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
|
||||||
|
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
||||||
|
void handleFileErrorAlert(libtorrent::file_error_alert *p);
|
||||||
|
void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p);
|
||||||
|
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p);
|
||||||
|
void handlePortmapWarningAlert(libtorrent::portmap_error_alert *p);
|
||||||
|
void handlePortmapAlert(libtorrent::portmap_alert *p);
|
||||||
|
void handlePeerBlockedAlert(libtorrent::peer_blocked_alert *p);
|
||||||
|
void handlePeerBanAlert(libtorrent::peer_ban_alert *p);
|
||||||
|
void handleUrlSeedAlert(libtorrent::url_seed_alert *p);
|
||||||
|
void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p);
|
||||||
|
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
|
||||||
|
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
|
||||||
|
|
||||||
|
bool isTempPathEnabled() const;
|
||||||
|
bool isAppendExtensionEnabled() const;
|
||||||
|
bool useAppendLabelToSavePath() const;
|
||||||
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
|
bool isResolveCountriesEnabled() const;
|
||||||
|
#endif
|
||||||
|
QString defaultSavePath() const;
|
||||||
|
QString tempPath() const;
|
||||||
|
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentPaused(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentResumed(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentChecked(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentFinished(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers);
|
||||||
|
void handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList<TrackerEntry> &deletedTrackers);
|
||||||
|
void handleTorrentTrackersChanged(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds);
|
||||||
|
void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList<QUrl> &urlSeeds);
|
||||||
|
void handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data);
|
||||||
|
void handleTorrentResumeDataFailed(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||||
|
void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||||
|
void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||||
|
void handleTorrentTrackerAuthenticationRequired(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||||
|
|
||||||
|
void saveResumeData();
|
||||||
|
bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data);
|
||||||
|
|
||||||
|
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
|
||||||
|
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
|
||||||
|
|
||||||
|
// BitTorrent
|
||||||
|
libtorrent::session *m_nativeSession;
|
||||||
|
|
||||||
|
bool m_LSDEnabled;
|
||||||
|
bool m_DHTEnabled;
|
||||||
|
bool m_PeXEnabled;
|
||||||
|
bool m_queueingEnabled;
|
||||||
|
bool m_torrentExportEnabled;
|
||||||
|
bool m_finishedTorrentExportEnabled;
|
||||||
|
bool m_preAllocateAll;
|
||||||
|
qreal m_globalMaxRatio;
|
||||||
|
int m_numResumeData;
|
||||||
|
int m_extraLimit;
|
||||||
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
|
bool m_geoipDBLoaded;
|
||||||
|
bool m_resolveCountries;
|
||||||
|
#endif
|
||||||
|
bool m_appendLabelToSavePath;
|
||||||
|
bool m_appendExtension;
|
||||||
|
uint m_refreshInterval;
|
||||||
|
MaxRatioAction m_highRatioAction;
|
||||||
|
QString m_defaultSavePath;
|
||||||
|
QString m_tempPath;
|
||||||
|
QString m_filterPath;
|
||||||
|
QString m_resumeFolderPath;
|
||||||
|
QFile m_resumeFolderLock;
|
||||||
|
QHash<InfoHash, QString> m_savePathsToRemove;
|
||||||
|
|
||||||
|
QTimer *m_refreshTimer;
|
||||||
|
QTimer *m_bigRatioTimer;
|
||||||
|
QTimer *m_resumeDataTimer;
|
||||||
|
Statistics *m_statistics;
|
||||||
|
// IP filtering
|
||||||
|
QPointer<FilterParserThread> m_filterParser;
|
||||||
|
QPointer<BandwidthScheduler> m_bwScheduler;
|
||||||
|
// Tracker
|
||||||
|
QPointer<Tracker> m_tracker;
|
||||||
|
|
||||||
|
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
||||||
|
QHash<InfoHash, TorrentHandle *> m_torrents;
|
||||||
|
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
||||||
|
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||||
|
|
||||||
|
QMutex m_alertsMutex;
|
||||||
|
QWaitCondition m_alertsWaitCondition;
|
||||||
|
QVector<libtorrent::alert *> m_alerts;
|
||||||
|
|
||||||
|
static Session *m_instance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_SESSION_H
|
||||||
96
src/core/bittorrent/sessionstatus.cpp
Normal file
96
src/core/bittorrent/sessionstatus.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sessionstatus.h"
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
SessionStatus::SessionStatus(const libtorrent::session_status &nativeStatus)
|
||||||
|
: m_nativeStatus(nativeStatus)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SessionStatus::hasIncomingConnections() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.has_incoming_connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::payloadDownloadRate() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.payload_download_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::payloadUploadRate() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.payload_upload_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong SessionStatus::totalDownload() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.total_download;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong SessionStatus::totalUpload() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.total_upload;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong SessionStatus::totalPayloadDownload() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.total_payload_download;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong SessionStatus::totalPayloadUpload() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.total_payload_upload;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong SessionStatus::totalWasted() const
|
||||||
|
{
|
||||||
|
return (m_nativeStatus.total_redundant_bytes + m_nativeStatus.total_failed_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::diskReadQueue() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.disk_read_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::diskWriteQueue() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.disk_write_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::dhtNodes() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.dht_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionStatus::peersCount() const
|
||||||
|
{
|
||||||
|
return m_nativeStatus.num_peers;
|
||||||
|
}
|
||||||
69
src/core/bittorrent/sessionstatus.h
Normal file
69
src/core/bittorrent/sessionstatus.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_SESSIONSTATUS_H
|
||||||
|
#define BITTORRENT_SESSIONSTATUS_H
|
||||||
|
|
||||||
|
#include <libtorrent/session_status.hpp>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class SessionStatus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SessionStatus(const libtorrent::session_status &nativeStatus);
|
||||||
|
|
||||||
|
bool hasIncomingConnections() const;
|
||||||
|
|
||||||
|
// Return current download rate for the BT
|
||||||
|
// session. Payload means that it only take into
|
||||||
|
// account "useful" part of the rate
|
||||||
|
int payloadDownloadRate() const;
|
||||||
|
|
||||||
|
// Return current upload rate for the BT
|
||||||
|
// session. Payload means that it only take into
|
||||||
|
// account "useful" part of the rate
|
||||||
|
int payloadUploadRate() const;
|
||||||
|
|
||||||
|
qlonglong totalDownload() const;
|
||||||
|
qlonglong totalUpload() const;
|
||||||
|
qlonglong totalPayloadDownload() const;
|
||||||
|
qlonglong totalPayloadUpload() const;
|
||||||
|
qlonglong totalWasted() const;
|
||||||
|
int diskReadQueue() const;
|
||||||
|
int diskWriteQueue() const;
|
||||||
|
int dhtNodes() const;
|
||||||
|
int peersCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
libtorrent::session_status m_nativeStatus;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_SESSIONSTATUS_H
|
||||||
166
src/core/bittorrent/torrentcreatorthread.cpp
Normal file
166
src/core/bittorrent/torrentcreatorthread.cpp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
#include <libtorrent/entry.hpp>
|
||||||
|
#include <libtorrent/bencode.hpp>
|
||||||
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
#include <libtorrent/file.hpp>
|
||||||
|
#include <libtorrent/storage.hpp>
|
||||||
|
#include <libtorrent/hasher.hpp>
|
||||||
|
#include <libtorrent/file_pool.hpp>
|
||||||
|
#include <libtorrent/create_torrent.hpp>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "core/utils/fs.h"
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
#include "core/utils/string.h"
|
||||||
|
#include "torrentcreatorthread.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
// do not include files and folders whose
|
||||||
|
// name starts with a .
|
||||||
|
bool fileFilter(const std::string &f)
|
||||||
|
{
|
||||||
|
return (libt::filename(f)[0] != '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
|
||||||
|
: QThread(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentCreatorThread::~TorrentCreatorThread()
|
||||||
|
{
|
||||||
|
m_abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||||
|
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
|
||||||
|
{
|
||||||
|
m_inputPath = Utils::Fs::fromNativePath(inputPath);
|
||||||
|
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||||
|
if (QFile(m_savePath).exists())
|
||||||
|
Utils::Fs::forceRemove(m_savePath);
|
||||||
|
m_trackers = trackers;
|
||||||
|
m_urlSeeds = urlSeeds;
|
||||||
|
m_comment = comment;
|
||||||
|
m_private = isPrivate;
|
||||||
|
m_pieceSize = pieceSize;
|
||||||
|
m_abort = false;
|
||||||
|
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces)
|
||||||
|
{
|
||||||
|
emit updateProgress(static_cast<int>((numHashes * 100.) / numPieces));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCreatorThread::abortCreation()
|
||||||
|
{
|
||||||
|
m_abort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentCreatorThread::run()
|
||||||
|
{
|
||||||
|
emit updateProgress(0);
|
||||||
|
|
||||||
|
QString creator_str("qBittorrent " VERSION);
|
||||||
|
try {
|
||||||
|
libt::file_storage fs;
|
||||||
|
// Adding files to the torrent
|
||||||
|
libt::add_files(fs, Utils::String::toStdString(Utils::Fs::toNativePath(m_inputPath)), fileFilter);
|
||||||
|
if (m_abort) return;
|
||||||
|
|
||||||
|
libt::create_torrent t(fs, m_pieceSize);
|
||||||
|
|
||||||
|
// Add url seeds
|
||||||
|
foreach (const QString &seed, m_urlSeeds)
|
||||||
|
t.add_url_seed(Utils::String::toStdString(seed.trimmed()));
|
||||||
|
|
||||||
|
int tier = 0;
|
||||||
|
bool newline = false;
|
||||||
|
foreach (const QString &tracker, m_trackers) {
|
||||||
|
if (tracker.isEmpty()) {
|
||||||
|
if (newline)
|
||||||
|
continue;
|
||||||
|
++tier;
|
||||||
|
newline = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
t.add_tracker(Utils::String::toStdString(tracker.trimmed()), tier);
|
||||||
|
newline = false;
|
||||||
|
}
|
||||||
|
if (m_abort) return;
|
||||||
|
|
||||||
|
// calculate the hash for all pieces
|
||||||
|
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/";
|
||||||
|
libt::set_piece_hashes(t, Utils::String::toStdString(Utils::Fs::toNativePath(parentPath)), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces()));
|
||||||
|
// Set qBittorrent as creator and add user comment to
|
||||||
|
// torrent_info structure
|
||||||
|
t.set_creator(creator_str.toUtf8().constData());
|
||||||
|
t.set_comment(m_comment.toUtf8().constData());
|
||||||
|
// Is private ?
|
||||||
|
t.set_priv(m_private);
|
||||||
|
if (m_abort) return;
|
||||||
|
|
||||||
|
// create the torrent and print it to out
|
||||||
|
qDebug("Saving to %s", qPrintable(m_savePath));
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1];
|
||||||
|
int len = Utils::Fs::toNativePath(m_savePath).toWCharArray(savePathW);
|
||||||
|
savePathW[len] = L'\0';
|
||||||
|
std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary);
|
||||||
|
delete[] savePathW;
|
||||||
|
#else
|
||||||
|
std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary);
|
||||||
|
#endif
|
||||||
|
if (outfile.fail())
|
||||||
|
throw std::exception();
|
||||||
|
|
||||||
|
libt::bencode(std::ostream_iterator<char>(outfile), t.generate());
|
||||||
|
outfile.close();
|
||||||
|
|
||||||
|
emit updateProgress(100);
|
||||||
|
emit creationSuccess(m_savePath, parentPath);
|
||||||
|
}
|
||||||
|
catch (std::exception& e) {
|
||||||
|
emit creationFailure(Utils::String::fromStdString(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -28,46 +28,46 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TORRENTCREATORTHREAD_H
|
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
||||||
#define TORRENTCREATORTHREAD_H
|
#define BITTORRENT_TORRENTCREATORTHREAD_H
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QDialog>
|
|
||||||
|
|
||||||
class TorrentCreatorThread : public QThread {
|
namespace BitTorrent
|
||||||
Q_OBJECT
|
{
|
||||||
|
class TorrentCreatorThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TorrentCreatorThread(QDialog *_parent) {
|
TorrentCreatorThread(QObject *parent = 0);
|
||||||
parent = _parent;
|
~TorrentCreatorThread();
|
||||||
}
|
|
||||||
~TorrentCreatorThread() {
|
|
||||||
abort = true;
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
|
|
||||||
void sendProgressSignal(int progress);
|
|
||||||
void abortCreation() { abort = true; }
|
|
||||||
|
|
||||||
protected:
|
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||||
void run();
|
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
|
||||||
|
void abortCreation();
|
||||||
|
|
||||||
signals:
|
protected:
|
||||||
void creationFailure(QString msg);
|
void run();
|
||||||
void creationSuccess(QString path, QString branch_path);
|
|
||||||
void updateProgress(int progress);
|
|
||||||
|
|
||||||
private:
|
signals:
|
||||||
QString input_path;
|
void creationFailure(const QString &msg);
|
||||||
QString save_path;
|
void creationSuccess(const QString &path, const QString &branchPath);
|
||||||
QStringList trackers;
|
void updateProgress(int progress);
|
||||||
QStringList url_seeds;
|
|
||||||
QString comment;
|
|
||||||
bool is_private;
|
|
||||||
int piece_size;
|
|
||||||
bool abort;
|
|
||||||
QDialog *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TORRENTCREATORTHREAD_H
|
private:
|
||||||
|
void sendProgressSignal(int numHashes, int numPieces);
|
||||||
|
|
||||||
|
QString m_inputPath;
|
||||||
|
QString m_savePath;
|
||||||
|
QStringList m_trackers;
|
||||||
|
QStringList m_urlSeeds;
|
||||||
|
QString m_comment;
|
||||||
|
bool m_private;
|
||||||
|
int m_pieceSize;
|
||||||
|
bool m_abort;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_TORRENTCREATORTHREAD_H
|
||||||
1887
src/core/bittorrent/torrenthandle.cpp
Normal file
1887
src/core/bittorrent/torrenthandle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
380
src/core/bittorrent/torrenthandle.h
Normal file
380
src/core/bittorrent/torrenthandle.h
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_TORRENTHANDLE_H
|
||||||
|
#define BITTORRENT_TORRENTHANDLE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include <libtorrent/torrent_handle.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
#include "core/tristatebool.h"
|
||||||
|
#include "private/speedmonitor.h"
|
||||||
|
#include "private/torrenthandleprivate.h"
|
||||||
|
#include "infohash.h"
|
||||||
|
#include "torrentinfo.h"
|
||||||
|
|
||||||
|
class QBitArray;
|
||||||
|
class QStringList;
|
||||||
|
template<typename T, typename U> struct QPair;
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class alert;
|
||||||
|
struct stats_alert;
|
||||||
|
struct torrent_checked_alert;
|
||||||
|
struct torrent_finished_alert;
|
||||||
|
struct torrent_paused_alert;
|
||||||
|
struct torrent_resumed_alert;
|
||||||
|
struct save_resume_data_alert;
|
||||||
|
struct save_resume_data_failed_alert;
|
||||||
|
struct file_renamed_alert;
|
||||||
|
struct storage_moved_alert;
|
||||||
|
struct storage_moved_failed_alert;
|
||||||
|
struct metadata_received_alert;
|
||||||
|
struct file_completed_alert;
|
||||||
|
struct tracker_error_alert;
|
||||||
|
struct tracker_reply_alert;
|
||||||
|
struct tracker_warning_alert;
|
||||||
|
struct fastresume_rejected_alert;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SessionPrivate;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct PeerAddress;
|
||||||
|
class Session;
|
||||||
|
class PeerInfo;
|
||||||
|
class TrackerEntry;
|
||||||
|
struct AddTorrentParams;
|
||||||
|
|
||||||
|
struct AddTorrentData
|
||||||
|
{
|
||||||
|
bool resumed;
|
||||||
|
// for both new and resumed torrents
|
||||||
|
QString name;
|
||||||
|
QString label;
|
||||||
|
QString savePath;
|
||||||
|
bool disableTempPath;
|
||||||
|
bool sequential;
|
||||||
|
bool hasSeedStatus;
|
||||||
|
// for new torrents
|
||||||
|
TriStateBool addPaused;
|
||||||
|
QVector<int> filePriorities;
|
||||||
|
bool ignoreShareRatio;
|
||||||
|
// for resumed torrents
|
||||||
|
QDateTime addedTime;
|
||||||
|
qreal ratioLimit;
|
||||||
|
|
||||||
|
AddTorrentData();
|
||||||
|
AddTorrentData(const AddTorrentParams &in);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrackerInfo
|
||||||
|
{
|
||||||
|
QString lastMessage;
|
||||||
|
quint32 numPeers;
|
||||||
|
|
||||||
|
TrackerInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class TorrentState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
Unknown = -1,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
|
||||||
|
Uploading,
|
||||||
|
PausedUploading,
|
||||||
|
QueuedUploading,
|
||||||
|
StalledUploading,
|
||||||
|
CheckingUploading,
|
||||||
|
ForcedUploading,
|
||||||
|
|
||||||
|
Allocating,
|
||||||
|
|
||||||
|
DownloadingMetadata,
|
||||||
|
Downloading,
|
||||||
|
PausedDownloading,
|
||||||
|
QueuedDownloading,
|
||||||
|
StalledDownloading,
|
||||||
|
CheckingDownloading,
|
||||||
|
ForcedDownloading
|
||||||
|
};
|
||||||
|
|
||||||
|
TorrentState(int value);
|
||||||
|
|
||||||
|
operator int() const;
|
||||||
|
QString toString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TorrentHandle : public QObject, public TorrentHandlePrivate
|
||||||
|
{
|
||||||
|
Q_DISABLE_COPY(TorrentHandle)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const qreal USE_GLOBAL_RATIO;
|
||||||
|
static const qreal NO_RATIO_LIMIT;
|
||||||
|
|
||||||
|
static const qreal MAX_RATIO;
|
||||||
|
|
||||||
|
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||||
|
const AddTorrentData &data);
|
||||||
|
~TorrentHandle();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
InfoHash hash() const;
|
||||||
|
QString name() const;
|
||||||
|
QDateTime creationDate() const;
|
||||||
|
QString creator() const;
|
||||||
|
QString comment() const;
|
||||||
|
bool isPrivate() const;
|
||||||
|
qlonglong totalSize() const;
|
||||||
|
qlonglong wantedSize() const;
|
||||||
|
qlonglong completedSize() const;
|
||||||
|
qlonglong incompletedSize() const;
|
||||||
|
qlonglong pieceLength() const;
|
||||||
|
qlonglong wastedSize() const;
|
||||||
|
QString currentTracker() const;
|
||||||
|
QString actualSavePath() const;
|
||||||
|
QString savePath() const;
|
||||||
|
QString rootPath() const;
|
||||||
|
QString savePathParsed() const;
|
||||||
|
int filesCount() const;
|
||||||
|
int piecesCount() const;
|
||||||
|
qreal progress() const;
|
||||||
|
QString label() const;
|
||||||
|
QDateTime addedTime() const;
|
||||||
|
qreal ratioLimit() const;
|
||||||
|
|
||||||
|
QString firstFileSavePath() const;
|
||||||
|
QString filePath(int index) const;
|
||||||
|
QString fileName(int index) const;
|
||||||
|
qlonglong fileSize(int index) const;
|
||||||
|
QStringList absoluteFilePaths() const;
|
||||||
|
QStringList absoluteFilePathsUnwanted() const;
|
||||||
|
QPair<int, int> fileExtremityPieces(int index) const;
|
||||||
|
QVector<int> filePriorities() const;
|
||||||
|
|
||||||
|
TorrentInfo info() const;
|
||||||
|
bool isSeed() const;
|
||||||
|
bool isPaused() const;
|
||||||
|
bool isResumed() const;
|
||||||
|
bool isQueued() const;
|
||||||
|
bool isForced() const;
|
||||||
|
bool isChecking() const;
|
||||||
|
bool isDownloading() const;
|
||||||
|
bool isUploading() const;
|
||||||
|
bool isCompleted() const;
|
||||||
|
bool isActive() const;
|
||||||
|
bool isInactive() const;
|
||||||
|
bool isSequentialDownload() const;
|
||||||
|
bool hasFirstLastPiecePriority() const;
|
||||||
|
TorrentState state() const;
|
||||||
|
bool hasMetadata() const;
|
||||||
|
bool hasMissingFiles() const;
|
||||||
|
bool hasError() const;
|
||||||
|
bool hasFilteredPieces() const;
|
||||||
|
int queuePosition() const;
|
||||||
|
QList<TrackerEntry> trackers() const;
|
||||||
|
QHash<QString, TrackerInfo> trackerInfos() const;
|
||||||
|
QList<QUrl> urlSeeds() const;
|
||||||
|
QString error() const;
|
||||||
|
qlonglong totalDownload() const;
|
||||||
|
qlonglong totalUpload() const;
|
||||||
|
int activeTime() const;
|
||||||
|
int seedingTime() const;
|
||||||
|
qulonglong eta() const;
|
||||||
|
QVector<qreal> filesProgress() const;
|
||||||
|
int seedsCount() const;
|
||||||
|
int peersCount() const;
|
||||||
|
int leechsCount() const;
|
||||||
|
int completeCount() const;
|
||||||
|
int incompleteCount() const;
|
||||||
|
QDateTime lastSeenComplete() const;
|
||||||
|
QDateTime completedTime() const;
|
||||||
|
int timeSinceUpload() const;
|
||||||
|
int timeSinceDownload() const;
|
||||||
|
int downloadLimit() const;
|
||||||
|
int uploadLimit() const;
|
||||||
|
bool superSeeding() const;
|
||||||
|
QList<PeerInfo> peers() const;
|
||||||
|
QBitArray pieces() const;
|
||||||
|
QBitArray downloadingPieces() const;
|
||||||
|
QVector<int> pieceAvailability() const;
|
||||||
|
qreal distributedCopies() const;
|
||||||
|
qreal maxRatio(bool *usesGlobalRatio = 0) const;
|
||||||
|
qreal realRatio() const;
|
||||||
|
int uploadPayloadRate() const;
|
||||||
|
int downloadPayloadRate() const;
|
||||||
|
int totalPayloadUpload() const;
|
||||||
|
int totalPayloadDownload() const;
|
||||||
|
int connectionsCount() const;
|
||||||
|
int connectionsLimit() const;
|
||||||
|
qlonglong nextAnnounce() const;
|
||||||
|
|
||||||
|
void setName(const QString &name);
|
||||||
|
void setLabel(const QString &label);
|
||||||
|
void setSequentialDownload(bool b);
|
||||||
|
void toggleSequentialDownload();
|
||||||
|
void toggleFirstLastPiecePriority();
|
||||||
|
void setFirstLastPiecePriority(bool b);
|
||||||
|
void pause();
|
||||||
|
void resume(bool forced = false);
|
||||||
|
void move(QString path);
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
void forceReannounce();
|
||||||
|
#else
|
||||||
|
void forceReannounce(int index = -1);
|
||||||
|
#endif
|
||||||
|
void forceDHTAnnounce();
|
||||||
|
void forceRecheck();
|
||||||
|
void setTrackerLogin(const QString &username, const QString &password);
|
||||||
|
void renameFile(int index, const QString &name);
|
||||||
|
bool saveTorrentFile(const QString &path);
|
||||||
|
void prioritizeFiles(const QVector<int> &priorities);
|
||||||
|
void setFilePriority(int index, int priority);
|
||||||
|
void setRatioLimit(qreal limit);
|
||||||
|
void setUploadLimit(int limit);
|
||||||
|
void setDownloadLimit(int limit);
|
||||||
|
void setSuperSeeding(bool enable);
|
||||||
|
void flushCache();
|
||||||
|
void addTrackers(const QList<TrackerEntry> &trackers);
|
||||||
|
void replaceTrackers(QList<TrackerEntry> trackers);
|
||||||
|
void addUrlSeeds(const QList<QUrl> &urlSeeds);
|
||||||
|
void removeUrlSeeds(const QList<QUrl> &urlSeeds);
|
||||||
|
bool connectPeer(const PeerAddress &peerAddress);
|
||||||
|
|
||||||
|
QString toMagnetUri() const;
|
||||||
|
|
||||||
|
bool needSaveResumeData() const;
|
||||||
|
void saveResumeData();
|
||||||
|
|
||||||
|
libtorrent::torrent_handle nativeHandle() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef boost::function<void ()> MoveStorageTrigger;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void updateStatus();
|
||||||
|
void updateStatus(const libtorrent::torrent_status &nativeStatus);
|
||||||
|
void updateState();
|
||||||
|
void updateTorrentInfo();
|
||||||
|
|
||||||
|
void handleAlert(libtorrent::alert *a);
|
||||||
|
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
|
||||||
|
void handleDefaultSavePathChanged();
|
||||||
|
void handleTempPathChanged();
|
||||||
|
void handleAppendExtensionToggled();
|
||||||
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
|
void handleResolveCountriesToggled();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
|
||||||
|
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
|
||||||
|
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p);
|
||||||
|
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p);
|
||||||
|
void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p);
|
||||||
|
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p);
|
||||||
|
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p);
|
||||||
|
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p);
|
||||||
|
void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p);
|
||||||
|
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p);
|
||||||
|
void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p);
|
||||||
|
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p);
|
||||||
|
void handleFileRenamedAlert(libtorrent::file_renamed_alert *p);
|
||||||
|
void handleFileCompletedAlert(libtorrent::file_completed_alert *p);
|
||||||
|
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
||||||
|
void handleStatsAlert(libtorrent::stats_alert *p);
|
||||||
|
|
||||||
|
bool isMoveInProgress() const;
|
||||||
|
bool useTempPath() const;
|
||||||
|
QString nativeActualSavePath() const;
|
||||||
|
|
||||||
|
void resolveCountries(bool b);
|
||||||
|
void adjustSavePath();
|
||||||
|
void adjustActualSavePath();
|
||||||
|
void moveStorage(const QString &newPath);
|
||||||
|
void appendExtensionsToIncompleteFiles();
|
||||||
|
void removeExtensionsFromIncompleteFiles();
|
||||||
|
bool addTracker(const TrackerEntry &tracker);
|
||||||
|
bool addUrlSeed(const QUrl &urlSeed);
|
||||||
|
bool removeUrlSeed(const QUrl &urlSeed);
|
||||||
|
|
||||||
|
SessionPrivate *const m_session;
|
||||||
|
libtorrent::torrent_handle m_nativeHandle;
|
||||||
|
libtorrent::torrent_status m_nativeStatus;
|
||||||
|
TorrentState m_state;
|
||||||
|
TorrentInfo m_torrentInfo;
|
||||||
|
SpeedMonitor m_speedMonitor;
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
QString m_nativeName;
|
||||||
|
QString m_nativeSavePath;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
InfoHash m_hash;
|
||||||
|
|
||||||
|
QString m_oldPath;
|
||||||
|
QString m_newPath;
|
||||||
|
// m_queuedPath is where files should be moved to,
|
||||||
|
// when current moving is completed
|
||||||
|
QString m_queuedPath;
|
||||||
|
QQueue<MoveStorageTrigger> m_moveStorageTriggers;
|
||||||
|
|
||||||
|
// Persistent data
|
||||||
|
QString m_name;
|
||||||
|
QDateTime m_addedTime;
|
||||||
|
QString m_savePath;
|
||||||
|
QString m_label;
|
||||||
|
bool m_hasSeedStatus;
|
||||||
|
qreal m_ratioLimit;
|
||||||
|
bool m_tempPathDisabled;
|
||||||
|
bool m_hasMissingFiles;
|
||||||
|
|
||||||
|
bool m_useDefaultSavePath;
|
||||||
|
bool m_pauseAfterRecheck;
|
||||||
|
bool m_needSaveResumeData;
|
||||||
|
QHash<QString, TrackerInfo> m_trackerInfos;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_TORRENTHANDLE_H
|
||||||
230
src/core/bittorrent/torrentinfo.cpp
Normal file
230
src/core/bittorrent/torrentinfo.cpp
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <libtorrent/error_code.hpp>
|
||||||
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
|
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
#include "core/utils/fs.h"
|
||||||
|
#include "core/utils/string.h"
|
||||||
|
#include "infohash.h"
|
||||||
|
#include "trackerentry.h"
|
||||||
|
#include "torrentinfo.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
TorrentInfo::TorrentInfo(boost::intrusive_ptr<const libt::torrent_info> nativeInfo)
|
||||||
|
: m_nativeInfo(const_cast<libt::torrent_info *>(nativeInfo.get()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo::TorrentInfo(const TorrentInfo &other)
|
||||||
|
: m_nativeInfo(other.m_nativeInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
||||||
|
{
|
||||||
|
m_nativeInfo = other.m_nativeInfo;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
|
||||||
|
{
|
||||||
|
error.clear();
|
||||||
|
libt::error_code ec;
|
||||||
|
TorrentInfo info(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec));
|
||||||
|
if (ec) {
|
||||||
|
error = QString::fromUtf8(ec.message().c_str());
|
||||||
|
qDebug("Cannot load .torrent file: %s", qPrintable(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo TorrentInfo::loadFromFile(const QString &path)
|
||||||
|
{
|
||||||
|
QString error;
|
||||||
|
return loadFromFile(path, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentInfo::isValid() const
|
||||||
|
{
|
||||||
|
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoHash TorrentInfo::hash() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return InfoHash();
|
||||||
|
return m_nativeInfo->info_hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::name() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::String::fromStdString(m_nativeInfo->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime TorrentInfo::creationDate() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QDateTime();
|
||||||
|
boost::optional<time_t> t = m_nativeInfo->creation_date();
|
||||||
|
return t ? QDateTime::fromTime_t(*t) : QDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::creator() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::String::fromStdString(m_nativeInfo->creator());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::comment() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::String::fromStdString(m_nativeInfo->comment());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentInfo::isPrivate() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return false;
|
||||||
|
return m_nativeInfo->priv();
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong TorrentInfo::totalSize() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->total_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TorrentInfo::filesCount() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->num_files();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TorrentInfo::pieceLength() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->piece_length();
|
||||||
|
}
|
||||||
|
|
||||||
|
int TorrentInfo::piecesCount() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->num_pieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::filePath(int index) const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->files().file_path(index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TorrentInfo::filePaths() const
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
list << filePath(i);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::fileName(int index) const
|
||||||
|
{
|
||||||
|
return Utils::Fs::fileName(filePath(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::origFilePath(int index) const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->orig_files().file_path(index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong TorrentInfo::fileSize(int index) const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->files().file_size(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong TorrentInfo::fileOffset(int index) const
|
||||||
|
{
|
||||||
|
if (!isValid()) return -1;
|
||||||
|
return m_nativeInfo->file_at(index).offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TrackerEntry> TorrentInfo::trackers() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QList<TrackerEntry>();
|
||||||
|
|
||||||
|
QList<TrackerEntry> trackers;
|
||||||
|
foreach (const libt::announce_entry &tracker, m_nativeInfo->trackers())
|
||||||
|
trackers.append(tracker);
|
||||||
|
|
||||||
|
return trackers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QUrl> TorrentInfo::urlSeeds() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QList<QUrl>();
|
||||||
|
|
||||||
|
QList<QUrl> urlSeeds;
|
||||||
|
foreach (const libt::web_seed_entry &webSeed, m_nativeInfo->web_seeds())
|
||||||
|
if (webSeed.type == libt::web_seed_entry::url_seed)
|
||||||
|
urlSeeds.append(QUrl(webSeed.url.c_str()));
|
||||||
|
|
||||||
|
return urlSeeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray TorrentInfo::metadata() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QByteArray();
|
||||||
|
return QByteArray(m_nativeInfo->metadata().get(), m_nativeInfo->metadata_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TorrentInfo::toMagnetUri() const
|
||||||
|
{
|
||||||
|
if (!isValid()) return QString();
|
||||||
|
return Utils::String::fromStdString(libt::make_magnet_uri(*m_nativeInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||||
|
{
|
||||||
|
if (!isValid()) return;
|
||||||
|
m_nativeInfo->rename_file(index, Utils::String::toStdString(newPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::intrusive_ptr<libtorrent::torrent_info> TorrentInfo::nativeInfo() const
|
||||||
|
{
|
||||||
|
return m_nativeInfo;
|
||||||
|
}
|
||||||
88
src/core/bittorrent/torrentinfo.h
Normal file
88
src/core/bittorrent/torrentinfo.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_TORRENTINFO_H
|
||||||
|
#define BITTORRENT_TORRENTINFO_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QUrl;
|
||||||
|
class QDateTime;
|
||||||
|
class QStringList;
|
||||||
|
class QByteArray;
|
||||||
|
template<typename T> class QList;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class InfoHash;
|
||||||
|
class TrackerEntry;
|
||||||
|
|
||||||
|
class TorrentInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit TorrentInfo(boost::intrusive_ptr<const libtorrent::torrent_info> nativeInfo = boost::intrusive_ptr<const libtorrent::torrent_info>());
|
||||||
|
TorrentInfo(const TorrentInfo &other);
|
||||||
|
|
||||||
|
static TorrentInfo loadFromFile(const QString &path, QString &error);
|
||||||
|
static TorrentInfo loadFromFile(const QString &path);
|
||||||
|
|
||||||
|
TorrentInfo &operator=(const TorrentInfo &other);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
InfoHash hash() const;
|
||||||
|
QString name() const;
|
||||||
|
QDateTime creationDate() const;
|
||||||
|
QString creator() const;
|
||||||
|
QString comment() const;
|
||||||
|
bool isPrivate() const;
|
||||||
|
qlonglong totalSize() const;
|
||||||
|
int filesCount() const;
|
||||||
|
int pieceLength() const;
|
||||||
|
int piecesCount() const;
|
||||||
|
QString filePath(int index) const;
|
||||||
|
QStringList filePaths() const;
|
||||||
|
QString fileName(int index) const;
|
||||||
|
QString origFilePath(int index) const;
|
||||||
|
qlonglong fileSize(int index) const;
|
||||||
|
qlonglong fileOffset(int index) const;
|
||||||
|
QList<TrackerEntry> trackers() const;
|
||||||
|
QList<QUrl> urlSeeds() const;
|
||||||
|
QByteArray metadata() const;
|
||||||
|
QString toMagnetUri() const;
|
||||||
|
|
||||||
|
void renameFile(uint index, const QString &newPath);
|
||||||
|
boost::intrusive_ptr<libtorrent::torrent_info> nativeInfo() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::intrusive_ptr<libtorrent::torrent_info> m_nativeInfo;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_TORRENTINFO_H
|
||||||
@@ -31,59 +31,68 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
|
#include <libtorrent/entry.hpp>
|
||||||
|
|
||||||
#include "preferences.h"
|
#include "core/preferences.h"
|
||||||
#include "http/server.h"
|
#include "core/http/server.h"
|
||||||
#include "qtracker.h"
|
#include "core/utils/string.h"
|
||||||
|
#include "tracker.h"
|
||||||
|
|
||||||
// QPeer
|
// static limits
|
||||||
bool QPeer::operator!=(const QPeer &other) const
|
static const int MAX_TORRENTS = 100;
|
||||||
|
static const int MAX_PEERS_PER_TORRENT = 1000;
|
||||||
|
static const int ANNOUNCE_INTERVAL = 1800; // 30min
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
// Peer
|
||||||
|
bool Peer::operator!=(const Peer &other) const
|
||||||
{
|
{
|
||||||
return qhash() != other.qhash();
|
return uid() != other.uid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QPeer::operator==(const QPeer &other) const
|
bool Peer::operator==(const Peer &other) const
|
||||||
{
|
{
|
||||||
return qhash() == other.qhash();
|
return uid() == other.uid();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QPeer::qhash() const
|
QString Peer::uid() const
|
||||||
{
|
{
|
||||||
return ip + ":" + QString::number(port);
|
return ip + ":" + QString::number(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
libtorrent::entry QPeer::toEntry(bool no_peer_id) const
|
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
||||||
{
|
{
|
||||||
libtorrent::entry::dictionary_type peer_map;
|
libtorrent::entry::dictionary_type peerMap;
|
||||||
if (!no_peer_id)
|
if (!noPeerId)
|
||||||
peer_map["id"] = libtorrent::entry(peer_id.toStdString());
|
peerMap["id"] = libtorrent::entry(Utils::String::toStdString(peerId));
|
||||||
peer_map["ip"] = libtorrent::entry(ip.toStdString());
|
peerMap["ip"] = libtorrent::entry(Utils::String::toStdString(ip));
|
||||||
peer_map["port"] = libtorrent::entry(port);
|
peerMap["port"] = libtorrent::entry(port);
|
||||||
|
|
||||||
return libtorrent::entry(peer_map);
|
return libtorrent::entry(peerMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// QTracker
|
// Tracker
|
||||||
|
|
||||||
QTracker::QTracker(QObject *parent)
|
Tracker::Tracker(QObject *parent)
|
||||||
: Http::ResponseBuilder(parent)
|
: Http::ResponseBuilder(parent)
|
||||||
, m_server(new Http::Server(this, this))
|
, m_server(new Http::Server(this, this))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QTracker::~QTracker()
|
Tracker::~Tracker()
|
||||||
{
|
{
|
||||||
if (m_server->isListening())
|
if (m_server->isListening())
|
||||||
qDebug("Shutting down the embedded tracker...");
|
qDebug("Shutting down the embedded tracker...");
|
||||||
// TODO: Store the torrent list
|
// TODO: Store the torrent list
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QTracker::start()
|
bool Tracker::start()
|
||||||
{
|
{
|
||||||
const int listen_port = Preferences::instance()->getTrackerPort();
|
const int listenPort = Preferences::instance()->getTrackerPort();
|
||||||
|
|
||||||
if (m_server->isListening()) {
|
if (m_server->isListening()) {
|
||||||
if (m_server->serverPort() == listen_port) {
|
if (m_server->serverPort() == listenPort) {
|
||||||
// Already listening on the right port, just return
|
// Already listening on the right port, just return
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -93,21 +102,21 @@ bool QTracker::start()
|
|||||||
|
|
||||||
qDebug("Starting the embedded tracker...");
|
qDebug("Starting the embedded tracker...");
|
||||||
// Listen on the predefined port
|
// Listen on the predefined port
|
||||||
return m_server->listen(QHostAddress::Any, listen_port);
|
return m_server->listen(QHostAddress::Any, listenPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env)
|
Http::Response Tracker::processRequest(const Http::Request &request, const Http::Environment &env)
|
||||||
{
|
{
|
||||||
clear(); // clear response
|
clear(); // clear response
|
||||||
|
|
||||||
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
|
//qDebug("Tracker received the following request:\n%s", qPrintable(parser.toString()));
|
||||||
// Is request a GET request?
|
// Is request a GET request?
|
||||||
if (request.method != "GET") {
|
if (request.method != "GET") {
|
||||||
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
qDebug("Tracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
||||||
status(100, "Invalid request type");
|
status(100, "Invalid request type");
|
||||||
}
|
}
|
||||||
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
|
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
|
||||||
qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path));
|
qDebug("Tracker: Unrecognized path: %s", qPrintable(request.path));
|
||||||
status(100, "Invalid request type");
|
status(100, "Invalid request type");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -120,85 +129,85 @@ Http::Response QTracker::processRequest(const Http::Request &request, const Http
|
|||||||
return response();
|
return response();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QTracker::respondToAnnounceRequest()
|
void Tracker::respondToAnnounceRequest()
|
||||||
{
|
{
|
||||||
const QStringMap &gets = m_request.gets;
|
const QStringMap &gets = m_request.gets;
|
||||||
TrackerAnnounceRequest annonce_req;
|
TrackerAnnounceRequest annonceReq;
|
||||||
|
|
||||||
// IP
|
// IP
|
||||||
annonce_req.peer.ip = m_env.clientAddress.toString();
|
annonceReq.peer.ip = m_env.clientAddress.toString();
|
||||||
|
|
||||||
// 1. Get info_hash
|
// 1. Get info_hash
|
||||||
if (!gets.contains("info_hash")) {
|
if (!gets.contains("info_hash")) {
|
||||||
qDebug("QTracker: Missing info_hash");
|
qDebug("Tracker: Missing info_hash");
|
||||||
status(101, "Missing info_hash");
|
status(101, "Missing info_hash");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annonce_req.info_hash = gets.value("info_hash");
|
annonceReq.infoHash = gets.value("info_hash");
|
||||||
// info_hash cannot be longer than 20 bytes
|
// info_hash cannot be longer than 20 bytes
|
||||||
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
||||||
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
||||||
status(150, "Invalid infohash");
|
status(150, "Invalid infohash");
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 2. Get peer ID
|
// 2. Get peer ID
|
||||||
if (!gets.contains("peer_id")) {
|
if (!gets.contains("peer_id")) {
|
||||||
qDebug("QTracker: Missing peer_id");
|
qDebug("Tracker: Missing peer_id");
|
||||||
status(102, "Missing peer_id");
|
status(102, "Missing peer_id");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annonce_req.peer.peer_id = gets.value("peer_id");
|
annonceReq.peer.peerId = gets.value("peer_id");
|
||||||
// peer_id cannot be longer than 20 bytes
|
// peer_id cannot be longer than 20 bytes
|
||||||
/*if (annonce_req.peer.peer_id.length() > 20) {
|
/*if (annonce_req.peer.peer_id.length() > 20) {
|
||||||
qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
qDebug("Tracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
||||||
status(151, "Invalid peerid");
|
status(151, "Invalid peerid");
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 3. Get port
|
// 3. Get port
|
||||||
if (!gets.contains("port")) {
|
if (!gets.contains("port")) {
|
||||||
qDebug("QTracker: Missing port");
|
qDebug("Tracker: Missing port");
|
||||||
status(103, "Missing port");
|
status(103, "Missing port");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
annonce_req.peer.port = gets.value("port").toInt(&ok);
|
annonceReq.peer.port = gets.value("port").toInt(&ok);
|
||||||
if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) {
|
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
|
||||||
qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port);
|
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
|
||||||
status(103, "Missing port");
|
status(103, "Missing port");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Get event
|
// 4. Get event
|
||||||
annonce_req.event = "";
|
annonceReq.event = "";
|
||||||
if (gets.contains("event")) {
|
if (gets.contains("event")) {
|
||||||
annonce_req.event = gets.value("event");
|
annonceReq.event = gets.value("event");
|
||||||
qDebug("QTracker: event is %s", qPrintable(annonce_req.event));
|
qDebug("Tracker: event is %s", qPrintable(annonceReq.event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Get numwant
|
// 5. Get numwant
|
||||||
annonce_req.numwant = 50;
|
annonceReq.numwant = 50;
|
||||||
if (gets.contains("numwant")) {
|
if (gets.contains("numwant")) {
|
||||||
int tmp = gets.value("numwant").toInt();
|
int tmp = gets.value("numwant").toInt();
|
||||||
if (tmp > 0) {
|
if (tmp > 0) {
|
||||||
qDebug("QTracker: numwant = %d", tmp);
|
qDebug("Tracker: numwant = %d", tmp);
|
||||||
annonce_req.numwant = tmp;
|
annonceReq.numwant = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. no_peer_id (extension)
|
// 6. no_peer_id (extension)
|
||||||
annonce_req.no_peer_id = false;
|
annonceReq.noPeerId = false;
|
||||||
if (gets.contains("no_peer_id"))
|
if (gets.contains("no_peer_id"))
|
||||||
annonce_req.no_peer_id = true;
|
annonceReq.noPeerId = true;
|
||||||
|
|
||||||
// 7. TODO: support "compact" extension
|
// 7. TODO: support "compact" extension
|
||||||
|
|
||||||
// Done parsing, now let's reply
|
// Done parsing, now let's reply
|
||||||
if (m_torrents.contains(annonce_req.info_hash)) {
|
if (m_torrents.contains(annonceReq.infoHash)) {
|
||||||
if (annonce_req.event == "stopped") {
|
if (annonceReq.event == "stopped") {
|
||||||
qDebug("QTracker: Peer stopped downloading, deleting it from the list");
|
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
|
||||||
m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash());
|
m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,36 +219,36 @@ void QTracker::respondToAnnounceRequest()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Register the user
|
// Register the user
|
||||||
PeerList peers = m_torrents.value(annonce_req.info_hash);
|
PeerList peers = m_torrents.value(annonceReq.infoHash);
|
||||||
if (peers.size() == MAX_PEERS_PER_TORRENT) {
|
if (peers.size() == MAX_PEERS_PER_TORRENT) {
|
||||||
// Too many peers, remove a random one
|
// Too many peers, remove a random one
|
||||||
peers.erase(peers.begin());
|
peers.erase(peers.begin());
|
||||||
}
|
}
|
||||||
peers[annonce_req.peer.qhash()] = annonce_req.peer;
|
peers[annonceReq.peer.uid()] = annonceReq.peer;
|
||||||
m_torrents[annonce_req.info_hash] = peers;
|
m_torrents[annonceReq.infoHash] = peers;
|
||||||
|
|
||||||
// Reply
|
// Reply
|
||||||
replyWithPeerList(annonce_req);
|
replyWithPeerList(annonceReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req)
|
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
||||||
{
|
{
|
||||||
// Prepare the entry for bencoding
|
// Prepare the entry for bencoding
|
||||||
libtorrent::entry::dictionary_type reply_dict;
|
libtorrent::entry::dictionary_type replyDict;
|
||||||
reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
||||||
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values();
|
QList<Peer> peers = m_torrents.value(annonceReq.infoHash).values();
|
||||||
libtorrent::entry::list_type peer_list;
|
libtorrent::entry::list_type peerList;
|
||||||
foreach (const QPeer &p, peers) {
|
foreach (const Peer &p, peers) {
|
||||||
//if (p != annonce_req.peer)
|
//if (p != annonce_req.peer)
|
||||||
peer_list.push_back(p.toEntry(annonce_req.no_peer_id));
|
peerList.push_back(p.toEntry(annonceReq.noPeerId));
|
||||||
}
|
}
|
||||||
reply_dict["peers"] = libtorrent::entry(peer_list);
|
replyDict["peers"] = libtorrent::entry(peerList);
|
||||||
libtorrent::entry reply_entry(reply_dict);
|
libtorrent::entry replyEntry(replyDict);
|
||||||
// bencode
|
// bencode
|
||||||
std::vector<char> buf;
|
std::vector<char> buf;
|
||||||
libtorrent::bencode(std::back_inserter(buf), reply_entry);
|
libtorrent::bencode(std::back_inserter(buf), replyEntry);
|
||||||
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
|
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
|
||||||
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
|
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||||
|
|
||||||
// HTTP reply
|
// HTTP reply
|
||||||
print(reply, Http::CONTENT_TYPE_TXT);
|
print(reply, Http::CONTENT_TYPE_TXT);
|
||||||
103
src/core/bittorrent/tracker.h
Normal file
103
src/core/bittorrent/tracker.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_TRACKER_H
|
||||||
|
#define BITTORRENT_TRACKER_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include "core/http/types.h"
|
||||||
|
#include "core/http/responsebuilder.h"
|
||||||
|
#include "core/http/irequesthandler.h"
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
class entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Http
|
||||||
|
{
|
||||||
|
class Server;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
struct Peer
|
||||||
|
{
|
||||||
|
QString ip;
|
||||||
|
QString peerId;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
bool operator!=(const Peer &other) const;
|
||||||
|
bool operator==(const Peer &other) const;
|
||||||
|
QString uid() const;
|
||||||
|
libtorrent::entry toEntry(bool noPeerId) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrackerAnnounceRequest
|
||||||
|
{
|
||||||
|
QString infoHash;
|
||||||
|
QString event;
|
||||||
|
int numwant;
|
||||||
|
Peer peer;
|
||||||
|
// Extensions
|
||||||
|
bool noPeerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QHash<QString, Peer> PeerList;
|
||||||
|
typedef QHash<QString, PeerList> TorrentList;
|
||||||
|
|
||||||
|
/* Basic Bittorrent tracker implementation in Qt */
|
||||||
|
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
||||||
|
class Tracker : public Http::ResponseBuilder, public Http::IRequestHandler
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Tracker)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Tracker(QObject *parent = 0);
|
||||||
|
~Tracker();
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
Http::Response processRequest(const Http::Request &request, const Http::Environment &env);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void respondToAnnounceRequest();
|
||||||
|
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
|
||||||
|
|
||||||
|
Http::Server *m_server;
|
||||||
|
TorrentList m_torrents;
|
||||||
|
|
||||||
|
Http::Request m_request;
|
||||||
|
Http::Environment m_env;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_TRACKER_H
|
||||||
93
src/core/bittorrent/trackerentry.cpp
Normal file
93
src/core/bittorrent/trackerentry.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
#include "core/utils/string.h"
|
||||||
|
#include "trackerentry.h"
|
||||||
|
|
||||||
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
TrackerEntry::TrackerEntry(const QString &url)
|
||||||
|
: m_nativeEntry(libtorrent::announce_entry(Utils::String::toStdString(url)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackerEntry::TrackerEntry(const libtorrent::announce_entry &nativeEntry)
|
||||||
|
: m_nativeEntry(nativeEntry)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackerEntry::TrackerEntry(const TrackerEntry &other)
|
||||||
|
: m_nativeEntry(other.m_nativeEntry)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TrackerEntry::url() const
|
||||||
|
{
|
||||||
|
return Utils::String::fromStdString(m_nativeEntry.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TrackerEntry::tier() const
|
||||||
|
{
|
||||||
|
return m_nativeEntry.tier;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackerEntry::Status TrackerEntry::status() const
|
||||||
|
{
|
||||||
|
if (m_nativeEntry.verified)
|
||||||
|
return Working;
|
||||||
|
else if ((m_nativeEntry.fails == 0) && m_nativeEntry.updating)
|
||||||
|
return Updating;
|
||||||
|
else if (m_nativeEntry.fails == 0)
|
||||||
|
return NotContacted;
|
||||||
|
else
|
||||||
|
return NotWorking;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrackerEntry::setTier(int value)
|
||||||
|
{
|
||||||
|
m_nativeEntry.tier = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackerEntry &TrackerEntry::operator=(const TrackerEntry &other)
|
||||||
|
{
|
||||||
|
this->m_nativeEntry = other.m_nativeEntry;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TrackerEntry::operator==(const TrackerEntry &other)
|
||||||
|
{
|
||||||
|
return (QUrl(url()) == QUrl(other.url()));
|
||||||
|
}
|
||||||
|
|
||||||
|
libtorrent::announce_entry TrackerEntry::nativeEntry() const
|
||||||
|
{
|
||||||
|
return m_nativeEntry;
|
||||||
|
}
|
||||||
68
src/core/bittorrent/trackerentry.h
Normal file
68
src/core/bittorrent/trackerentry.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITTORRENT_TRACKERENTRY_H
|
||||||
|
#define BITTORRENT_TRACKERENTRY_H
|
||||||
|
|
||||||
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TrackerEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
NotContacted,
|
||||||
|
Working,
|
||||||
|
Updating,
|
||||||
|
NotWorking
|
||||||
|
};
|
||||||
|
|
||||||
|
TrackerEntry(const QString &url);
|
||||||
|
TrackerEntry(const libtorrent::announce_entry &nativeEntry);
|
||||||
|
TrackerEntry(const TrackerEntry &other);
|
||||||
|
|
||||||
|
QString url() const;
|
||||||
|
int tier() const;
|
||||||
|
Status status() const;
|
||||||
|
|
||||||
|
void setTier(int value);
|
||||||
|
TrackerEntry &operator=(const TrackerEntry &other);
|
||||||
|
bool operator==(const TrackerEntry &other);
|
||||||
|
|
||||||
|
libtorrent::announce_entry nativeEntry() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
libtorrent::announce_entry m_nativeEntry;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITTORRENT_TRACKERENTRY_H
|
||||||
@@ -1,43 +1,81 @@
|
|||||||
INCLUDEPATH += $$PWD
|
|
||||||
|
|
||||||
unix:!macx:dbus: include(qtnotify/qtnotify.pri)
|
|
||||||
|
|
||||||
include(qtlibtorrent/qtlibtorrent.pri)
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/misc.h \
|
$$PWD/types.h \
|
||||||
$$PWD/fs_utils.h \
|
$$PWD/tristatebool.h \
|
||||||
$$PWD/downloadthread.h \
|
|
||||||
$$PWD/torrentpersistentdata.h \
|
|
||||||
$$PWD/filesystemwatcher.h \
|
$$PWD/filesystemwatcher.h \
|
||||||
$$PWD/scannedfoldersmodel.h \
|
|
||||||
$$PWD/qinisettings.h \
|
$$PWD/qinisettings.h \
|
||||||
$$PWD/smtp.h \
|
|
||||||
$$PWD/dnsupdater.h \
|
|
||||||
$$PWD/logger.h \
|
$$PWD/logger.h \
|
||||||
$$PWD/preferences.h \
|
$$PWD/preferences.h \
|
||||||
$$PWD/qtracker.h \
|
$$PWD/iconprovider.h \
|
||||||
$$PWD/http/irequesthandler.h \
|
$$PWD/http/irequesthandler.h \
|
||||||
$$PWD/http/connection.h \
|
$$PWD/http/connection.h \
|
||||||
$$PWD/http/requestparser.h \
|
$$PWD/http/requestparser.h \
|
||||||
$$PWD/http/responsegenerator.h \
|
$$PWD/http/responsegenerator.h \
|
||||||
$$PWD/http/server.h \
|
$$PWD/http/server.h \
|
||||||
$$PWD/http/types.h \
|
$$PWD/http/types.h \
|
||||||
$$PWD/http/responsebuilder.h
|
$$PWD/http/responsebuilder.h \
|
||||||
|
$$PWD/net/dnsupdater.h \
|
||||||
|
$$PWD/net/downloadmanager.h \
|
||||||
|
$$PWD/net/downloadhandler.h \
|
||||||
|
$$PWD/net/portforwarder.h \
|
||||||
|
$$PWD/net/reverseresolution.h \
|
||||||
|
$$PWD/net/smtp.h \
|
||||||
|
$$PWD/bittorrent/infohash.h \
|
||||||
|
$$PWD/bittorrent/session.h \
|
||||||
|
$$PWD/bittorrent/sessionstatus.h \
|
||||||
|
$$PWD/bittorrent/cachestatus.h \
|
||||||
|
$$PWD/bittorrent/magneturi.h \
|
||||||
|
$$PWD/bittorrent/torrentinfo.h \
|
||||||
|
$$PWD/bittorrent/torrenthandle.h \
|
||||||
|
$$PWD/bittorrent/peerinfo.h \
|
||||||
|
$$PWD/bittorrent/trackerentry.h \
|
||||||
|
$$PWD/bittorrent/tracker.h \
|
||||||
|
$$PWD/bittorrent/torrentcreatorthread.h \
|
||||||
|
$$PWD/bittorrent/private/sessionprivate.h \
|
||||||
|
$$PWD/bittorrent/private/torrenthandleprivate.h \
|
||||||
|
$$PWD/bittorrent/private/speedmonitor.h \
|
||||||
|
$$PWD/bittorrent/private/bandwidthscheduler.h \
|
||||||
|
$$PWD/bittorrent/private/filterparserthread.h \
|
||||||
|
$$PWD/bittorrent/private/statistics.h \
|
||||||
|
$$PWD/utils/fs.h \
|
||||||
|
$$PWD/utils/misc.h \
|
||||||
|
$$PWD/utils/string.h \
|
||||||
|
$$PWD/torrentfilter.h \
|
||||||
|
$$PWD/scanfoldersmodel.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/downloadthread.cpp \
|
$$PWD/tristatebool.cpp \
|
||||||
$$PWD/scannedfoldersmodel.cpp \
|
$$PWD/filesystemwatcher.cpp \
|
||||||
$$PWD/torrentpersistentdata.cpp \
|
|
||||||
$$PWD/misc.cpp \
|
|
||||||
$$PWD/fs_utils.cpp \
|
|
||||||
$$PWD/smtp.cpp \
|
|
||||||
$$PWD/dnsupdater.cpp \
|
|
||||||
$$PWD/logger.cpp \
|
$$PWD/logger.cpp \
|
||||||
$$PWD/preferences.cpp \
|
$$PWD/preferences.cpp \
|
||||||
$$PWD/qtracker.cpp \
|
$$PWD/iconprovider.cpp \
|
||||||
$$PWD/http/connection.cpp \
|
$$PWD/http/connection.cpp \
|
||||||
$$PWD/http/requestparser.cpp \
|
$$PWD/http/requestparser.cpp \
|
||||||
$$PWD/http/responsegenerator.cpp \
|
$$PWD/http/responsegenerator.cpp \
|
||||||
$$PWD/http/server.cpp \
|
$$PWD/http/server.cpp \
|
||||||
$$PWD/http/responsebuilder.cpp
|
$$PWD/http/responsebuilder.cpp \
|
||||||
|
$$PWD/net/dnsupdater.cpp \
|
||||||
|
$$PWD/net/downloadmanager.cpp \
|
||||||
|
$$PWD/net/downloadhandler.cpp \
|
||||||
|
$$PWD/net/portforwarder.cpp \
|
||||||
|
$$PWD/net/reverseresolution.cpp \
|
||||||
|
$$PWD/net/smtp.cpp \
|
||||||
|
$$PWD/bittorrent/infohash.cpp \
|
||||||
|
$$PWD/bittorrent/session.cpp \
|
||||||
|
$$PWD/bittorrent/sessionstatus.cpp \
|
||||||
|
$$PWD/bittorrent/cachestatus.cpp \
|
||||||
|
$$PWD/bittorrent/magneturi.cpp \
|
||||||
|
$$PWD/bittorrent/torrentinfo.cpp \
|
||||||
|
$$PWD/bittorrent/torrenthandle.cpp \
|
||||||
|
$$PWD/bittorrent/peerinfo.cpp \
|
||||||
|
$$PWD/bittorrent/trackerentry.cpp \
|
||||||
|
$$PWD/bittorrent/tracker.cpp \
|
||||||
|
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
||||||
|
$$PWD/bittorrent/private/speedmonitor.cpp \
|
||||||
|
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
|
||||||
|
$$PWD/bittorrent/private/filterparserthread.cpp \
|
||||||
|
$$PWD/bittorrent/private/statistics.cpp \
|
||||||
|
$$PWD/utils/fs.cpp \
|
||||||
|
$$PWD/utils/misc.cpp \
|
||||||
|
$$PWD/utils/string.cpp \
|
||||||
|
$$PWD/torrentfilter.cpp \
|
||||||
|
$$PWD/scanfoldersmodel.cpp
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2011 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QRegExp>
|
|
||||||
#include <QStringList>
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
#include <QUrlQuery>
|
|
||||||
#endif
|
|
||||||
#include "dnsupdater.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
DNSUpdater::DNSUpdater(QObject *parent) :
|
|
||||||
QObject(parent), m_state(OK), m_service(DNS::NONE)
|
|
||||||
{
|
|
||||||
updateCredentials();
|
|
||||||
|
|
||||||
// Load saved settings from previous session
|
|
||||||
const Preferences* const pref = Preferences::instance();
|
|
||||||
m_lastIPCheckTime = pref->getDNSLastUpd();
|
|
||||||
m_lastIP = QHostAddress(pref->getDNSLastIP());
|
|
||||||
|
|
||||||
// Start IP checking timer
|
|
||||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
|
||||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
|
||||||
m_ipCheckTimer.start();
|
|
||||||
|
|
||||||
// Check lastUpdate to avoid flooding
|
|
||||||
if (!m_lastIPCheckTime.isValid() ||
|
|
||||||
m_lastIPCheckTime.secsTo(QDateTime::currentDateTime())*1000 > IP_CHECK_INTERVAL_MS) {
|
|
||||||
checkPublicIP();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DNSUpdater::~DNSUpdater() {
|
|
||||||
// Save lastupdate time and last ip
|
|
||||||
Preferences* const pref = Preferences::instance();
|
|
||||||
pref->setDNSLastUpd(m_lastIPCheckTime);
|
|
||||||
pref->setDNSLastIP(m_lastIP.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::checkPublicIP()
|
|
||||||
{
|
|
||||||
Q_ASSERT(m_state == OK);
|
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
|
||||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
|
||||||
SLOT(ipRequestFinished(QNetworkReply*)));
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setUrl(QUrl("http://checkip.dyndns.org"));
|
|
||||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
|
||||||
manager->get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
if (reply->error()) {
|
|
||||||
// Error
|
|
||||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
|
||||||
} else {
|
|
||||||
// Parse response
|
|
||||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
|
||||||
QString ret = reply->readAll();
|
|
||||||
if (ipregex.indexIn(ret) >= 0) {
|
|
||||||
QString ip_str = ipregex.cap(1);
|
|
||||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
|
||||||
QHostAddress new_ip(ip_str);
|
|
||||||
if (!new_ip.isNull()) {
|
|
||||||
if (m_lastIP != new_ip) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
|
||||||
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
|
||||||
m_lastIP = new_ip;
|
|
||||||
updateDNSService();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Clean up
|
|
||||||
reply->deleteLater();
|
|
||||||
sender()->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::updateDNSService()
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
// Prepare request
|
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
|
||||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
|
||||||
SLOT(ipUpdateFinished(QNetworkReply*)));
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
|
||||||
QNetworkRequest request;
|
|
||||||
request.setUrl(getUpdateUrl());
|
|
||||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
|
||||||
manager->get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl DNSUpdater::getUpdateUrl() const
|
|
||||||
{
|
|
||||||
QUrl url;
|
|
||||||
#ifdef QT_NO_OPENSSL
|
|
||||||
url.setScheme("http");
|
|
||||||
#else
|
|
||||||
url.setScheme("https");
|
|
||||||
#endif
|
|
||||||
url.setUserName(m_username);
|
|
||||||
url.setPassword(m_password);
|
|
||||||
|
|
||||||
Q_ASSERT(!m_lastIP.isNull());
|
|
||||||
// Service specific
|
|
||||||
switch(m_service) {
|
|
||||||
case DNS::DYNDNS:
|
|
||||||
url.setHost("members.dyndns.org");
|
|
||||||
break;
|
|
||||||
case DNS::NOIP:
|
|
||||||
url.setHost("dynupdate.no-ip.com");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qWarning() << "Unrecognized Dynamic DNS service!";
|
|
||||||
Q_ASSERT(0);
|
|
||||||
}
|
|
||||||
url.setPath("/nic/update");
|
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
url.addQueryItem("hostname", m_domain);
|
|
||||||
url.addQueryItem("myip", m_lastIP.toString());
|
|
||||||
#else
|
|
||||||
QUrlQuery urlQuery(url);
|
|
||||||
urlQuery.addQueryItem("hostname", m_domain);
|
|
||||||
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
|
||||||
url.setQuery(urlQuery);
|
|
||||||
#endif
|
|
||||||
Q_ASSERT(url.isValid());
|
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << url.toString();
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
|
|
||||||
{
|
|
||||||
if (reply->error()) {
|
|
||||||
// Error
|
|
||||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
|
||||||
} else {
|
|
||||||
// Pase reply
|
|
||||||
processIPUpdateReply(reply->readAll());
|
|
||||||
}
|
|
||||||
// Clean up
|
|
||||||
reply->deleteLater();
|
|
||||||
sender()->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::processIPUpdateReply(const QString &reply)
|
|
||||||
{
|
|
||||||
Logger* const logger = Logger::instance();
|
|
||||||
qDebug() << Q_FUNC_INFO << reply;
|
|
||||||
QString code = reply.split(" ").first();
|
|
||||||
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
|
||||||
if (code == "good" || code == "nochg") {
|
|
||||||
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (code == "911" || code == "dnserr") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
|
|
||||||
m_lastIP.clear();
|
|
||||||
// It will retry in 30 minutes because the timer was not stopped
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Everything bellow is an error, stop updating until the user updates something
|
|
||||||
m_ipCheckTimer.stop();
|
|
||||||
m_lastIP.clear();
|
|
||||||
if (code == "nohost") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (code == "badauth") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (code == "badagent") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
|
||||||
Log::CRITICAL);
|
|
||||||
m_state = FATAL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (code == "!donator") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
|
||||||
Log::CRITICAL);
|
|
||||||
m_state = FATAL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (code == "abuse") {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
|
||||||
m_state = FATAL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSUpdater::updateCredentials()
|
|
||||||
{
|
|
||||||
if (m_state == FATAL) return;
|
|
||||||
Preferences* const pref = Preferences::instance();
|
|
||||||
Logger* const logger = Logger::instance();
|
|
||||||
bool change = false;
|
|
||||||
// Get DNS service information
|
|
||||||
if (m_service != pref->getDynDNSService()) {
|
|
||||||
m_service = pref->getDynDNSService();
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
if (m_domain != pref->getDynDomainName()) {
|
|
||||||
m_domain = pref->getDynDomainName();
|
|
||||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
|
||||||
if (domain_regex.indexIn(m_domain) < 0) {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
|
||||||
m_lastIP.clear();
|
|
||||||
m_ipCheckTimer.stop();
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
if (m_username != pref->getDynDNSUsername()) {
|
|
||||||
m_username = pref->getDynDNSUsername();
|
|
||||||
if (m_username.length() < 4) {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
|
||||||
m_lastIP.clear();
|
|
||||||
m_ipCheckTimer.stop();
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
if (m_password != pref->getDynDNSPassword()) {
|
|
||||||
m_password = pref->getDynDNSPassword();
|
|
||||||
if (m_password.length() < 4) {
|
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
|
||||||
m_lastIP.clear();
|
|
||||||
m_ipCheckTimer.stop();
|
|
||||||
m_state = INVALID_CREDS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_state == INVALID_CREDS && change) {
|
|
||||||
m_state = OK; // Try again
|
|
||||||
m_ipCheckTimer.start();
|
|
||||||
checkPublicIP();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl DNSUpdater::getRegistrationUrl(int service)
|
|
||||||
{
|
|
||||||
switch(service) {
|
|
||||||
case DNS::DYNDNS:
|
|
||||||
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
|
||||||
case DNS::NOIP:
|
|
||||||
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
|
||||||
default:
|
|
||||||
Q_ASSERT(0);
|
|
||||||
}
|
|
||||||
return QUrl();
|
|
||||||
}
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QNetworkProxy>
|
|
||||||
#include <QNetworkCookieJar>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "downloadthread.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "qinisettings.h"
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
/** Download Thread **/
|
|
||||||
|
|
||||||
DownloadThread::DownloadThread(QObject* parent) : QObject(parent) {
|
|
||||||
connect(&m_networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList<QSslError>)));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray DownloadThread::gUncompress(Bytef *inData, size_t len) {
|
|
||||||
if (len <= 4) {
|
|
||||||
qWarning("gUncompress: Input data is truncated");
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray result;
|
|
||||||
|
|
||||||
z_stream strm;
|
|
||||||
static const int CHUNK_SIZE = 1024;
|
|
||||||
char out[CHUNK_SIZE];
|
|
||||||
|
|
||||||
/* allocate inflate state */
|
|
||||||
strm.zalloc = Z_NULL;
|
|
||||||
strm.zfree = Z_NULL;
|
|
||||||
strm.opaque = Z_NULL;
|
|
||||||
strm.avail_in = len;
|
|
||||||
strm.next_in = inData;
|
|
||||||
|
|
||||||
const int windowBits = 15;
|
|
||||||
const int ENABLE_ZLIB_GZIP = 32;
|
|
||||||
|
|
||||||
int ret = inflateInit2(&strm, windowBits|ENABLE_ZLIB_GZIP ); // gzip decoding
|
|
||||||
if (ret != Z_OK)
|
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
// run inflate()
|
|
||||||
do {
|
|
||||||
strm.avail_out = CHUNK_SIZE;
|
|
||||||
strm.next_out = reinterpret_cast<unsigned char*>(out);
|
|
||||||
|
|
||||||
ret = inflate(&strm, Z_NO_FLUSH);
|
|
||||||
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
|
|
||||||
|
|
||||||
switch (ret) {
|
|
||||||
case Z_NEED_DICT:
|
|
||||||
case Z_DATA_ERROR:
|
|
||||||
case Z_MEM_ERROR:
|
|
||||||
(void) inflateEnd(&strm);
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.append(out, CHUNK_SIZE - strm.avail_out);
|
|
||||||
} while (!strm.avail_out);
|
|
||||||
|
|
||||||
// clean up and return
|
|
||||||
inflateEnd(&strm);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadThread::processDlFinished(QNetworkReply* reply) {
|
|
||||||
QString url = reply->url().toString();
|
|
||||||
qDebug("Download finished: %s", qPrintable(url));
|
|
||||||
// Check if the request was successful
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
// Failure
|
|
||||||
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error())));
|
|
||||||
emit downloadFailure(url, errorCodeToString(reply->error()));
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check if the server ask us to redirect somewhere lese
|
|
||||||
const QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
|
||||||
if (redirection.isValid()) {
|
|
||||||
// We should redirect
|
|
||||||
QUrl newUrl = redirection.toUrl();
|
|
||||||
// Resolve relative urls
|
|
||||||
if (newUrl.isRelative())
|
|
||||||
newUrl = reply->url().resolved(newUrl);
|
|
||||||
const QString newUrlString = newUrl.toString();
|
|
||||||
qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(newUrlString));
|
|
||||||
// Redirect to magnet workaround
|
|
||||||
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
|
||||||
qDebug("Magnet redirect detected.");
|
|
||||||
reply->abort();
|
|
||||||
emit magnetRedirect(newUrlString, url);
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_redirectMapping.insert(newUrlString, url);
|
|
||||||
// redirecting with first cookies
|
|
||||||
downloadUrl(newUrlString, m_networkManager.cookieJar()->cookiesForUrl(url));
|
|
||||||
reply->deleteLater();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Checking if it was redirected, restoring initial URL
|
|
||||||
if (m_redirectMapping.contains(url)) {
|
|
||||||
url = m_redirectMapping.take(url);
|
|
||||||
}
|
|
||||||
// Success
|
|
||||||
QTemporaryFile *tmpfile = new QTemporaryFile;
|
|
||||||
if (tmpfile->open()) {
|
|
||||||
tmpfile->setAutoRemove(false);
|
|
||||||
QString filePath = tmpfile->fileName();
|
|
||||||
qDebug("Temporary filename is: %s", qPrintable(filePath));
|
|
||||||
if (reply->isOpen() || reply->open(QIODevice::ReadOnly)) {
|
|
||||||
QByteArray replyData = reply->readAll();
|
|
||||||
if (reply->rawHeader("Content-Encoding") == "gzip") {
|
|
||||||
// uncompress gzip reply
|
|
||||||
replyData = gUncompress(reinterpret_cast<unsigned char*>(replyData.data()), replyData.length());
|
|
||||||
}
|
|
||||||
tmpfile->write(replyData);
|
|
||||||
tmpfile->close();
|
|
||||||
// XXX: tmpfile needs to be deleted on Windows before using the file
|
|
||||||
// or it will complain that the file is used by another process.
|
|
||||||
delete tmpfile;
|
|
||||||
// Send finished signal
|
|
||||||
emit downloadFinished(url, filePath);
|
|
||||||
} else {
|
|
||||||
delete tmpfile;
|
|
||||||
fsutils::forceRemove(filePath);
|
|
||||||
// Error when reading the request
|
|
||||||
emit downloadFailure(url, tr("I/O Error"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete tmpfile;
|
|
||||||
emit downloadFailure(url, tr("I/O Error"));
|
|
||||||
}
|
|
||||||
// Clean up
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadThread::downloadTorrentUrl(const QString &url, const QList<QNetworkCookie>& cookies)
|
|
||||||
{
|
|
||||||
// Process request
|
|
||||||
QNetworkReply *reply = downloadUrl(url, cookies);
|
|
||||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply* DownloadThread::downloadUrl(const QString &url, const QList<QNetworkCookie>& cookies) {
|
|
||||||
// Update proxy settings
|
|
||||||
applyProxySettings();
|
|
||||||
// Set cookies
|
|
||||||
if (!cookies.empty()) {
|
|
||||||
qDebug("Setting %d cookies for url: %s", cookies.size(), qPrintable(url));
|
|
||||||
m_networkManager.cookieJar()->setCookiesFromUrl(cookies, url);
|
|
||||||
}
|
|
||||||
// Process download request
|
|
||||||
qDebug("url is %s", qPrintable(url));
|
|
||||||
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
|
|
||||||
QNetworkRequest request(qurl);
|
|
||||||
// Spoof Firefox 3.5 user agent to avoid
|
|
||||||
// Web server banning
|
|
||||||
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5");
|
|
||||||
qDebug("Downloading %s...", request.url().toEncoded().data());
|
|
||||||
qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size());
|
|
||||||
for (int i=0; i<m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
|
|
||||||
qDebug("%s=%s", m_networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), m_networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data());
|
|
||||||
qDebug("Domain: %s, Path: %s", qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).path()));
|
|
||||||
}
|
|
||||||
// accept gzip
|
|
||||||
request.setRawHeader("Accept-Encoding", "gzip");
|
|
||||||
return m_networkManager.get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) {
|
|
||||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
|
||||||
if (!reply) return;
|
|
||||||
if (bytesTotal > 0) {
|
|
||||||
// Total number of bytes is available
|
|
||||||
if (bytesTotal > 1048576*10) {
|
|
||||||
// More than 10MB, this is probably not a torrent file, aborting...
|
|
||||||
reply->abort();
|
|
||||||
reply->deleteLater();
|
|
||||||
} else {
|
|
||||||
disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bytesReceived > 1048576*10) {
|
|
||||||
// More than 10MB, this is probably not a torrent file, aborting...
|
|
||||||
reply->abort();
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadThread::applyProxySettings() {
|
|
||||||
QNetworkProxy proxy;
|
|
||||||
const Preferences* const pref = Preferences::instance();
|
|
||||||
if (pref->isProxyEnabled()) {
|
|
||||||
// Proxy enabled
|
|
||||||
proxy.setHostName(pref->getProxyIp());
|
|
||||||
proxy.setPort(pref->getProxyPort());
|
|
||||||
// Default proxy type is HTTP, we must change if it is SOCKS5
|
|
||||||
const int proxy_type = pref->getProxyType();
|
|
||||||
if (proxy_type == Proxy::SOCKS5 || proxy_type == Proxy::SOCKS5_PW) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "using SOCKS proxy";
|
|
||||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
|
||||||
} else {
|
|
||||||
qDebug() << Q_FUNC_INFO << "using HTTP proxy";
|
|
||||||
proxy.setType(QNetworkProxy::HttpProxy);
|
|
||||||
}
|
|
||||||
// Authentication?
|
|
||||||
if (pref->isProxyAuthEnabled()) {
|
|
||||||
qDebug("Proxy requires authentication, authenticating");
|
|
||||||
proxy.setUser(pref->getProxyUsername());
|
|
||||||
proxy.setPassword(pref->getProxyPassword());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
proxy.setType(QNetworkProxy::NoProxy);
|
|
||||||
}
|
|
||||||
m_networkManager.setProxy(proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
|
|
||||||
switch(status) {
|
|
||||||
case QNetworkReply::HostNotFoundError:
|
|
||||||
return tr("The remote host name was not found (invalid hostname)");
|
|
||||||
case QNetworkReply::OperationCanceledError:
|
|
||||||
return tr("The operation was canceled");
|
|
||||||
case QNetworkReply::RemoteHostClosedError:
|
|
||||||
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
|
||||||
case QNetworkReply::TimeoutError:
|
|
||||||
return tr("The connection to the remote server timed out");
|
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
|
||||||
return tr("SSL/TLS handshake failed");
|
|
||||||
case QNetworkReply::ConnectionRefusedError:
|
|
||||||
return tr("The remote server refused the connection");
|
|
||||||
case QNetworkReply::ProxyConnectionRefusedError:
|
|
||||||
return tr("The connection to the proxy server was refused");
|
|
||||||
case QNetworkReply::ProxyConnectionClosedError:
|
|
||||||
return tr("The proxy server closed the connection prematurely");
|
|
||||||
case QNetworkReply::ProxyNotFoundError:
|
|
||||||
return tr("The proxy host name was not found");
|
|
||||||
case QNetworkReply::ProxyTimeoutError:
|
|
||||||
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
|
||||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
|
||||||
return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered");
|
|
||||||
case QNetworkReply::ContentAccessDenied:
|
|
||||||
return tr("The access to the remote content was denied (401)");
|
|
||||||
case QNetworkReply::ContentOperationNotPermittedError:
|
|
||||||
return tr("The operation requested on the remote content is not permitted");
|
|
||||||
case QNetworkReply::ContentNotFoundError:
|
|
||||||
return tr("The remote content was not found at the server (404)");
|
|
||||||
case QNetworkReply::AuthenticationRequiredError:
|
|
||||||
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
|
||||||
case QNetworkReply::ProtocolUnknownError:
|
|
||||||
return tr("The Network Access API cannot honor the request because the protocol is not known");
|
|
||||||
case QNetworkReply::ProtocolInvalidOperationError:
|
|
||||||
return tr("The requested operation is invalid for this protocol");
|
|
||||||
case QNetworkReply::UnknownNetworkError:
|
|
||||||
return tr("An unknown network-related error was detected");
|
|
||||||
case QNetworkReply::UnknownProxyError:
|
|
||||||
return tr("An unknown proxy-related error was detected");
|
|
||||||
case QNetworkReply::UnknownContentError:
|
|
||||||
return tr("An unknown error related to the remote content was detected");
|
|
||||||
case QNetworkReply::ProtocolFailure:
|
|
||||||
return tr("A breakdown in protocol was detected");
|
|
||||||
default:
|
|
||||||
return tr("Unknown error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
void DownloadThread::ignoreSslErrors(QNetworkReply* reply, const QList<QSslError> &errors) {
|
|
||||||
Q_UNUSED(errors)
|
|
||||||
// Ignore all SSL errors
|
|
||||||
reply->ignoreSslErrors();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DOWNLOADTHREAD_H
|
|
||||||
#define DOWNLOADTHREAD_H
|
|
||||||
|
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkCookie>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QSslError>
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class DownloadThread : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
DownloadThread(QObject* parent = 0);
|
|
||||||
QNetworkReply* downloadUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
|
||||||
void downloadTorrentUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
|
||||||
//void setProxy(QString IP, int port, QString username, QString password);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void downloadFinished(const QString &url, const QString &file_path);
|
|
||||||
void downloadFailure(const QString &url, const QString &reason);
|
|
||||||
void magnetRedirect(const QString &url_new, const QString &url_old);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void processDlFinished(QNetworkReply* reply);
|
|
||||||
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
void ignoreSslErrors(QNetworkReply*,const QList<QSslError>&);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
static QByteArray gUncompress(Bytef *inData, size_t len);
|
|
||||||
QString errorCodeToString(QNetworkReply::NetworkError status);
|
|
||||||
void applyProxySettings();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QNetworkAccessManager m_networkManager;
|
|
||||||
QHash<QString, QString> m_redirectMapping;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
278
src/core/filesystemwatcher.cpp
Normal file
278
src/core/filesystemwatcher.cpp
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
#include <QtGlobal>
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
#include <QSet>
|
||||||
|
#include <iostream>
|
||||||
|
#include <errno.h>
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <string.h>
|
||||||
|
#elif !defined Q_OS_HAIKU
|
||||||
|
#include <sys/vfs.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "core/preferences.h"
|
||||||
|
#include "core/bittorrent/torrentinfo.h"
|
||||||
|
#include "core/bittorrent/magneturi.h"
|
||||||
|
#include "filesystemwatcher.h"
|
||||||
|
|
||||||
|
#ifndef CIFS_MAGIC_NUMBER
|
||||||
|
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NFS_SUPER_MAGIC
|
||||||
|
#define NFS_SUPER_MAGIC 0x6969
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SMB_SUPER_MAGIC
|
||||||
|
#define SMB_SUPER_MAGIC 0x517B
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int WATCH_INTERVAL = 10000; // 10 sec
|
||||||
|
const int MAX_PARTIAL_RETRIES = 5;
|
||||||
|
|
||||||
|
FileSystemWatcher::FileSystemWatcher(QObject *parent)
|
||||||
|
: QFileSystemWatcher(parent)
|
||||||
|
{
|
||||||
|
m_filters << "*.torrent" << "*.magnet";
|
||||||
|
connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystemWatcher::~FileSystemWatcher()
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (m_watchTimer)
|
||||||
|
delete m_watchTimer;
|
||||||
|
#endif
|
||||||
|
if (m_partialTorrentTimer)
|
||||||
|
delete m_partialTorrentTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FileSystemWatcher::directories() const
|
||||||
|
{
|
||||||
|
QStringList dirs;
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
if (m_watchTimer) {
|
||||||
|
foreach (const QDir &dir, m_watchedFolders)
|
||||||
|
dirs << dir.canonicalPath();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
dirs << QFileSystemWatcher::directories();
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addPath(const QString &path)
|
||||||
|
{
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) return;
|
||||||
|
|
||||||
|
// Check if the path points to a network file system or not
|
||||||
|
if (isNetworkFileSystem(path)) {
|
||||||
|
// Network mode
|
||||||
|
qDebug("Network folder detected: %s", qPrintable(path));
|
||||||
|
qDebug("Using file polling mode instead of inotify...");
|
||||||
|
m_watchedFolders << dir;
|
||||||
|
// Set up the watch timer
|
||||||
|
if (!m_watchTimer) {
|
||||||
|
m_watchTimer = new QTimer(this);
|
||||||
|
connect(m_watchTimer, SIGNAL(timeout()), SLOT(scanNetworkFolders()));
|
||||||
|
m_watchTimer->start(WATCH_INTERVAL); // 5 sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
// Normal mode
|
||||||
|
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
||||||
|
QFileSystemWatcher::addPath(path);
|
||||||
|
scanLocalFolder(path);
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::removePath(const QString &path)
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
QDir dir(path);
|
||||||
|
for (int i = 0; i < m_watchedFolders.count(); ++i) {
|
||||||
|
if (QDir(m_watchedFolders.at(i)) == dir) {
|
||||||
|
m_watchedFolders.removeAt(i);
|
||||||
|
if (m_watchedFolders.isEmpty())
|
||||||
|
delete m_watchTimer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Normal mode
|
||||||
|
QFileSystemWatcher::removePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::scanLocalFolder(QString path)
|
||||||
|
{
|
||||||
|
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
||||||
|
QStringList torrents;
|
||||||
|
// Local folders scan
|
||||||
|
addTorrentsFromDir(QDir(path), torrents);
|
||||||
|
// Report detected torrent files
|
||||||
|
if (!torrents.empty()) {
|
||||||
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
|
emit torrentsAdded(torrents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::scanNetworkFolders()
|
||||||
|
{
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
qDebug("scanNetworkFolders() called");
|
||||||
|
QStringList torrents;
|
||||||
|
// Network folders scan
|
||||||
|
foreach (const QDir &dir, m_watchedFolders) {
|
||||||
|
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
||||||
|
addTorrentsFromDir(dir, torrents);
|
||||||
|
}
|
||||||
|
// Report detected torrent files
|
||||||
|
if (!torrents.empty()) {
|
||||||
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
|
emit torrentsAdded(torrents);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::processPartialTorrents()
|
||||||
|
{
|
||||||
|
QStringList noLongerPartial;
|
||||||
|
|
||||||
|
// Check which torrents are still partial
|
||||||
|
foreach (const QString &torrentPath, m_partialTorrents.keys()) {
|
||||||
|
if (!QFile::exists(torrentPath)) {
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
}
|
||||||
|
else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
|
||||||
|
noLongerPartial << torrentPath;
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
}
|
||||||
|
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
|
||||||
|
m_partialTorrents.remove(torrentPath);
|
||||||
|
QFile::rename(torrentPath, torrentPath + ".invalid");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
++m_partialTorrents[torrentPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the partial timer if necessary
|
||||||
|
if (m_partialTorrents.empty()) {
|
||||||
|
m_partialTorrentTimer->stop();
|
||||||
|
m_partialTorrentTimer->deleteLater();
|
||||||
|
qDebug("No longer any partial torrent.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
||||||
|
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify of new torrents
|
||||||
|
if (!noLongerPartial.isEmpty())
|
||||||
|
emit torrentsAdded(noLongerPartial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::startPartialTorrentTimer()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_partialTorrents.isEmpty());
|
||||||
|
if (!m_partialTorrentTimer) {
|
||||||
|
m_partialTorrentTimer = new QTimer();
|
||||||
|
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
||||||
|
m_partialTorrentTimer->setSingleShot(true);
|
||||||
|
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrents)
|
||||||
|
{
|
||||||
|
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
||||||
|
foreach (const QString &file, files) {
|
||||||
|
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||||
|
if (fileAbsPath.endsWith(".magnet")) {
|
||||||
|
QFile f(fileAbsPath);
|
||||||
|
if (f.open(QIODevice::ReadOnly)
|
||||||
|
&& !BitTorrent::MagnetUri(QString::fromLocal8Bit(f.readAll())).isValid()) {
|
||||||
|
torrents << fileAbsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
|
||||||
|
torrents << fileAbsPath;
|
||||||
|
}
|
||||||
|
else if (!m_partialTorrents.contains(fileAbsPath)) {
|
||||||
|
qDebug("Partial torrent detected at: %s", qPrintable(fileAbsPath));
|
||||||
|
qDebug("Delay the file's processing...");
|
||||||
|
m_partialTorrents.insert(fileAbsPath, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_partialTorrents.empty())
|
||||||
|
startPartialTorrentTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
|
bool FileSystemWatcher::isNetworkFileSystem(QString path)
|
||||||
|
{
|
||||||
|
QString file = path;
|
||||||
|
if (!file.endsWith("/"))
|
||||||
|
file += "/";
|
||||||
|
file += ".";
|
||||||
|
struct statfs buf;
|
||||||
|
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
||||||
|
return ((strcmp(buf.f_fstypename, "nfs") == 0) || (strcmp(buf.f_fstypename, "cifs") == 0) || (strcmp(buf.f_fstypename, "smbfs") == 0));
|
||||||
|
#else
|
||||||
|
return ((buf.f_type == (long)CIFS_MAGIC_NUMBER) || (buf.f_type == (long)NFS_SUPER_MAGIC) || (buf.f_type == (long)SMB_SUPER_MAGIC));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
||||||
|
switch(errno) {
|
||||||
|
case EACCES:
|
||||||
|
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
||||||
|
break;
|
||||||
|
case EFAULT:
|
||||||
|
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
||||||
|
break;
|
||||||
|
case EINTR:
|
||||||
|
std::cerr << "This call was interrupted by a signal" << std::endl;
|
||||||
|
break;
|
||||||
|
case EIO:
|
||||||
|
std::cerr << "I/O Error" << std::endl;
|
||||||
|
break;
|
||||||
|
case ELOOP:
|
||||||
|
std::cerr << "Too many symlinks" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
std::cerr << "path is too long" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
std::cerr << "The file referred by path does not exist" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOMEM:
|
||||||
|
std::cerr << "Insufficient kernel memory" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOSYS:
|
||||||
|
std::cerr << "The file system does not detect this call" << std::endl;
|
||||||
|
break;
|
||||||
|
case ENOTDIR:
|
||||||
|
std::cerr << "A component of the path is not a directory" << std::endl;
|
||||||
|
break;
|
||||||
|
case EOVERFLOW:
|
||||||
|
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cerr << "Unknown error" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Errno: " << errno << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -8,290 +8,45 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
#include <QSet>
|
|
||||||
#include <iostream>
|
|
||||||
#include <errno.h>
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#include <string.h>
|
|
||||||
#elif !defined Q_OS_HAIKU
|
|
||||||
#include <sys/vfs.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#ifndef CIFS_MAGIC_NUMBER
|
|
||||||
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NFS_SUPER_MAGIC
|
|
||||||
#define NFS_SUPER_MAGIC 0x6969
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SMB_SUPER_MAGIC
|
|
||||||
#define SMB_SUPER_MAGIC 0x517B
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const int WATCH_INTERVAL = 10000; // 10 sec
|
|
||||||
const int MAX_PARTIAL_RETRIES = 5;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Subclassing QFileSystemWatcher in order to support Network File
|
* Subclassing QFileSystemWatcher in order to support Network File
|
||||||
* System watching (NFS, CIFS) on Linux and Mac OS.
|
* System watching (NFS, CIFS) on Linux and Mac OS.
|
||||||
*/
|
*/
|
||||||
class FileSystemWatcher: public QFileSystemWatcher {
|
class FileSystemWatcher : public QFileSystemWatcher
|
||||||
Q_OBJECT
|
{
|
||||||
|
Q_OBJECT
|
||||||
private:
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
QList<QDir> watched_folders;
|
|
||||||
QPointer<QTimer> watch_timer;
|
|
||||||
#endif
|
|
||||||
QStringList m_filters;
|
|
||||||
// Partial torrents
|
|
||||||
QHash<QString, int> m_partialTorrents;
|
|
||||||
QPointer<QTimer> m_partialTorrentTimer;
|
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
private:
|
|
||||||
static bool isNetworkFileSystem(QString path) {
|
|
||||||
QString file = path;
|
|
||||||
if (!file.endsWith("/"))
|
|
||||||
file += "/";
|
|
||||||
file += ".";
|
|
||||||
struct statfs buf;
|
|
||||||
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
|
||||||
return (strcmp(buf.f_fstypename, "nfs") == 0 || strcmp(buf.f_fstypename, "cifs") == 0 || strcmp(buf.f_fstypename, "smbfs") == 0);
|
|
||||||
#else
|
|
||||||
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC || buf.f_type == (long)SMB_SUPER_MAGIC);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
|
||||||
switch(errno) {
|
|
||||||
case EACCES:
|
|
||||||
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
|
||||||
break;
|
|
||||||
case EFAULT:
|
|
||||||
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
|
||||||
break;
|
|
||||||
case EINTR:
|
|
||||||
std::cerr << "This call was interrupted by a signal" << std::endl;
|
|
||||||
break;
|
|
||||||
case EIO:
|
|
||||||
std::cerr << "I/O Error" << std::endl;
|
|
||||||
break;
|
|
||||||
case ELOOP:
|
|
||||||
std::cerr << "Too many symlinks" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENAMETOOLONG:
|
|
||||||
std::cerr << "path is too long" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
std::cerr << "The file referred by path does not exist" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOMEM:
|
|
||||||
std::cerr << "Insufficient kernel memory" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOSYS:
|
|
||||||
std::cerr << "The file system does not detect this call" << std::endl;
|
|
||||||
break;
|
|
||||||
case ENOTDIR:
|
|
||||||
std::cerr << "A component of the path is not a directory" << std::endl;
|
|
||||||
break;
|
|
||||||
case EOVERFLOW:
|
|
||||||
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cerr << "Unknown error" << std::endl;
|
|
||||||
}
|
|
||||||
std::cerr << "Errno: " << errno << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
|
explicit FileSystemWatcher(QObject *parent = 0);
|
||||||
m_filters << "*.torrent" << "*.magnet";
|
~FileSystemWatcher();
|
||||||
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString)));
|
|
||||||
}
|
|
||||||
|
|
||||||
~FileSystemWatcher() {
|
QStringList directories() const;
|
||||||
#ifndef Q_OS_WIN
|
void addPath(const QString &path);
|
||||||
if (watch_timer)
|
void removePath(const QString &path);
|
||||||
delete watch_timer;
|
|
||||||
#endif
|
|
||||||
if (m_partialTorrentTimer)
|
|
||||||
delete m_partialTorrentTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList directories() const {
|
|
||||||
QStringList dirs;
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
if (watch_timer) {
|
|
||||||
foreach (const QDir &dir, watched_folders)
|
|
||||||
dirs << dir.canonicalPath();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
dirs << QFileSystemWatcher::directories();
|
|
||||||
return dirs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addPath(const QString & path) {
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
QDir dir(path);
|
|
||||||
if (!dir.exists())
|
|
||||||
return;
|
|
||||||
// Check if the path points to a network file system or not
|
|
||||||
if (isNetworkFileSystem(path)) {
|
|
||||||
// Network mode
|
|
||||||
qDebug("Network folder detected: %s", qPrintable(path));
|
|
||||||
qDebug("Using file polling mode instead of inotify...");
|
|
||||||
watched_folders << dir;
|
|
||||||
// Set up the watch timer
|
|
||||||
if (!watch_timer) {
|
|
||||||
watch_timer = new QTimer(this);
|
|
||||||
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders()));
|
|
||||||
watch_timer->start(WATCH_INTERVAL); // 5 sec
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
// Normal mode
|
|
||||||
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
|
||||||
QFileSystemWatcher::addPath(path);
|
|
||||||
scanLocalFolder(path);
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void removePath(const QString & path) {
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
QDir dir(path);
|
|
||||||
for (int i = 0; i < watched_folders.count(); ++i) {
|
|
||||||
if (QDir(watched_folders.at(i)) == dir) {
|
|
||||||
watched_folders.removeAt(i);
|
|
||||||
if (watched_folders.isEmpty())
|
|
||||||
delete watch_timer;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Normal mode
|
|
||||||
QFileSystemWatcher::removePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected slots:
|
|
||||||
void scanLocalFolder(QString path) {
|
|
||||||
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
|
||||||
QStringList torrents;
|
|
||||||
// Local folders scan
|
|
||||||
addTorrentsFromDir(QDir(path), torrents);
|
|
||||||
// Report detected torrent files
|
|
||||||
if (!torrents.empty()) {
|
|
||||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scanNetworkFolders() {
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
qDebug("scanNetworkFolders() called");
|
|
||||||
QStringList torrents;
|
|
||||||
// Network folders scan
|
|
||||||
foreach (const QDir &dir, watched_folders) {
|
|
||||||
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
|
||||||
addTorrentsFromDir(dir, torrents);
|
|
||||||
}
|
|
||||||
// Report detected torrent files
|
|
||||||
if (!torrents.empty()) {
|
|
||||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void processPartialTorrents() {
|
|
||||||
QStringList no_longer_partial;
|
|
||||||
|
|
||||||
// Check which torrents are still partial
|
|
||||||
foreach (const QString& torrent_path, m_partialTorrents.keys()) {
|
|
||||||
if (!QFile::exists(torrent_path)) {
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (fsutils::isValidTorrentFile(torrent_path)) {
|
|
||||||
no_longer_partial << torrent_path;
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
} else {
|
|
||||||
if (m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) {
|
|
||||||
m_partialTorrents.remove(torrent_path);
|
|
||||||
QFile::rename(torrent_path, torrent_path+".invalid");
|
|
||||||
} else {
|
|
||||||
m_partialTorrents[torrent_path]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the partial timer if necessary
|
|
||||||
if (m_partialTorrents.empty()) {
|
|
||||||
m_partialTorrentTimer->stop();
|
|
||||||
m_partialTorrentTimer->deleteLater();
|
|
||||||
qDebug("No longer any partial torrent.");
|
|
||||||
} else {
|
|
||||||
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
|
||||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
|
||||||
}
|
|
||||||
// Notify of new torrents
|
|
||||||
if (!no_longer_partial.isEmpty())
|
|
||||||
emit torrentsAdded(no_longer_partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void torrentsAdded(QStringList &pathList);
|
void torrentsAdded(const QStringList &pathList);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void scanLocalFolder(QString path);
|
||||||
|
void scanNetworkFolders();
|
||||||
|
void processPartialTorrents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startPartialTorrentTimer() {
|
void startPartialTorrentTimer();
|
||||||
Q_ASSERT(!m_partialTorrents.isEmpty());
|
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
|
||||||
if (!m_partialTorrentTimer) {
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
m_partialTorrentTimer = new QTimer();
|
static bool isNetworkFileSystem(QString path);
|
||||||
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
#endif
|
||||||
m_partialTorrentTimer->setSingleShot(true);
|
|
||||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
|
|
||||||
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
|
||||||
foreach (const QString &file, files) {
|
|
||||||
const QString file_abspath = dir.absoluteFilePath(file);
|
|
||||||
if (file_abspath.endsWith(".magnet")) {
|
|
||||||
QFile f(file_abspath);
|
|
||||||
if (f.open(QIODevice::ReadOnly)
|
|
||||||
&& !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) {
|
|
||||||
torrents << file_abspath;
|
|
||||||
}
|
|
||||||
} else if (fsutils::isValidTorrentFile(file_abspath)) {
|
|
||||||
torrents << file_abspath;
|
|
||||||
} else {
|
|
||||||
if (!m_partialTorrents.contains(file_abspath)) {
|
|
||||||
qDebug("Partial torrent detected at: %s", qPrintable(file_abspath));
|
|
||||||
qDebug("Delay the file's processing...");
|
|
||||||
m_partialTorrents.insert(file_abspath, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!m_partialTorrents.empty())
|
|
||||||
startPartialTorrentTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
QList<QDir> m_watchedFolders;
|
||||||
|
QPointer<QTimer> m_watchTimer;
|
||||||
|
#endif
|
||||||
|
QStringList m_filters;
|
||||||
|
// Partial torrents
|
||||||
|
QHash<QString, int> m_partialTorrents;
|
||||||
|
QPointer<QTimer> m_partialTorrentTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILESYSTEMWATCHER_H
|
#endif // FILESYSTEMWATCHER_H
|
||||||
|
|||||||
@@ -1,531 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2012 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QSettings>
|
|
||||||
#ifdef DISABLE_GUI
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#else
|
|
||||||
#include <QApplication>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include <CoreServices/CoreServices.h>
|
|
||||||
#include <Carbon/Carbon.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#elif defined(Q_OS_HAIKU)
|
|
||||||
#include <kernel/fs_info.h>
|
|
||||||
#else
|
|
||||||
#include <sys/vfs.h>
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#include <shlobj.h>
|
|
||||||
#include <winbase.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
#include <QDesktopServices>
|
|
||||||
#else
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a path to a string suitable for display.
|
|
||||||
* This function makes sure the directory separator used is consistent
|
|
||||||
* with the OS being run.
|
|
||||||
*/
|
|
||||||
QString fsutils::toNativePath(const QString& path) {
|
|
||||||
return QDir::toNativeSeparators(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::fromNativePath(const QString &path) {
|
|
||||||
return QDir::fromNativeSeparators(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file extension part of a file name.
|
|
||||||
*/
|
|
||||||
QString fsutils::fileExtension(const QString &filename) {
|
|
||||||
QString ext = QString(filename).remove(".!qB");
|
|
||||||
const int point_index = ext.lastIndexOf(".");
|
|
||||||
return (point_index >= 0) ? ext.mid(point_index + 1) : QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::fileName(const QString& file_path) {
|
|
||||||
QString path = fsutils::fromNativePath(file_path);
|
|
||||||
const int slash_index = path.lastIndexOf("/");
|
|
||||||
if (slash_index == -1)
|
|
||||||
return path;
|
|
||||||
return path.mid(slash_index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::folderName(const QString& file_path) {
|
|
||||||
QString path = fsutils::fromNativePath(file_path);
|
|
||||||
const int slash_index = path.lastIndexOf("/");
|
|
||||||
if (slash_index == -1)
|
|
||||||
return path;
|
|
||||||
return path.left(slash_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fsutils::isValidTorrentFile(const QString& torrent_path) {
|
|
||||||
try {
|
|
||||||
boost::intrusive_ptr<libtorrent::torrent_info> t = new torrent_info(fsutils::toNativePath(torrent_path).toUtf8().constData());
|
|
||||||
if (!t->is_valid() || t->num_files() == 0)
|
|
||||||
return false;
|
|
||||||
} catch(std::exception&) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an empty folder tree.
|
|
||||||
*
|
|
||||||
* This function will also remove .DS_Store files on Mac OS and
|
|
||||||
* Thumbs.db on Windows.
|
|
||||||
*/
|
|
||||||
bool fsutils::smartRemoveEmptyFolderTree(const QString& dir_path) {
|
|
||||||
qDebug() << Q_FUNC_INFO << dir_path;
|
|
||||||
if (dir_path.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QDir dir(dir_path);
|
|
||||||
if (!dir.exists())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Remove Files created by the OS
|
|
||||||
#if defined Q_OS_MAC
|
|
||||||
fsutils::forceRemove(dir_path + QLatin1String("/.DS_Store"));
|
|
||||||
#elif defined Q_OS_WIN
|
|
||||||
fsutils::forceRemove(dir_path + QLatin1String("/Thumbs.db"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QFileInfoList sub_files = dir.entryInfoList();
|
|
||||||
foreach (const QFileInfo& info, sub_files) {
|
|
||||||
QString sub_name = info.fileName();
|
|
||||||
if (sub_name == "." || sub_name == "..")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QString sub_path = info.absoluteFilePath();
|
|
||||||
qDebug() << Q_FUNC_INFO << "sub file: " << sub_path;
|
|
||||||
if (info.isDir()) {
|
|
||||||
if (!smartRemoveEmptyFolderTree(sub_path)) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (info.isHidden()) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path;
|
|
||||||
if (!fsutils::forceRemove(sub_path)) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path;
|
|
||||||
return QDir().rmdir(dir_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the file with the given file_path.
|
|
||||||
*
|
|
||||||
* This function will try to fix the file permissions before removing it.
|
|
||||||
*/
|
|
||||||
bool fsutils::forceRemove(const QString& file_path) {
|
|
||||||
QFile f(file_path);
|
|
||||||
if (!f.exists())
|
|
||||||
return true;
|
|
||||||
// Make sure we have read/write permissions
|
|
||||||
f.setPermissions(f.permissions()|QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
|
|
||||||
// Remove the file
|
|
||||||
return f.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of a file.
|
|
||||||
* If the file is a folder, it will compute its size based on its content.
|
|
||||||
*
|
|
||||||
* Returns -1 in case of error.
|
|
||||||
*/
|
|
||||||
qint64 fsutils::computePathSize(const QString& path) {
|
|
||||||
// Check if it is a file
|
|
||||||
QFileInfo fi(path);
|
|
||||||
if (!fi.exists()) return -1;
|
|
||||||
if (fi.isFile()) return fi.size();
|
|
||||||
// Compute folder size based on its content
|
|
||||||
qint64 size = 0;
|
|
||||||
foreach (const QFileInfo &subfi, QDir(path).entryInfoList(QDir::Dirs|QDir::Files)) {
|
|
||||||
if (subfi.fileName().startsWith(".")) continue;
|
|
||||||
if (subfi.isDir())
|
|
||||||
size += fsutils::computePathSize(subfi.absoluteFilePath());
|
|
||||||
else
|
|
||||||
size += subfi.size();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes deep comparison of two files to make sure they are identical.
|
|
||||||
*/
|
|
||||||
bool fsutils::sameFiles(const QString& path1, const QString& path2) {
|
|
||||||
QFile f1(path1), f2(path2);
|
|
||||||
if (!f1.exists() || !f2.exists()) return false;
|
|
||||||
if (f1.size() != f2.size()) return false;
|
|
||||||
if (!f1.open(QIODevice::ReadOnly)) return false;
|
|
||||||
if (!f2.open(QIODevice::ReadOnly)) {
|
|
||||||
f1.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool same = true;
|
|
||||||
while(!f1.atEnd() && !f2.atEnd()) {
|
|
||||||
if (f1.read(1024) != f2.read(1024)) {
|
|
||||||
same = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f1.close(); f2.close();
|
|
||||||
return same;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::updateLabelInSavePath(const QString& defaultSavePath, const QString& save_path, const QString& old_label, const QString& new_label) {
|
|
||||||
if (old_label == new_label) return fsutils::fromNativePath(save_path);
|
|
||||||
QString defaultPath = fsutils::fromNativePath(defaultSavePath);
|
|
||||||
QString path = fsutils::fromNativePath(save_path);
|
|
||||||
qDebug("UpdateLabelInSavePath(%s, %s, %s)", qPrintable(path), qPrintable(old_label), qPrintable(new_label));
|
|
||||||
if (!path.startsWith(defaultPath)) return path;
|
|
||||||
QString new_save_path = path;
|
|
||||||
new_save_path.remove(defaultPath);
|
|
||||||
QStringList path_parts = new_save_path.split("/", QString::SkipEmptyParts);
|
|
||||||
if (path_parts.empty()) {
|
|
||||||
if (!new_label.isEmpty())
|
|
||||||
path_parts << new_label;
|
|
||||||
} else {
|
|
||||||
if (old_label.isEmpty() || path_parts.first() != old_label) {
|
|
||||||
if (path_parts.first() != new_label)
|
|
||||||
path_parts.prepend(new_label);
|
|
||||||
} else {
|
|
||||||
if (new_label.isEmpty()) {
|
|
||||||
path_parts.removeAt(0);
|
|
||||||
} else {
|
|
||||||
if (path_parts.first() != new_label)
|
|
||||||
path_parts.replace(0, new_label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_save_path = defaultPath;
|
|
||||||
if (!new_save_path.endsWith("/")) new_save_path += "/";
|
|
||||||
new_save_path += path_parts.join("/");
|
|
||||||
qDebug("New save path is %s", qPrintable(new_save_path));
|
|
||||||
return new_save_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::toValidFileSystemName(QString filename) {
|
|
||||||
qDebug("toValidFSName: %s", qPrintable(filename));
|
|
||||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
|
||||||
filename.replace(regex, " ");
|
|
||||||
qDebug("toValidFSName, result: %s", qPrintable(filename));
|
|
||||||
return filename.trimmed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fsutils::isValidFileSystemName(const QString& filename) {
|
|
||||||
if (filename.isEmpty()) return false;
|
|
||||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
|
||||||
return !filename.contains(regex);
|
|
||||||
}
|
|
||||||
|
|
||||||
long long fsutils::freeDiskSpaceOnPath(QString path) {
|
|
||||||
if (path.isEmpty()) return -1;
|
|
||||||
QDir dir_path(path);
|
|
||||||
if (!dir_path.exists()) {
|
|
||||||
QStringList parts = path.split("/");
|
|
||||||
while (parts.size() > 1 && !QDir(parts.join("/")).exists()) {
|
|
||||||
parts.removeLast();
|
|
||||||
}
|
|
||||||
dir_path = QDir(parts.join("/"));
|
|
||||||
if (!dir_path.exists()) return -1;
|
|
||||||
}
|
|
||||||
Q_ASSERT(dir_path.exists());
|
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
|
||||||
unsigned long long available;
|
|
||||||
#ifdef Q_OS_HAIKU
|
|
||||||
const QString statfs_path = dir_path.path()+"/.";
|
|
||||||
dev_t device = dev_for_path (qPrintable(statfs_path));
|
|
||||||
if (device >= 0) {
|
|
||||||
fs_info info;
|
|
||||||
if(fs_stat_dev(device, &info)==B_OK){
|
|
||||||
available = ((unsigned long long)(info.free_blocks*info.block_size));
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
#else
|
|
||||||
struct statfs stats;
|
|
||||||
const QString statfs_path = dir_path.path()+"/.";
|
|
||||||
const int ret = statfs (qPrintable(statfs_path), &stats) ;
|
|
||||||
if (ret == 0) {
|
|
||||||
available = ((unsigned long long)stats.f_bavail) *
|
|
||||||
((unsigned long long)stats.f_bsize) ;
|
|
||||||
return available;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
|
|
||||||
PULARGE_INTEGER,
|
|
||||||
PULARGE_INTEGER,
|
|
||||||
PULARGE_INTEGER);
|
|
||||||
GetDiskFreeSpaceEx_t
|
|
||||||
pGetDiskFreeSpaceEx = (GetDiskFreeSpaceEx_t)::GetProcAddress
|
|
||||||
(
|
|
||||||
::GetModuleHandle(TEXT("kernel32.dll")),
|
|
||||||
"GetDiskFreeSpaceExW"
|
|
||||||
);
|
|
||||||
if ( pGetDiskFreeSpaceEx )
|
|
||||||
{
|
|
||||||
ULARGE_INTEGER bytesFree, bytesTotal;
|
|
||||||
unsigned long long *ret;
|
|
||||||
if (pGetDiskFreeSpaceEx((LPCTSTR)(fsutils::toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) {
|
|
||||||
ret = (unsigned long long*)&bytesFree;
|
|
||||||
return *ret;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::branchPath(const QString& file_path, QString* removed) {
|
|
||||||
QString ret = fsutils::fromNativePath(file_path);
|
|
||||||
if (ret.endsWith("/"))
|
|
||||||
ret.chop(1);
|
|
||||||
const int slashIndex = ret.lastIndexOf("/");
|
|
||||||
if (slashIndex >= 0) {
|
|
||||||
if (removed)
|
|
||||||
*removed = ret.mid(slashIndex + 1);
|
|
||||||
ret = ret.left(slashIndex);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fsutils::sameFileNames(const QString &first, const QString &second) {
|
|
||||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
|
||||||
return QString::compare(first, second, Qt::CaseSensitive) == 0;
|
|
||||||
#else
|
|
||||||
return QString::compare(first, second, Qt::CaseInsensitive) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::expandPath(const QString &path) {
|
|
||||||
QString ret = fsutils::fromNativePath(path.trimmed());
|
|
||||||
if (ret.isEmpty())
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return QDir::cleanPath(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::expandPathAbs(const QString& path) {
|
|
||||||
QString ret = fsutils::expandPath(path);
|
|
||||||
|
|
||||||
if (!QDir::isAbsolutePath(ret))
|
|
||||||
ret = QDir(ret).absolutePath();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::QDesktopServicesDataLocation() {
|
|
||||||
QString result;
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
LPWSTR path=new WCHAR[256];
|
|
||||||
if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
|
|
||||||
result = fsutils::fromNativePath(QString::fromWCharArray(path));
|
|
||||||
if (!QCoreApplication::applicationName().isEmpty())
|
|
||||||
result += QLatin1String("/") + qApp->applicationName();
|
|
||||||
#else
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
FSRef ref;
|
|
||||||
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
|
|
||||||
if (err)
|
|
||||||
return QString();
|
|
||||||
QString path;
|
|
||||||
QByteArray ba(2048, 0);
|
|
||||||
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
|
||||||
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
|
||||||
result += QLatin1Char('/') + qApp->applicationName();
|
|
||||||
#else
|
|
||||||
QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
|
|
||||||
if (xdgDataHome.isEmpty())
|
|
||||||
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
|
|
||||||
xdgDataHome += QLatin1String("/data/")
|
|
||||||
+ qApp->applicationName();
|
|
||||||
result = xdgDataHome;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
if (!result.endsWith("/"))
|
|
||||||
result += "/";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::QDesktopServicesCacheLocation() {
|
|
||||||
QString result;
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
|
||||||
result = QDesktopServicesDataLocation() + QLatin1String("cache");
|
|
||||||
#else
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
|
|
||||||
FSRef ref;
|
|
||||||
OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref);
|
|
||||||
if (err)
|
|
||||||
return QString();
|
|
||||||
QByteArray ba(2048, 0);
|
|
||||||
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
|
||||||
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
|
||||||
result += QLatin1Char('/') + qApp->applicationName();
|
|
||||||
#else
|
|
||||||
QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
|
|
||||||
if (xdgCacheHome.isEmpty())
|
|
||||||
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
|
|
||||||
xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName();
|
|
||||||
result = xdgCacheHome;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
if (!result.endsWith("/"))
|
|
||||||
result += "/";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::QDesktopServicesDownloadLocation() {
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
|
||||||
// as long as it stays WinXP like we do the same on OS/2
|
|
||||||
// TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads
|
|
||||||
// instead of hardcoding "Downloads"
|
|
||||||
// Unfortunately, this would break compatibility with WinXP
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
|
|
||||||
QCoreApplication::translate("fsutils", "Downloads"));
|
|
||||||
#else
|
|
||||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
|
|
||||||
QCoreApplication::translate("fsutils", "Downloads"));
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
|
||||||
QString save_path;
|
|
||||||
// Default save path on Linux
|
|
||||||
QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData());
|
|
||||||
if (config_path.isEmpty())
|
|
||||||
config_path = QDir::home().absoluteFilePath(".config");
|
|
||||||
|
|
||||||
QString user_dirs_file = config_path + "/user-dirs.dirs";
|
|
||||||
if (QFile::exists(user_dirs_file)) {
|
|
||||||
QSettings settings(user_dirs_file, QSettings::IniFormat);
|
|
||||||
// We need to force UTF-8 encoding here since this is not
|
|
||||||
// the default for Ini files.
|
|
||||||
settings.setIniCodec("UTF-8");
|
|
||||||
QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString();
|
|
||||||
if (!xdg_download_dir.isEmpty()) {
|
|
||||||
// Resolve $HOME environment variables
|
|
||||||
xdg_download_dir.replace("$HOME", QDir::homePath());
|
|
||||||
save_path = xdg_download_dir;
|
|
||||||
qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback
|
|
||||||
if (!save_path.isEmpty() && !QFile::exists(save_path)) {
|
|
||||||
QDir().mkpath(save_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save_path.isEmpty() || !QFile::exists(save_path)) {
|
|
||||||
save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
|
||||||
qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work";
|
|
||||||
}
|
|
||||||
|
|
||||||
return save_path;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
// TODO: How to support this on Mac OS X?
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fallback
|
|
||||||
return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::searchEngineLocation() {
|
|
||||||
QString folder = "nova";
|
|
||||||
if (misc::pythonVersion() >= 3)
|
|
||||||
folder = "nova3";
|
|
||||||
const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation()
|
|
||||||
+ folder);
|
|
||||||
QDir locationDir(location);
|
|
||||||
if (!locationDir.exists())
|
|
||||||
locationDir.mkpath(locationDir.absolutePath());
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::BTBackupLocation() {
|
|
||||||
const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation()
|
|
||||||
+ "BT_backup");
|
|
||||||
QDir locationDir(location);
|
|
||||||
if (!locationDir.exists())
|
|
||||||
locationDir.mkpath(locationDir.absolutePath());
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString fsutils::cacheLocation() {
|
|
||||||
QString location = fsutils::expandPathAbs(QDesktopServicesCacheLocation());
|
|
||||||
QDir locationDir(location);
|
|
||||||
if (!locationDir.exists())
|
|
||||||
locationDir.mkpath(locationDir.absolutePath());
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
@@ -42,30 +42,28 @@ QT_END_NAMESPACE
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class IRequestHandler;
|
||||||
|
|
||||||
class IRequestHandler;
|
class Connection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Connection)
|
||||||
|
|
||||||
class Connection : public QObject
|
public:
|
||||||
{
|
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||||
Q_OBJECT
|
~Connection();
|
||||||
Q_DISABLE_COPY(Connection)
|
|
||||||
|
|
||||||
public:
|
private slots:
|
||||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
void read();
|
||||||
~Connection();
|
|
||||||
|
|
||||||
private slots:
|
private:
|
||||||
void read();
|
static bool acceptsGzipEncoding(const QString &encoding);
|
||||||
|
void sendResponse(const Response &response);
|
||||||
private:
|
|
||||||
static bool acceptsGzipEncoding(const QString &encoding);
|
|
||||||
void sendResponse(const Response &response);
|
|
||||||
|
|
||||||
QTcpSocket *m_socket;
|
|
||||||
IRequestHandler *m_requestHandler;
|
|
||||||
QByteArray m_receivedData;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
QTcpSocket *m_socket;
|
||||||
|
IRequestHandler *m_requestHandler;
|
||||||
|
QByteArray m_receivedData;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_CONNECTION_H
|
#endif // HTTP_CONNECTION_H
|
||||||
|
|||||||
@@ -33,14 +33,12 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class IRequestHandler
|
||||||
class IRequestHandler
|
{
|
||||||
{
|
public:
|
||||||
public:
|
virtual ~IRequestHandler() {}
|
||||||
virtual ~IRequestHandler() {}
|
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_IREQUESTHANDLER_H
|
#endif // HTTP_IREQUESTHANDLER_H
|
||||||
|
|||||||
@@ -36,39 +36,37 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class RequestParser
|
||||||
class RequestParser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum ErrorCode
|
|
||||||
{
|
{
|
||||||
NoError = 0,
|
public:
|
||||||
IncompleteRequest,
|
enum ErrorCode
|
||||||
BadRequest
|
{
|
||||||
|
NoError = 0,
|
||||||
|
IncompleteRequest,
|
||||||
|
BadRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
// when result != NoError parsed request is undefined
|
||||||
|
// Warning! Header names are converted to lower-case.
|
||||||
|
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RequestParser(uint maxContentLength);
|
||||||
|
|
||||||
|
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
||||||
|
|
||||||
|
bool parseHttpHeader(const QByteArray &data);
|
||||||
|
bool parseStartingLine(const QString &line);
|
||||||
|
bool parseContent(const QByteArray &data);
|
||||||
|
bool parseFormData(const QByteArray &data);
|
||||||
|
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
||||||
|
|
||||||
|
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
||||||
|
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
||||||
|
|
||||||
|
const uint m_maxContentLength;
|
||||||
|
Request m_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
// when result != NoError parsed request is undefined
|
|
||||||
// Warning! Header names are converted to lower-case.
|
|
||||||
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
|
||||||
|
|
||||||
private:
|
|
||||||
RequestParser(uint maxContentLength);
|
|
||||||
|
|
||||||
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
|
||||||
|
|
||||||
bool parseHttpHeader(const QByteArray &data);
|
|
||||||
bool parseStartingLine(const QString &line);
|
|
||||||
bool parseContent(const QByteArray &data);
|
|
||||||
bool parseFormData(const QByteArray &data);
|
|
||||||
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
|
||||||
|
|
||||||
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
|
||||||
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
|
||||||
|
|
||||||
const uint m_maxContentLength;
|
|
||||||
Request m_request;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_REQUESTPARSER_H
|
#endif // HTTP_REQUESTPARSER_H
|
||||||
|
|||||||
@@ -34,27 +34,25 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class ResponseBuilder : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ResponseBuilder(QObject *parent = 0);
|
||||||
|
|
||||||
class ResponseBuilder : public QObject
|
protected:
|
||||||
{
|
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||||
public:
|
void header(const QString &name, const QString &value);
|
||||||
explicit ResponseBuilder(QObject *parent = 0);
|
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
void clear();
|
||||||
|
|
||||||
protected:
|
Response response() const;
|
||||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
|
||||||
void header(const QString &name, const QString &value);
|
|
||||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
|
||||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
Response response() const;
|
private:
|
||||||
|
void print_impl(const QByteArray &data, const QString &type);
|
||||||
private:
|
|
||||||
void print_impl(const QByteArray &data, const QString &type);
|
|
||||||
|
|
||||||
Response m_response;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
Response m_response;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_RESPONSEBUILDER_H
|
#endif // HTTP_RESPONSEBUILDER_H
|
||||||
|
|||||||
@@ -37,13 +37,11 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class ResponseGenerator
|
||||||
class ResponseGenerator
|
{
|
||||||
{
|
public:
|
||||||
public:
|
static QByteArray generate(Response response);
|
||||||
static QByteArray generate(Response response);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_RESPONSEGENERATOR_H
|
#endif // HTTP_RESPONSEGENERATOR_H
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
using namespace Http;
|
using namespace Http;
|
||||||
|
|
||||||
Server::Server(IRequestHandler *requestHandler, QObject* parent)
|
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||||
: QTcpServer(parent)
|
: QTcpServer(parent)
|
||||||
, m_requestHandler(requestHandler)
|
, m_requestHandler(requestHandler)
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
|
|||||||
@@ -41,40 +41,38 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
class IRequestHandler;
|
||||||
|
class Connection;
|
||||||
|
|
||||||
class IRequestHandler;
|
class Server : public QTcpServer
|
||||||
class Connection;
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(Server)
|
||||||
|
|
||||||
class Server : public QTcpServer
|
public:
|
||||||
{
|
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
||||||
Q_OBJECT
|
~Server();
|
||||||
Q_DISABLE_COPY(Server)
|
|
||||||
|
|
||||||
public:
|
#ifndef QT_NO_OPENSSL
|
||||||
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||||
~Server();
|
void disableHttps();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
private:
|
||||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||||
void disableHttps();
|
void incomingConnection(qintptr socketDescriptor);
|
||||||
#endif
|
#else
|
||||||
|
void incomingConnection(int socketDescriptor);
|
||||||
private:
|
#endif
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
|
||||||
void incomingConnection(qintptr socketDescriptor);
|
|
||||||
#else
|
|
||||||
void incomingConnection(int socketDescriptor);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
IRequestHandler *m_requestHandler;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
bool m_https;
|
|
||||||
QSslCertificate m_certificate;
|
|
||||||
QSslKey m_key;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
IRequestHandler *m_requestHandler;
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
bool m_https;
|
||||||
|
QSslCertificate m_certificate;
|
||||||
|
QSslKey m_key;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_SERVER_H
|
#endif // HTTP_SERVER_H
|
||||||
|
|||||||
@@ -37,59 +37,57 @@ typedef QMap<QString, QString> QStringMap;
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
const QString HEADER_SET_COOKIE = "Set-Cookie";
|
||||||
|
const QString HEADER_CONTENT_TYPE = "Content-Type";
|
||||||
|
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||||
|
const QString HEADER_CONTENT_LENGTH = "Content-Length";
|
||||||
|
const QString HEADER_CACHE_CONTROL = "Cache-Control";
|
||||||
|
|
||||||
const QString HEADER_SET_COOKIE = "Set-Cookie";
|
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
|
||||||
const QString HEADER_CONTENT_TYPE = "Content-Type";
|
const QString CONTENT_TYPE_GIF = "image/gif";
|
||||||
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
|
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
|
||||||
const QString HEADER_CONTENT_LENGTH = "Content-Length";
|
const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8";
|
||||||
const QString HEADER_CACHE_CONTROL = "Cache-Control";
|
const QString CONTENT_TYPE_PNG = "image/png";
|
||||||
|
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
|
||||||
|
|
||||||
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
|
struct Environment
|
||||||
const QString CONTENT_TYPE_GIF = "image/gif";
|
{
|
||||||
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
|
QHostAddress clientAddress;
|
||||||
const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8";
|
};
|
||||||
const QString CONTENT_TYPE_PNG = "image/png";
|
|
||||||
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
|
|
||||||
|
|
||||||
struct Environment
|
struct UploadedFile
|
||||||
{
|
{
|
||||||
QHostAddress clientAddress;
|
QString filename; // original filename
|
||||||
};
|
QString type; // MIME type
|
||||||
|
QByteArray data; // File data
|
||||||
|
};
|
||||||
|
|
||||||
struct UploadedFile
|
struct Request
|
||||||
{
|
{
|
||||||
QString filename; // original filename
|
QString method;
|
||||||
QString type; // MIME type
|
QString path;
|
||||||
QByteArray data; // File data
|
QStringMap headers;
|
||||||
};
|
QStringMap gets;
|
||||||
|
QStringMap posts;
|
||||||
|
QMap<QString, UploadedFile> files;
|
||||||
|
};
|
||||||
|
|
||||||
struct Request
|
struct ResponseStatus
|
||||||
{
|
{
|
||||||
QString method;
|
uint code;
|
||||||
QString path;
|
QString text;
|
||||||
QStringMap headers;
|
|
||||||
QStringMap gets;
|
|
||||||
QStringMap posts;
|
|
||||||
QMap<QString, UploadedFile> files;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResponseStatus
|
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||||
{
|
};
|
||||||
uint code;
|
|
||||||
QString text;
|
|
||||||
|
|
||||||
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
struct Response
|
||||||
};
|
{
|
||||||
|
ResponseStatus status;
|
||||||
struct Response
|
QStringMap headers;
|
||||||
{
|
QByteArray content;
|
||||||
ResponseStatus status;
|
|
||||||
QStringMap headers;
|
|
||||||
QByteArray content;
|
|
||||||
|
|
||||||
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HTTP_TYPES_H
|
#endif // HTTP_TYPES_H
|
||||||
|
|||||||
64
src/core/iconprovider.cpp
Normal file
64
src/core/iconprovider.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
IconProvider::IconProvider(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IconProvider::~IconProvider() {}
|
||||||
|
|
||||||
|
void IconProvider::initInstance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new IconProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconProvider::freeInstance()
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconProvider *IconProvider::instance()
|
||||||
|
{
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString IconProvider::getIconPath(const QString &iconId)
|
||||||
|
{
|
||||||
|
return ":/icons/oxygen/" + iconId + ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
IconProvider *IconProvider::m_instance = 0;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2011 Christophe Dumez
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,40 +25,31 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ICONPROVIDER_H
|
#ifndef ICONPROVIDER_H
|
||||||
#define ICONPROVIDER_H
|
#define ICONPROVIDER_H
|
||||||
|
|
||||||
#include <QIcon>
|
#include <QObject>
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class IconProvider
|
class QString;
|
||||||
|
|
||||||
|
class IconProvider : public QObject
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(IconProvider)
|
Q_DISABLE_COPY(IconProvider)
|
||||||
|
|
||||||
private:
|
|
||||||
explicit IconProvider();
|
|
||||||
static IconProvider* m_instance;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static IconProvider* instance();
|
static void initInstance();
|
||||||
static void drop();
|
static void freeInstance();
|
||||||
QIcon getIcon(const QString& iconId);
|
static IconProvider *instance();
|
||||||
QString getIconPath(const QString& iconId);
|
|
||||||
|
|
||||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
virtual QString getIconPath(const QString &iconId);
|
||||||
public:
|
|
||||||
void useSystemIconTheme(bool enable);
|
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
QIcon generateDifferentSizes(const QIcon& icon);
|
explicit IconProvider(QObject *parent = 0);
|
||||||
|
~IconProvider();
|
||||||
|
|
||||||
private:
|
static IconProvider *m_instance;
|
||||||
bool m_useSystemTheme;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ICONPROVIDER_H
|
#endif // ICONPROVIDER_H
|
||||||
@@ -44,15 +44,18 @@ Logger::Logger()
|
|||||||
|
|
||||||
Logger::~Logger() {}
|
Logger::~Logger() {}
|
||||||
|
|
||||||
Logger * Logger::instance()
|
Logger *Logger::instance()
|
||||||
{
|
{
|
||||||
if (!m_instance)
|
|
||||||
m_instance = new Logger;
|
|
||||||
|
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::drop()
|
void Logger::initInstance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new Logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::freeInstance()
|
||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ class Logger : public QObject
|
|||||||
Q_DISABLE_COPY(Logger)
|
Q_DISABLE_COPY(Logger)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Logger* instance();
|
static void initInstance();
|
||||||
static void drop();
|
static void freeInstance();
|
||||||
~Logger();
|
static Logger *instance();
|
||||||
|
|
||||||
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
|
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
@@ -72,6 +72,8 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Logger();
|
Logger();
|
||||||
|
~Logger();
|
||||||
|
|
||||||
static Logger* m_instance;
|
static Logger* m_instance;
|
||||||
QVector<Log::Msg> m_messages;
|
QVector<Log::Msg> m_messages;
|
||||||
QVector<Log::Peer> m_peers;
|
QVector<Log::Peer> m_peers;
|
||||||
|
|||||||
118
src/core/misc.h
118
src/core/misc.h
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MISC_H
|
|
||||||
#define MISC_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <ctime>
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QUrl>
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
#include <QIcon>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
#include <libtorrent/error_code.hpp>
|
|
||||||
|
|
||||||
namespace libtorrent {
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
class big_number;
|
|
||||||
typedef big_number sha1_hash;
|
|
||||||
#else
|
|
||||||
class sha1_hash;
|
|
||||||
#endif
|
|
||||||
struct lazy_entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
const qlonglong MAX_ETA = 8640000;
|
|
||||||
enum shutDownAction { NO_SHUTDOWN, SHUTDOWN_COMPUTER, SUSPEND_COMPUTER, HIBERNATE_COMPUTER };
|
|
||||||
|
|
||||||
/* Miscellaneaous functions that can be useful */
|
|
||||||
namespace misc
|
|
||||||
{
|
|
||||||
QString toQString(const std::string &str);
|
|
||||||
QString toQString(const char* str);
|
|
||||||
QString toQStringU(const std::string &str);
|
|
||||||
QString toQStringU(const char* str);
|
|
||||||
QString toQString(const libtorrent::sha1_hash &hash);
|
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
void shutdownComputer(shutDownAction action = SHUTDOWN_COMPUTER);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QString parseHtmlLinks(const QString &raw_text);
|
|
||||||
|
|
||||||
bool isUrl(const QString &s);
|
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
// Get screen center
|
|
||||||
QPoint screenCenter(QWidget *win);
|
|
||||||
#endif
|
|
||||||
int pythonVersion();
|
|
||||||
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
|
|
||||||
// use Binary prefix standards from IEC 60027-2
|
|
||||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
|
||||||
// value must be given in bytes
|
|
||||||
QString friendlyUnit(qreal val, bool is_speed = false);
|
|
||||||
bool isPreviewable(const QString& extension);
|
|
||||||
QString magnetUriToName(const QString& magnet_uri);
|
|
||||||
QString magnetUriToHash(const QString& magnet_uri);
|
|
||||||
QString bcLinkToMagnet(QString bc_link);
|
|
||||||
// Take a number of seconds and return an user-friendly
|
|
||||||
// time duration like "1d 2h 10m".
|
|
||||||
QString userFriendlyDuration(qlonglong seconds);
|
|
||||||
QString getUserIDString();
|
|
||||||
|
|
||||||
// Convert functions
|
|
||||||
QStringList toStringList(const QList<bool> &l);
|
|
||||||
QList<int> intListfromStringList(const QStringList &l);
|
|
||||||
QList<bool> boolListfromStringList(const QStringList &l);
|
|
||||||
|
|
||||||
QString toQString(time_t t);
|
|
||||||
QString accurateDoubleToString(const double &n, const int &precision);
|
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
bool naturalSort(QString left, QString right, bool& result);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Implements constant-time comparison to protect against timing attacks
|
|
||||||
// Taken from https://crackstation.net/hashing-security.htm
|
|
||||||
bool slowEquals(const QByteArray &a, const QByteArray &b);
|
|
||||||
void loadBencodedFile(const QString &filename, std::vector<char> &buffer, libtorrent::lazy_entry &entry, libtorrent::error_code &ec);
|
|
||||||
|
|
||||||
void msleep(unsigned long msecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
305
src/core/net/dnsupdater.cpp
Normal file
305
src/core/net/dnsupdater.cpp
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2011 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QStringList>
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "core/logger.h"
|
||||||
|
#include "dnsupdater.h"
|
||||||
|
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
DNSUpdater::DNSUpdater(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_state(OK)
|
||||||
|
, m_service(DNS::NONE)
|
||||||
|
{
|
||||||
|
updateCredentials();
|
||||||
|
|
||||||
|
// Load saved settings from previous session
|
||||||
|
const Preferences *const pref = Preferences::instance();
|
||||||
|
m_lastIPCheckTime = pref->getDNSLastUpd();
|
||||||
|
m_lastIP = QHostAddress(pref->getDNSLastIP());
|
||||||
|
|
||||||
|
// Start IP checking timer
|
||||||
|
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||||
|
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
||||||
|
m_ipCheckTimer.start();
|
||||||
|
|
||||||
|
// Check lastUpdate to avoid flooding
|
||||||
|
if (!m_lastIPCheckTime.isValid()
|
||||||
|
|| (m_lastIPCheckTime.secsTo(QDateTime::currentDateTime()) * 1000 > IP_CHECK_INTERVAL_MS)) {
|
||||||
|
checkPublicIP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSUpdater::~DNSUpdater()
|
||||||
|
{
|
||||||
|
// Save lastupdate time and last ip
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
pref->setDNSLastUpd(m_lastIPCheckTime);
|
||||||
|
pref->setDNSLastIP(m_lastIP.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::checkPublicIP()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_state == OK);
|
||||||
|
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||||
|
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipRequestFinished(QNetworkReply *)));
|
||||||
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(QUrl("http://checkip.dyndns.org"));
|
||||||
|
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
||||||
|
manager->get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
if (reply->error()) {
|
||||||
|
// Error
|
||||||
|
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Parse response
|
||||||
|
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
||||||
|
QString ret = reply->readAll();
|
||||||
|
if (ipregex.indexIn(ret) >= 0) {
|
||||||
|
QString ip_str = ipregex.cap(1);
|
||||||
|
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
||||||
|
QHostAddress new_ip(ip_str);
|
||||||
|
if (!new_ip.isNull()) {
|
||||||
|
if (m_lastIP != new_ip) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
||||||
|
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
||||||
|
m_lastIP = new_ip;
|
||||||
|
updateDNSService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean up
|
||||||
|
reply->deleteLater();
|
||||||
|
sender()->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::updateDNSService()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
// Prepare request
|
||||||
|
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||||
|
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipUpdateFinished(QNetworkReply *)));
|
||||||
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(getUpdateUrl());
|
||||||
|
request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org");
|
||||||
|
manager->get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl DNSUpdater::getUpdateUrl() const
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
#ifdef QT_NO_OPENSSL
|
||||||
|
url.setScheme("http");
|
||||||
|
#else
|
||||||
|
url.setScheme("https");
|
||||||
|
#endif
|
||||||
|
url.setUserName(m_username);
|
||||||
|
url.setPassword(m_password);
|
||||||
|
|
||||||
|
Q_ASSERT(!m_lastIP.isNull());
|
||||||
|
// Service specific
|
||||||
|
switch(m_service) {
|
||||||
|
case DNS::DYNDNS:
|
||||||
|
url.setHost("members.dyndns.org");
|
||||||
|
break;
|
||||||
|
case DNS::NOIP:
|
||||||
|
url.setHost("dynupdate.no-ip.com");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qWarning() << "Unrecognized Dynamic DNS service!";
|
||||||
|
Q_ASSERT(0);
|
||||||
|
}
|
||||||
|
url.setPath("/nic/update");
|
||||||
|
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||||
|
url.addQueryItem("hostname", m_domain);
|
||||||
|
url.addQueryItem("myip", m_lastIP.toString());
|
||||||
|
#else
|
||||||
|
QUrlQuery urlQuery(url);
|
||||||
|
urlQuery.addQueryItem("hostname", m_domain);
|
||||||
|
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
||||||
|
url.setQuery(urlQuery);
|
||||||
|
#endif
|
||||||
|
Q_ASSERT(url.isValid());
|
||||||
|
|
||||||
|
qDebug() << Q_FUNC_INFO << url.toString();
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
if (reply->error()) {
|
||||||
|
// Error
|
||||||
|
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Pase reply
|
||||||
|
processIPUpdateReply(reply->readAll());
|
||||||
|
}
|
||||||
|
// Clean up
|
||||||
|
reply->deleteLater();
|
||||||
|
sender()->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::processIPUpdateReply(const QString &reply)
|
||||||
|
{
|
||||||
|
Logger *const logger = Logger::instance();
|
||||||
|
qDebug() << Q_FUNC_INFO << reply;
|
||||||
|
QString code = reply.split(" ").first();
|
||||||
|
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
||||||
|
if (code == "good" || code == "nochg") {
|
||||||
|
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((code == "911") || (code == "dnserr")) {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
|
||||||
|
m_lastIP.clear();
|
||||||
|
// It will retry in 30 minutes because the timer was not stopped
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Everything bellow is an error, stop updating until the user updates something
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
|
m_lastIP.clear();
|
||||||
|
if (code == "nohost") {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code == "badauth") {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code == "badagent") {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
||||||
|
Log::CRITICAL);
|
||||||
|
m_state = FATAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code == "!donator") {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
||||||
|
Log::CRITICAL);
|
||||||
|
m_state = FATAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code == "abuse") {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
||||||
|
m_state = FATAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNSUpdater::updateCredentials()
|
||||||
|
{
|
||||||
|
if (m_state == FATAL) return;
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
Logger *const logger = Logger::instance();
|
||||||
|
bool change = false;
|
||||||
|
// Get DNS service information
|
||||||
|
if (m_service != pref->getDynDNSService()) {
|
||||||
|
m_service = pref->getDynDNSService();
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
if (m_domain != pref->getDynDomainName()) {
|
||||||
|
m_domain = pref->getDynDomainName();
|
||||||
|
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
||||||
|
if (domain_regex.indexIn(m_domain) < 0) {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||||
|
m_lastIP.clear();
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
if (m_username != pref->getDynDNSUsername()) {
|
||||||
|
m_username = pref->getDynDNSUsername();
|
||||||
|
if (m_username.length() < 4) {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
||||||
|
m_lastIP.clear();
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
if (m_password != pref->getDynDNSPassword()) {
|
||||||
|
m_password = pref->getDynDNSPassword();
|
||||||
|
if (m_password.length() < 4) {
|
||||||
|
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
||||||
|
m_lastIP.clear();
|
||||||
|
m_ipCheckTimer.stop();
|
||||||
|
m_state = INVALID_CREDS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_state == INVALID_CREDS) && change) {
|
||||||
|
m_state = OK; // Try again
|
||||||
|
m_ipCheckTimer.start();
|
||||||
|
checkPublicIP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl DNSUpdater::getRegistrationUrl(int service)
|
||||||
|
{
|
||||||
|
switch(service) {
|
||||||
|
case DNS::DYNDNS:
|
||||||
|
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
||||||
|
case DNS::NOIP:
|
||||||
|
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
||||||
|
default:
|
||||||
|
Q_ASSERT(0);
|
||||||
|
}
|
||||||
|
return QUrl();
|
||||||
|
}
|
||||||
@@ -36,46 +36,55 @@
|
|||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include "preferences.h"
|
#include "core/preferences.h"
|
||||||
|
|
||||||
/*!
|
namespace Net
|
||||||
* Based on http://www.dyndns.com/developers/specs/
|
{
|
||||||
*/
|
// Based on http://www.dyndns.com/developers/specs/
|
||||||
class DNSUpdater : public QObject
|
class DNSUpdater : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
|
||||||
explicit DNSUpdater(QObject *parent = 0);
|
|
||||||
~DNSUpdater();
|
|
||||||
static QUrl getRegistrationUrl(int service);
|
|
||||||
|
|
||||||
public slots:
|
public:
|
||||||
void updateCredentials();
|
explicit DNSUpdater(QObject *parent = 0);
|
||||||
|
~DNSUpdater();
|
||||||
|
|
||||||
private slots:
|
static QUrl getRegistrationUrl(int service);
|
||||||
void checkPublicIP();
|
|
||||||
void ipRequestFinished(QNetworkReply* reply);
|
|
||||||
void updateDNSService();
|
|
||||||
void ipUpdateFinished(QNetworkReply* reply);
|
|
||||||
|
|
||||||
private:
|
public slots:
|
||||||
QUrl getUpdateUrl() const;
|
void updateCredentials();
|
||||||
void processIPUpdateReply(const QString &reply);
|
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
QHostAddress m_lastIP;
|
void checkPublicIP();
|
||||||
QDateTime m_lastIPCheckTime;
|
void ipRequestFinished(QNetworkReply *reply);
|
||||||
QTimer m_ipCheckTimer;
|
void updateDNSService();
|
||||||
int m_state;
|
void ipUpdateFinished(QNetworkReply *reply);
|
||||||
// Service creds
|
|
||||||
DNS::Service m_service;
|
|
||||||
QString m_domain;
|
|
||||||
QString m_username;
|
|
||||||
QString m_password;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
QUrl getUpdateUrl() const;
|
||||||
enum State { OK, INVALID_CREDS, FATAL };
|
void processIPUpdateReply(const QString &reply);
|
||||||
};
|
|
||||||
|
private:
|
||||||
|
QHostAddress m_lastIP;
|
||||||
|
QDateTime m_lastIPCheckTime;
|
||||||
|
QTimer m_ipCheckTimer;
|
||||||
|
int m_state;
|
||||||
|
// Service creds
|
||||||
|
DNS::Service m_service;
|
||||||
|
QString m_domain;
|
||||||
|
QString m_username;
|
||||||
|
QString m_password;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
INVALID_CREDS,
|
||||||
|
FATAL
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif // DNSUPDATER_H
|
#endif // DNSUPDATER_H
|
||||||
290
src/core/net/downloadhandler.cpp
Normal file
290
src/core/net/downloadhandler.cpp
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <QNetworkCookie>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include "core/utils/fs.h"
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
#include "downloadmanager.h"
|
||||||
|
#include "downloadhandler.h"
|
||||||
|
|
||||||
|
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||||
|
static QByteArray gUncompress(Bytef *inData, uInt len);
|
||||||
|
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit)
|
||||||
|
: QObject(manager)
|
||||||
|
, m_reply(reply)
|
||||||
|
, m_manager(manager)
|
||||||
|
, m_sizeLimit(limit)
|
||||||
|
, m_url(reply->url().toString())
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler::~DownloadHandler()
|
||||||
|
{
|
||||||
|
if (m_reply)
|
||||||
|
delete m_reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns original url
|
||||||
|
QString DownloadHandler::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadHandler::processFinishedDownload()
|
||||||
|
{
|
||||||
|
QString url = m_reply->url().toString();
|
||||||
|
qDebug("Download finished: %s", qPrintable(url));
|
||||||
|
// Check if the request was successful
|
||||||
|
if (m_reply->error() != QNetworkReply::NoError) {
|
||||||
|
// Failure
|
||||||
|
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(m_reply->error())));
|
||||||
|
emit downloadFailed(m_url, errorCodeToString(m_reply->error()));
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check if the server ask us to redirect somewhere else
|
||||||
|
const QVariant redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||||
|
if (redirection.isValid()) {
|
||||||
|
// We should redirect
|
||||||
|
handleRedirection(redirection.toUrl());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Success
|
||||||
|
QString filePath;
|
||||||
|
if (saveToFile(filePath))
|
||||||
|
emit downloadFinished(m_url, filePath);
|
||||||
|
else
|
||||||
|
emit downloadFailed(m_url, tr("I/O Error"));
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
|
{
|
||||||
|
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
||||||
|
|
||||||
|
if (bytesTotal > 0) {
|
||||||
|
// Total number of bytes is available
|
||||||
|
if (bytesTotal > m_sizeLimit) {
|
||||||
|
m_reply->abort();
|
||||||
|
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bytesReceived > m_sizeLimit) {
|
||||||
|
m_reply->abort();
|
||||||
|
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadHandler::init()
|
||||||
|
{
|
||||||
|
m_reply->setParent(this);
|
||||||
|
if (m_sizeLimit > 0)
|
||||||
|
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
||||||
|
connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownloadHandler::saveToFile(QString &filePath)
|
||||||
|
{
|
||||||
|
QTemporaryFile *tmpfile = new QTemporaryFile;
|
||||||
|
if (!tmpfile->open()) {
|
||||||
|
delete tmpfile;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpfile->setAutoRemove(false);
|
||||||
|
filePath = tmpfile->fileName();
|
||||||
|
qDebug("Temporary filename is: %s", qPrintable(filePath));
|
||||||
|
if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) {
|
||||||
|
QByteArray replyData = m_reply->readAll();
|
||||||
|
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
|
||||||
|
// uncompress gzip reply
|
||||||
|
replyData = gUncompress(reinterpret_cast<unsigned char*>(replyData.data()), static_cast<uInt>(replyData.length()));
|
||||||
|
}
|
||||||
|
tmpfile->write(replyData);
|
||||||
|
tmpfile->close();
|
||||||
|
// XXX: tmpfile needs to be deleted on Windows before using the file
|
||||||
|
// or it will complain that the file is used by another process.
|
||||||
|
delete tmpfile;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete tmpfile;
|
||||||
|
Utils::Fs::forceRemove(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadHandler::handleRedirection(QUrl newUrl)
|
||||||
|
{
|
||||||
|
// Resolve relative urls
|
||||||
|
if (newUrl.isRelative())
|
||||||
|
newUrl = m_reply->url().resolved(newUrl);
|
||||||
|
|
||||||
|
const QString newUrlString = newUrl.toString();
|
||||||
|
qDebug("Redirecting from %s to %s", qPrintable(m_reply->url().toString()), qPrintable(newUrlString));
|
||||||
|
|
||||||
|
// Redirect to magnet workaround
|
||||||
|
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||||
|
qDebug("Magnet redirect detected.");
|
||||||
|
m_reply->abort();
|
||||||
|
emit redirectedToMagnet(m_url, newUrlString);
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_sizeLimit);
|
||||||
|
m_reply->deleteLater();
|
||||||
|
m_reply = tmp->m_reply;
|
||||||
|
m_sizeLimit = tmp->m_sizeLimit;
|
||||||
|
init();
|
||||||
|
tmp->m_reply = 0;
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString errorCodeToString(QNetworkReply::NetworkError status)
|
||||||
|
{
|
||||||
|
switch(status) {
|
||||||
|
case QNetworkReply::HostNotFoundError:
|
||||||
|
return QObject::tr("The remote host name was not found (invalid hostname)");
|
||||||
|
case QNetworkReply::OperationCanceledError:
|
||||||
|
return QObject::tr("The operation was canceled");
|
||||||
|
case QNetworkReply::RemoteHostClosedError:
|
||||||
|
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
||||||
|
case QNetworkReply::TimeoutError:
|
||||||
|
return QObject::tr("The connection to the remote server timed out");
|
||||||
|
case QNetworkReply::SslHandshakeFailedError:
|
||||||
|
return QObject::tr("SSL/TLS handshake failed");
|
||||||
|
case QNetworkReply::ConnectionRefusedError:
|
||||||
|
return QObject::tr("The remote server refused the connection");
|
||||||
|
case QNetworkReply::ProxyConnectionRefusedError:
|
||||||
|
return QObject::tr("The connection to the proxy server was refused");
|
||||||
|
case QNetworkReply::ProxyConnectionClosedError:
|
||||||
|
return QObject::tr("The proxy server closed the connection prematurely");
|
||||||
|
case QNetworkReply::ProxyNotFoundError:
|
||||||
|
return QObject::tr("The proxy host name was not found");
|
||||||
|
case QNetworkReply::ProxyTimeoutError:
|
||||||
|
return QObject::tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
||||||
|
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||||
|
return QObject::tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered");
|
||||||
|
case QNetworkReply::ContentAccessDenied:
|
||||||
|
return QObject::tr("The access to the remote content was denied (401)");
|
||||||
|
case QNetworkReply::ContentOperationNotPermittedError:
|
||||||
|
return QObject::tr("The operation requested on the remote content is not permitted");
|
||||||
|
case QNetworkReply::ContentNotFoundError:
|
||||||
|
return QObject::tr("The remote content was not found at the server (404)");
|
||||||
|
case QNetworkReply::AuthenticationRequiredError:
|
||||||
|
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
||||||
|
case QNetworkReply::ProtocolUnknownError:
|
||||||
|
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known");
|
||||||
|
case QNetworkReply::ProtocolInvalidOperationError:
|
||||||
|
return QObject::tr("The requested operation is invalid for this protocol");
|
||||||
|
case QNetworkReply::UnknownNetworkError:
|
||||||
|
return QObject::tr("An unknown network-related error was detected");
|
||||||
|
case QNetworkReply::UnknownProxyError:
|
||||||
|
return QObject::tr("An unknown proxy-related error was detected");
|
||||||
|
case QNetworkReply::UnknownContentError:
|
||||||
|
return QObject::tr("An unknown error related to the remote content was detected");
|
||||||
|
case QNetworkReply::ProtocolFailure:
|
||||||
|
return QObject::tr("A breakdown in protocol was detected");
|
||||||
|
default:
|
||||||
|
return QObject::tr("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray gUncompress(Bytef *inData, uInt len)
|
||||||
|
{
|
||||||
|
if (len <= 4) {
|
||||||
|
qWarning("gUncompress: Input data is truncated");
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray result;
|
||||||
|
|
||||||
|
z_stream strm;
|
||||||
|
static const int CHUNK_SIZE = 1024;
|
||||||
|
char out[CHUNK_SIZE];
|
||||||
|
|
||||||
|
/* allocate inflate state */
|
||||||
|
strm.zalloc = Z_NULL;
|
||||||
|
strm.zfree = Z_NULL;
|
||||||
|
strm.opaque = Z_NULL;
|
||||||
|
strm.avail_in = len;
|
||||||
|
strm.next_in = inData;
|
||||||
|
|
||||||
|
const int windowBits = 15;
|
||||||
|
const int ENABLE_ZLIB_GZIP = 32;
|
||||||
|
|
||||||
|
int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding
|
||||||
|
if (ret != Z_OK)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
// run inflate()
|
||||||
|
do {
|
||||||
|
strm.avail_out = CHUNK_SIZE;
|
||||||
|
strm.next_out = reinterpret_cast<unsigned char*>(out);
|
||||||
|
|
||||||
|
ret = inflate(&strm, Z_NO_FLUSH);
|
||||||
|
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
(void) inflateEnd(&strm);
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(out, CHUNK_SIZE - strm.avail_out);
|
||||||
|
}
|
||||||
|
while (!strm.avail_out);
|
||||||
|
|
||||||
|
// clean up and return
|
||||||
|
inflateEnd(&strm);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
76
src/core/net/downloadhandler.h
Normal file
76
src/core/net/downloadhandler.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NET_DOWNLOADHANDLER_H
|
||||||
|
#define NET_DOWNLOADHANDLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
class QUrl;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Net
|
||||||
|
{
|
||||||
|
class DownloadManager;
|
||||||
|
|
||||||
|
class DownloadHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit = 0);
|
||||||
|
~DownloadHandler();
|
||||||
|
|
||||||
|
QString url() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void downloadFinished(const QString &url, const QString &filePath);
|
||||||
|
void downloadFailed(const QString &url, const QString &reason);
|
||||||
|
void redirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void processFinishedDownload();
|
||||||
|
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
bool saveToFile(QString &filePath);
|
||||||
|
void handleRedirection(QUrl newUrl);
|
||||||
|
|
||||||
|
QNetworkReply *m_reply;
|
||||||
|
DownloadManager *m_manager;
|
||||||
|
qint64 m_sizeLimit;
|
||||||
|
QString m_url;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NET_DOWNLOADHANDLER_H
|
||||||
149
src/core/net/downloadmanager.cpp
Normal file
149
src/core/net/downloadmanager.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkProxy>
|
||||||
|
#include <QNetworkCookieJar>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkCookie>
|
||||||
|
#include <QSslError>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "core/preferences.h"
|
||||||
|
#include "downloadhandler.h"
|
||||||
|
#include "downloadmanager.h"
|
||||||
|
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
DownloadManager *DownloadManager::m_instance = 0;
|
||||||
|
|
||||||
|
DownloadManager::DownloadManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadManager::~DownloadManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::initInstance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new DownloadManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::freeInstance()
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadManager *DownloadManager::instance()
|
||||||
|
{
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler *DownloadManager::downloadUrl(const QString &url, qint64 limit)
|
||||||
|
{
|
||||||
|
// Update proxy settings
|
||||||
|
applyProxySettings();
|
||||||
|
|
||||||
|
// Process download request
|
||||||
|
qDebug("url is %s", qPrintable(url));
|
||||||
|
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
|
||||||
|
QNetworkRequest request(qurl);
|
||||||
|
|
||||||
|
// Spoof Firefox 3.5 user agent to avoid
|
||||||
|
// Web server banning
|
||||||
|
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5");
|
||||||
|
|
||||||
|
qDebug("Downloading %s...", request.url().toEncoded().data());
|
||||||
|
// accept gzip
|
||||||
|
request.setRawHeader("Accept-Encoding", "gzip");
|
||||||
|
return new DownloadHandler(m_networkManager.get(request), this, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QString &url) const
|
||||||
|
{
|
||||||
|
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
||||||
|
{
|
||||||
|
qDebug("Setting %d cookies for url: %s", cookieList.size(), qPrintable(url.toString()));
|
||||||
|
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadManager::applyProxySettings()
|
||||||
|
{
|
||||||
|
QNetworkProxy proxy;
|
||||||
|
const Preferences* const pref = Preferences::instance();
|
||||||
|
|
||||||
|
if (pref->isProxyEnabled()) {
|
||||||
|
// Proxy enabled
|
||||||
|
proxy.setHostName(pref->getProxyIp());
|
||||||
|
proxy.setPort(pref->getProxyPort());
|
||||||
|
// Default proxy type is HTTP, we must change if it is SOCKS5
|
||||||
|
const int proxyType = pref->getProxyType();
|
||||||
|
if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "using SOCKS proxy";
|
||||||
|
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug() << Q_FUNC_INFO << "using HTTP proxy";
|
||||||
|
proxy.setType(QNetworkProxy::HttpProxy);
|
||||||
|
}
|
||||||
|
// Authentication?
|
||||||
|
if (pref->isProxyAuthEnabled()) {
|
||||||
|
qDebug("Proxy requires authentication, authenticating");
|
||||||
|
proxy.setUser(pref->getProxyUsername());
|
||||||
|
proxy.setPassword(pref->getProxyPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
proxy.setType(QNetworkProxy::NoProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_networkManager.setProxy(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||||
|
{
|
||||||
|
Q_UNUSED(errors)
|
||||||
|
// Ignore all SSL errors
|
||||||
|
reply->ignoreSslErrors();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Ivan Sorokin
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,57 +25,52 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : vanyacpp@gmail.com
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ALERTDISPATCHER_H
|
#ifndef NET_DOWNLOADMANAGER_H
|
||||||
#define ALERTDISPATCHER_H
|
#define NET_DOWNLOADMANAGER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMutex>
|
#include <QNetworkAccessManager>
|
||||||
#include <QWaitCondition>
|
|
||||||
#include <QAtomicPointer>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
|
|
||||||
#include <vector>
|
QT_BEGIN_NAMESPACE
|
||||||
#include <memory>
|
class QNetworkReply;
|
||||||
|
class QNetworkCookie;
|
||||||
|
class QSslError;
|
||||||
|
class QUrl;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace libtorrent {
|
namespace Net
|
||||||
class session;
|
{
|
||||||
class alert;
|
class DownloadHandler;
|
||||||
|
|
||||||
|
class DownloadManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void initInstance();
|
||||||
|
static void freeInstance();
|
||||||
|
static DownloadManager *instance();
|
||||||
|
|
||||||
|
DownloadHandler *downloadUrl(const QString &url, qint64 limit = 0);
|
||||||
|
QList<QNetworkCookie> cookiesForUrl(const QString &url) const;
|
||||||
|
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
void ignoreSslErrors(QNetworkReply *,const QList<QSslError> &);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
DownloadManager(QObject *parent = 0);
|
||||||
|
~DownloadManager();
|
||||||
|
|
||||||
|
void applyProxySettings();
|
||||||
|
|
||||||
|
static DownloadManager *m_instance;
|
||||||
|
QNetworkAccessManager m_networkManager;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class QAlertDispatcher : public QObject {
|
#endif // NET_DOWNLOADMANAGER_H
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(QAlertDispatcher)
|
|
||||||
|
|
||||||
struct Tag;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QAlertDispatcher(libtorrent::session *session, QObject* parent);
|
|
||||||
~QAlertDispatcher();
|
|
||||||
|
|
||||||
void getPendingAlertsNoWait(std::vector<libtorrent::alert*>&);
|
|
||||||
void getPendingAlerts(std::vector<libtorrent::alert*>&, unsigned long time = ULONG_MAX);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void alertsReceived();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void dispatch(QSharedPointer<Tag>,
|
|
||||||
std::auto_ptr<libtorrent::alert>);
|
|
||||||
void enqueueToMainThread();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void deliverSignal();
|
|
||||||
|
|
||||||
private:
|
|
||||||
libtorrent::session *m_session;
|
|
||||||
QWaitCondition alerts_condvar;
|
|
||||||
std::vector<libtorrent::alert*> alerts;
|
|
||||||
QSharedPointer<Tag> current_tag;
|
|
||||||
bool event_posted;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // ALERTDISPATCHER_H
|
|
||||||
160
src/core/net/portforwarder.cpp
Normal file
160
src/core/net/portforwarder.cpp
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <libtorrent/session.hpp>
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
#include <libtorrent/upnp.hpp>
|
||||||
|
#include <libtorrent/natpmp.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "core/logger.h"
|
||||||
|
#include "core/preferences.h"
|
||||||
|
#include "portforwarder.h"
|
||||||
|
|
||||||
|
namespace libt = libtorrent;
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
PortForwarder::PortForwarder(libtorrent::session *provider, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_active(false)
|
||||||
|
, m_provider(provider)
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
, m_upnp(0)
|
||||||
|
, m_natpmp(0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
configure();
|
||||||
|
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PortForwarder::~PortForwarder()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::initInstance(libtorrent::session *const provider)
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new PortForwarder(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::freeInstance()
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PortForwarder *PortForwarder::instance()
|
||||||
|
{
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::addPort(qint16 port)
|
||||||
|
{
|
||||||
|
if (!m_mappedPorts.contains(port)) {
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
m_mappedPorts.insert(port, qMakePair(0, 0));
|
||||||
|
#else
|
||||||
|
m_mappedPorts.insert(port, 0);
|
||||||
|
#endif
|
||||||
|
if (m_active) {
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
m_mappedPorts[port].first = m_upnp->add_mapping(libt::upnp::tcp, port, port);
|
||||||
|
m_mappedPorts[port].second = m_natpmp->add_mapping(libt::natpmp::tcp, port, port);
|
||||||
|
#else
|
||||||
|
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::deletePort(qint16 port)
|
||||||
|
{
|
||||||
|
if (m_mappedPorts.contains(port)) {
|
||||||
|
if (m_active) {
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
m_upnp->delete_mapping(m_mappedPorts[port].first);
|
||||||
|
m_natpmp->delete_mapping(m_mappedPorts[port].second);
|
||||||
|
#else
|
||||||
|
m_provider->delete_port_mapping(m_mappedPorts[port]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
m_mappedPorts.remove(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::configure()
|
||||||
|
{
|
||||||
|
bool enable = Preferences::instance()->isUPnPEnabled();
|
||||||
|
if (m_active != enable) {
|
||||||
|
if (enable)
|
||||||
|
start();
|
||||||
|
else
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::start()
|
||||||
|
{
|
||||||
|
qDebug("Enabling UPnP / NAT-PMP");
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
m_upnp = m_provider->start_upnp();
|
||||||
|
m_natpmp = m_provider->start_natpmp();
|
||||||
|
foreach (qint16 port, m_mappedPorts.keys()) {
|
||||||
|
m_mappedPorts[port].first = m_upnp->add_mapping(libt::upnp::tcp, port, port);
|
||||||
|
m_mappedPorts[port].second = m_natpmp->add_mapping(libt::natpmp::tcp, port, port);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
m_provider->start_upnp();
|
||||||
|
m_provider->start_natpmp();
|
||||||
|
foreach (qint16 port, m_mappedPorts.keys())
|
||||||
|
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
|
||||||
|
#endif
|
||||||
|
m_active = true;
|
||||||
|
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarder::stop()
|
||||||
|
{
|
||||||
|
qDebug("Disabling UPnP / NAT-PMP");
|
||||||
|
m_provider->stop_upnp();
|
||||||
|
m_provider->stop_natpmp();
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
m_upnp = 0;
|
||||||
|
m_natpmp = 0;
|
||||||
|
#endif
|
||||||
|
m_active = false;
|
||||||
|
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
PortForwarder *PortForwarder::m_instance = 0;
|
||||||
84
src/core/net/portforwarder.h
Normal file
84
src/core/net/portforwarder.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NET_PORTFORWARDER_H
|
||||||
|
#define NET_PORTFORWARDER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHash>
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
class upnp;
|
||||||
|
class natpmp;
|
||||||
|
#endif
|
||||||
|
class session;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Net
|
||||||
|
{
|
||||||
|
class PortForwarder : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(PortForwarder)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void initInstance(libtorrent::session *const provider);
|
||||||
|
static void freeInstance();
|
||||||
|
static PortForwarder *instance();
|
||||||
|
|
||||||
|
void addPort(qint16 port);
|
||||||
|
void deletePort(qint16 port);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void configure();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit PortForwarder(libtorrent::session *const provider, QObject *parent = 0);
|
||||||
|
~PortForwarder();
|
||||||
|
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
bool m_active;
|
||||||
|
libtorrent::session *m_provider;
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10000
|
||||||
|
libtorrent::upnp *m_upnp;
|
||||||
|
libtorrent::natpmp *m_natpmp;
|
||||||
|
QHash<qint16, QPair<int, int> > m_mappedPorts;
|
||||||
|
#else
|
||||||
|
QHash<qint16, int> m_mappedPorts;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static PortForwarder *m_instance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NET_PORTFORWARDER_H
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -28,14 +28,9 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef REVERSERESOLUTION_H
|
|
||||||
#define REVERSERESOLUTION_H
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QCache>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include "misc.h"
|
#include <QString>
|
||||||
|
|
||||||
#include <boost/version.hpp>
|
#include <boost/version.hpp>
|
||||||
#if BOOST_VERSION < 103500
|
#if BOOST_VERSION < 103500
|
||||||
@@ -44,62 +39,56 @@
|
|||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "reverseresolution.h"
|
||||||
|
|
||||||
const int CACHE_SIZE = 500;
|
const int CACHE_SIZE = 500;
|
||||||
|
|
||||||
class ReverseResolution: public QObject {
|
using namespace Net;
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(ReverseResolution)
|
|
||||||
|
|
||||||
public:
|
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
||||||
explicit ReverseResolution(QObject* parent): QObject(parent) {
|
{
|
||||||
|
return (!hostname.isEmpty() && hostname != ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReverseResolution::ReverseResolution(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
m_cache.setMaxCost(CACHE_SIZE);
|
m_cache.setMaxCost(CACHE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ReverseResolution() {
|
ReverseResolution::~ReverseResolution()
|
||||||
|
{
|
||||||
qDebug("Deleting host name resolver...");
|
qDebug("Deleting host name resolver...");
|
||||||
}
|
}
|
||||||
|
|
||||||
void resolve(const QString &ip) {
|
void ReverseResolution::resolve(const QString &ip)
|
||||||
|
{
|
||||||
if (m_cache.contains(ip)) {
|
if (m_cache.contains(ip)) {
|
||||||
const QString& hostname = *m_cache.object(ip);
|
const QString &hostname = *m_cache.object(ip);
|
||||||
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
|
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
|
||||||
if (isUsefulHostName(hostname, ip))
|
if (isUsefulHostName(hostname, ip))
|
||||||
emit ip_resolved(ip, hostname);
|
emit ipResolved(ip, hostname);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Actually resolve the ip
|
else {
|
||||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
// Actually resolve the ip
|
||||||
}
|
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
void ReverseResolution::hostResolved(const QHostInfo &host)
|
||||||
void ip_resolved(const QString &ip, const QString &hostname);
|
{
|
||||||
|
const QString &ip = m_lookups.take(host.lookupId());
|
||||||
private slots:
|
|
||||||
void hostResolved(const QHostInfo& host) {
|
|
||||||
const QString& ip = m_lookups.take(host.lookupId());
|
|
||||||
Q_ASSERT(!ip.isNull());
|
Q_ASSERT(!ip.isNull());
|
||||||
|
|
||||||
if (host.error() != QHostInfo::NoError) {
|
if (host.error() != QHostInfo::NoError) {
|
||||||
qDebug() << "DNS Reverse resolution error: " << host.errorString();
|
qDebug() << "DNS Reverse resolution error: " << host.errorString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& hostname = host.hostName();
|
const QString &hostname = host.hostName();
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
||||||
m_cache.insert(ip, new QString(hostname));
|
m_cache.insert(ip, new QString(hostname));
|
||||||
if (isUsefulHostName(hostname, ip))
|
if (isUsefulHostName(hostname, ip))
|
||||||
emit ip_resolved(ip, hostname);
|
emit ipResolved(ip, hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
static inline bool isUsefulHostName(const QString& hostname, const QString& ip) {
|
|
||||||
return (!hostname.isEmpty() && hostname != ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int /* LookupID */, QString /* IP */> m_lookups;
|
|
||||||
QCache<QString /* IP */, QString /* HostName */> m_cache;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // REVERSERESOLUTION_H
|
|
||||||
67
src/core/net/reverseresolution.h
Normal file
67
src/core/net/reverseresolution.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NET_REVERSERESOLUTION_H
|
||||||
|
#define NET_REVERSERESOLUTION_H
|
||||||
|
|
||||||
|
#include <QCache>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QHostInfo;
|
||||||
|
class QString;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Net
|
||||||
|
{
|
||||||
|
class ReverseResolution : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(ReverseResolution)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReverseResolution(QObject *parent = 0);
|
||||||
|
~ReverseResolution();
|
||||||
|
|
||||||
|
void resolve(const QString &ip);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ipResolved(const QString &ip, const QString &hostname);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void hostResolved(const QHostInfo &host);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<int /* LookupID */, QString /* IP */> m_lookups;
|
||||||
|
QCache<QString /* IP */, QString /* HostName */> m_cache;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // NET_REVERSERESOLUTION_H
|
||||||
502
src/core/net/smtp.cpp
Normal file
502
src/core/net/smtp.cpp
Normal file
@@ -0,0 +1,502 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2011 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code is based on QxtSmtp from libqxt (http://libqxt.org)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "smtp.h"
|
||||||
|
#include "core/preferences.h"
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
#include <QSslSocket>
|
||||||
|
#else
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#endif
|
||||||
|
#include <QTextCodec>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QHostInfo>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const short DEFAULT_PORT = 25;
|
||||||
|
const short DEFAULT_PORT_SSL = 465;
|
||||||
|
|
||||||
|
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
|
||||||
|
{
|
||||||
|
const int blockSize = 64; // HMAC-MD5 block size
|
||||||
|
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
||||||
|
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
||||||
|
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
||||||
|
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
||||||
|
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
||||||
|
|
||||||
|
for (int i = 0; i < key.length(); i++) {
|
||||||
|
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
||||||
|
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
||||||
|
}
|
||||||
|
|
||||||
|
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
|
||||||
|
QByteArray total = outerPadding;
|
||||||
|
QByteArray part = innerPadding;
|
||||||
|
part.append(msg);
|
||||||
|
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
|
||||||
|
return QCryptographicHash::hash(total, QCryptographicHash::Md5);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray determineFQDN()
|
||||||
|
{
|
||||||
|
QString hostname = QHostInfo::localHostName();
|
||||||
|
if (hostname.isEmpty())
|
||||||
|
hostname = "localhost";
|
||||||
|
|
||||||
|
return hostname.toLocal8Bit();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
using namespace Net;
|
||||||
|
|
||||||
|
Smtp::Smtp(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_state(Init)
|
||||||
|
, m_useSsl(false)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
m_socket = new QSslSocket(this);
|
||||||
|
#else
|
||||||
|
m_socket = new QTcpSocket(this);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
|
||||||
|
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
||||||
|
|
||||||
|
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
||||||
|
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
||||||
|
== "750c783e6ab0b503eaa86e310a5db738");
|
||||||
|
Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), "Hi There").toHex()
|
||||||
|
== "9294727a3638bb1c13f48ef8158bfc9d");
|
||||||
|
}
|
||||||
|
|
||||||
|
Smtp::~Smtp()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
||||||
|
{
|
||||||
|
const Preferences* const pref = Preferences::instance();
|
||||||
|
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
||||||
|
m_message = "";
|
||||||
|
m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
||||||
|
m_message += encodeMimeHeader("From", from, latin1);
|
||||||
|
m_message += encodeMimeHeader("Subject", subject, latin1);
|
||||||
|
m_message += encodeMimeHeader("To", to, latin1);
|
||||||
|
m_message += "MIME-Version: 1.0\r\n";
|
||||||
|
m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||||
|
m_message += "Content-Transfer-Encoding: base64\r\n";
|
||||||
|
m_message += "\r\n";
|
||||||
|
// Encode the body in base64
|
||||||
|
QString crlf_body = body;
|
||||||
|
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
||||||
|
int ct = b.length();
|
||||||
|
for (int i = 0; i < ct; i += 78)
|
||||||
|
m_message += b.mid(i, 78);
|
||||||
|
m_from = from;
|
||||||
|
m_rcpt = to;
|
||||||
|
// Authentication
|
||||||
|
if (pref->getMailNotificationSMTPAuth()) {
|
||||||
|
m_username = pref->getMailNotificationSMTPUsername();
|
||||||
|
m_password = pref->getMailNotificationSMTPPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to SMTP server
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
if (pref->getMailNotificationSMTPSSL()) {
|
||||||
|
m_socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL);
|
||||||
|
m_useSsl = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||||
|
m_useSsl = false;
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::readyRead()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
// SMTP is line-oriented
|
||||||
|
m_buffer += m_socket->readAll();
|
||||||
|
while (true) {
|
||||||
|
int pos = m_buffer.indexOf("\r\n");
|
||||||
|
if (pos < 0) return; // Loop exit condition
|
||||||
|
QByteArray line = m_buffer.left(pos);
|
||||||
|
m_buffer = m_buffer.mid(pos + 2);
|
||||||
|
qDebug() << "Response line:" << line;
|
||||||
|
// Extract reponse code
|
||||||
|
QByteArray code = line.left(3);
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case Init: {
|
||||||
|
if (code[0] == '2') {
|
||||||
|
// The server may send a multiline greeting/INIT/220 response.
|
||||||
|
// We wait until it finishes.
|
||||||
|
if (line[3] != ' ')
|
||||||
|
break;
|
||||||
|
// Connection was successful
|
||||||
|
ehlo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError("Connection failed, unrecognized reply: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EhloSent:
|
||||||
|
case HeloSent:
|
||||||
|
case EhloGreetReceived:
|
||||||
|
parseEhloResponse(code, line[3] != ' ', line.mid(4));
|
||||||
|
break;
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
case StartTLSSent:
|
||||||
|
if (code == "220") {
|
||||||
|
m_socket->startClientEncryption();
|
||||||
|
ehlo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
authenticate();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case AuthRequestSent:
|
||||||
|
case AuthUsernameSent:
|
||||||
|
if (m_authType == AuthPlain) authPlain();
|
||||||
|
else if (m_authType == AuthLogin) authLogin();
|
||||||
|
else authCramMD5(line.mid(4));
|
||||||
|
break;
|
||||||
|
case AuthSent:
|
||||||
|
case Authenticated:
|
||||||
|
if (code[0] == '2') {
|
||||||
|
qDebug() << "Sending <mail from>...";
|
||||||
|
m_socket->write("mail from:<" + m_from.toLatin1() + ">\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = Rcpt;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Authentication failed!
|
||||||
|
logError("Authentication failed, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Rcpt:
|
||||||
|
if (code[0] == '2') {
|
||||||
|
m_socket->write("rcpt to:<" + m_rcpt.toLatin1() + ">\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = Data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError("<mail from> was rejected by server, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Data:
|
||||||
|
if (code[0] == '2') {
|
||||||
|
m_socket->write("data\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = Body;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError("<Rcpt to> was rejected by server, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Body:
|
||||||
|
if (code[0] == '3') {
|
||||||
|
m_socket->write(m_message + "\r\n.\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = Quit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError("<data> was rejected by server, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Quit:
|
||||||
|
if (code[0] == '2') {
|
||||||
|
m_socket->write("QUIT\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
// here, we just close.
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logError("Message was rejected by the server, error: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qDebug() << "Disconnecting from host";
|
||||||
|
m_socket->disconnectFromHost();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix)
|
||||||
|
{
|
||||||
|
QByteArray rv = "";
|
||||||
|
QByteArray line = key.toLatin1() + ": ";
|
||||||
|
if (!prefix.isEmpty()) line += prefix;
|
||||||
|
if (!value.contains("=?") && latin1->canEncode(value)) {
|
||||||
|
bool firstWord = true;
|
||||||
|
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
|
||||||
|
if (line.size() > 78) {
|
||||||
|
rv = rv + line + "\r\n";
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
if (firstWord)
|
||||||
|
line += word;
|
||||||
|
else
|
||||||
|
line += " " + word;
|
||||||
|
firstWord = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The text cannot be losslessly encoded as Latin-1. Therefore, we
|
||||||
|
// must use base64 encoding.
|
||||||
|
QByteArray utf8 = value.toUtf8();
|
||||||
|
// Use base64 encoding
|
||||||
|
QByteArray base64 = utf8.toBase64();
|
||||||
|
int ct = base64.length();
|
||||||
|
line += "=?utf-8?b?";
|
||||||
|
for (int i = 0; i < ct; i += 4) {
|
||||||
|
/*if (line.length() > 72) {
|
||||||
|
rv += line + "?\n\r";
|
||||||
|
line = " =?utf-8?b?";
|
||||||
|
}*/
|
||||||
|
line = line + base64.mid(i, 4);
|
||||||
|
}
|
||||||
|
line += "?="; // end encoded-word atom
|
||||||
|
}
|
||||||
|
return rv + line + "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::ehlo()
|
||||||
|
{
|
||||||
|
QByteArray address = determineFQDN();
|
||||||
|
m_socket->write("ehlo " + address + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = EhloSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::helo()
|
||||||
|
{
|
||||||
|
QByteArray address = determineFQDN();
|
||||||
|
m_socket->write("helo " + address + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = HeloSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QString &line)
|
||||||
|
{
|
||||||
|
if (code != "250") {
|
||||||
|
// Error
|
||||||
|
if (m_state == EhloSent) {
|
||||||
|
// try to send HELO instead of EHLO
|
||||||
|
qDebug() << "EHLO failed, trying HELO instead...";
|
||||||
|
helo();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Both EHLO and HELO failed, chances are this is NOT
|
||||||
|
// a SMTP server
|
||||||
|
logError("Both EHLO and HELO failed, msg: "+line);
|
||||||
|
m_state = Close;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state != EhloGreetReceived) {
|
||||||
|
if (!continued) {
|
||||||
|
// greeting only, no extensions
|
||||||
|
qDebug() << "No extension";
|
||||||
|
m_state = EhloDone;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// greeting followed by extensions
|
||||||
|
m_state = EhloGreetReceived;
|
||||||
|
qDebug () << "EHLO greet received";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
|
||||||
|
<< line.section(' ', 1);
|
||||||
|
m_extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
|
||||||
|
if (!continued)
|
||||||
|
m_state = EhloDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_state != EhloDone) return;
|
||||||
|
|
||||||
|
if (m_extensions.contains("STARTTLS") && m_useSsl) {
|
||||||
|
qDebug() << "STARTTLS";
|
||||||
|
startTLS();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
authenticate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::authenticate()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
if (!m_extensions.contains("AUTH") ||
|
||||||
|
m_username.isEmpty() || m_password.isEmpty()) {
|
||||||
|
// Skip authentication
|
||||||
|
qDebug() << "Skipping authentication...";
|
||||||
|
m_state = Authenticated;
|
||||||
|
// At this point the server will not send any response
|
||||||
|
// So fill the buffer with a fake one to pass the tests
|
||||||
|
// in readyRead()
|
||||||
|
m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// AUTH extension is supported, check which
|
||||||
|
// authentication modes are supported by
|
||||||
|
// the server
|
||||||
|
QStringList auth = m_extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
|
||||||
|
if (auth.contains("CRAM-MD5")) {
|
||||||
|
qDebug() << "Using CRAM-MD5 authentication...";
|
||||||
|
authCramMD5();
|
||||||
|
}
|
||||||
|
else if (auth.contains("PLAIN")) {
|
||||||
|
qDebug() << "Using PLAIN authentication...";
|
||||||
|
authPlain();
|
||||||
|
}
|
||||||
|
else if (auth.contains("LOGIN")) {
|
||||||
|
qDebug() << "Using LOGIN authentication...";
|
||||||
|
authLogin();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Skip authentication
|
||||||
|
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||||
|
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
||||||
|
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
||||||
|
m_state = Authenticated;
|
||||||
|
// At this point the server will not send any response
|
||||||
|
// So fill the buffer with a fake one to pass the tests
|
||||||
|
// in readyRead()
|
||||||
|
m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::startTLS()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
m_socket->write("starttls\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = StartTLSSent;
|
||||||
|
#else
|
||||||
|
authenticate();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::authCramMD5(const QByteArray& challenge)
|
||||||
|
{
|
||||||
|
if (m_state != AuthRequestSent) {
|
||||||
|
m_socket->write("auth cram-md5\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_authType = AuthCramMD5;
|
||||||
|
m_state = AuthRequestSent;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QByteArray response = m_username.toLatin1() + ' '
|
||||||
|
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
|
||||||
|
m_socket->write(response.toBase64() + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = AuthSent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::authPlain()
|
||||||
|
{
|
||||||
|
if (m_state != AuthRequestSent) {
|
||||||
|
m_authType = AuthPlain;
|
||||||
|
// Prepare Auth string
|
||||||
|
QByteArray auth;
|
||||||
|
auth += '\0';
|
||||||
|
auth += m_username.toLatin1();
|
||||||
|
qDebug() << "username: " << m_username.toLatin1();
|
||||||
|
auth += '\0';
|
||||||
|
auth += m_password.toLatin1();
|
||||||
|
qDebug() << "password: " << m_password.toLatin1();
|
||||||
|
// Send it
|
||||||
|
m_socket->write("auth plain "+ auth.toBase64() + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = AuthSent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::authLogin()
|
||||||
|
{
|
||||||
|
if ((m_state != AuthRequestSent) && (m_state != AuthUsernameSent)) {
|
||||||
|
m_socket->write("auth login\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_authType = AuthLogin;
|
||||||
|
m_state = AuthRequestSent;
|
||||||
|
}
|
||||||
|
else if (m_state == AuthRequestSent) {
|
||||||
|
m_socket->write(m_username.toLatin1().toBase64() + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = AuthUsernameSent;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_socket->write(m_password.toLatin1().toBase64() + "\r\n");
|
||||||
|
m_socket->flush();
|
||||||
|
m_state = AuthSent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Smtp::logError(const QString &msg)
|
||||||
|
{
|
||||||
|
qDebug() << "Email Notification Error:" << msg;
|
||||||
|
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2011 Christophe Dumez
|
* Copyright (C) 2011 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -50,50 +50,76 @@ class QTcpSocket;
|
|||||||
class QTextCodec;
|
class QTextCodec;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class Smtp : public QObject {
|
namespace Net
|
||||||
Q_OBJECT
|
{
|
||||||
|
class Smtp : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Smtp(QObject *parent = 0);
|
Smtp(QObject *parent = 0);
|
||||||
~Smtp();
|
~Smtp();
|
||||||
void sendMail(const QString &from, const QString &to, const QString &subject, const QString &body);
|
|
||||||
|
|
||||||
private slots:
|
void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body);
|
||||||
void readyRead();
|
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray());
|
void readyRead();
|
||||||
void ehlo();
|
|
||||||
void helo();
|
|
||||||
void parseEhloResponse(const QByteArray& code, bool continued, const QString& line);
|
|
||||||
void authenticate();
|
|
||||||
void startTLS();
|
|
||||||
void authCramMD5(const QByteArray& challenge = QByteArray());
|
|
||||||
void authPlain();
|
|
||||||
void authLogin();
|
|
||||||
void logError(const QString &msg);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum states { Rcpt, EhloSent, HeloSent, EhloDone, EhloGreetReceived, AuthRequestSent, AuthSent,
|
enum States
|
||||||
AuthUsernameSent, Authenticated, StartTLSSent, Data, Init, Body, Quit, Close };
|
{
|
||||||
enum AuthType { AuthPlain, AuthLogin, AuthCramMD5 };
|
Rcpt,
|
||||||
|
EhloSent,
|
||||||
|
HeloSent,
|
||||||
|
EhloDone,
|
||||||
|
EhloGreetReceived,
|
||||||
|
AuthRequestSent,
|
||||||
|
AuthSent,
|
||||||
|
AuthUsernameSent,
|
||||||
|
Authenticated,
|
||||||
|
StartTLSSent,
|
||||||
|
Data,
|
||||||
|
Init,
|
||||||
|
Body,
|
||||||
|
Quit,
|
||||||
|
Close
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
enum AuthType
|
||||||
QByteArray message;
|
{
|
||||||
|
AuthPlain,
|
||||||
|
AuthLogin,
|
||||||
|
AuthCramMD5
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix = QByteArray());
|
||||||
|
void ehlo();
|
||||||
|
void helo();
|
||||||
|
void parseEhloResponse(const QByteArray &code, bool continued, const QString &line);
|
||||||
|
void authenticate();
|
||||||
|
void startTLS();
|
||||||
|
void authCramMD5(const QByteArray &challenge = QByteArray());
|
||||||
|
void authPlain();
|
||||||
|
void authLogin();
|
||||||
|
void logError(const QString &msg);
|
||||||
|
|
||||||
|
QByteArray m_message;
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
QSslSocket *socket;
|
QSslSocket *m_socket;
|
||||||
#else
|
#else
|
||||||
QTcpSocket *socket;
|
QTcpSocket *m_socket;
|
||||||
#endif
|
#endif
|
||||||
QString from;
|
QString m_from;
|
||||||
QString rcpt;
|
QString m_rcpt;
|
||||||
QString response;
|
QString m_response;
|
||||||
int state;
|
int m_state;
|
||||||
QHash<QString, QString> extensions;
|
QHash<QString, QString> m_extensions;
|
||||||
QByteArray buffer;
|
QByteArray m_buffer;
|
||||||
bool use_ssl;
|
bool m_useSsl;
|
||||||
AuthType authType;
|
AuthType m_authType;
|
||||||
QString username;
|
QString m_username;
|
||||||
QString password;
|
QString m_password;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -50,8 +50,8 @@
|
|||||||
#include <winreg.h>
|
#include <winreg.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "misc.h"
|
#include "core/utils/fs.h"
|
||||||
#include "fs_utils.h"
|
#include "core/utils/misc.h"
|
||||||
|
|
||||||
|
|
||||||
Preferences* Preferences::m_instance = 0;
|
Preferences* Preferences::m_instance = 0;
|
||||||
@@ -116,15 +116,18 @@ Preferences::~Preferences()
|
|||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
Preferences * Preferences::instance()
|
Preferences *Preferences::instance()
|
||||||
{
|
{
|
||||||
if (!m_instance)
|
|
||||||
m_instance = new Preferences;
|
|
||||||
|
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::drop()
|
void Preferences::initInstance()
|
||||||
|
{
|
||||||
|
if (!m_instance)
|
||||||
|
m_instance = new Preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::freeInstance()
|
||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
@@ -132,12 +135,11 @@ void Preferences::drop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::save()
|
bool Preferences::save()
|
||||||
{
|
{
|
||||||
QReadLocker locker(&lock);
|
QReadLocker locker(&lock);
|
||||||
|
|
||||||
if (!dirty)
|
if (!dirty) return false;
|
||||||
return;
|
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
// QSettings delete the file before writing it out. This can result in problems
|
// QSettings delete the file before writing it out. This can result in problems
|
||||||
@@ -160,20 +162,20 @@ void Preferences::save()
|
|||||||
settings->sync(); // Important to get error status
|
settings->sync(); // Important to get error status
|
||||||
if (settings->status() == QSettings::AccessError) {
|
if (settings->status() == QSettings::AccessError) {
|
||||||
delete settings;
|
delete settings;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
QString new_path = settings->fileName();
|
QString new_path = settings->fileName();
|
||||||
delete settings;
|
delete settings;
|
||||||
QString final_path = new_path;
|
QString final_path = new_path;
|
||||||
int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||||
final_path.remove(index, 4);
|
final_path.remove(index, 4);
|
||||||
fsutils::forceRemove(final_path);
|
Utils::Fs::forceRemove(final_path);
|
||||||
QFile::rename(new_path, final_path);
|
QFile::rename(new_path, final_path);
|
||||||
#else
|
#else
|
||||||
delete settings;
|
delete settings;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
emit changed();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const
|
const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const
|
||||||
@@ -345,7 +347,7 @@ void Preferences::setWinStartup(bool b)
|
|||||||
{
|
{
|
||||||
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||||
if (b) {
|
if (b) {
|
||||||
const QString bin_path = "\"" + fsutils::toNativePath(qApp->applicationFilePath()) + "\"";
|
const QString bin_path = "\"" + Utils::Fs::toNativePath(qApp->applicationFilePath()) + "\"";
|
||||||
settings.setValue("qBittorrent", bin_path);
|
settings.setValue("qBittorrent", bin_path);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -359,13 +361,13 @@ QString Preferences::getSavePath() const
|
|||||||
{
|
{
|
||||||
QString save_path = value("Preferences/Downloads/SavePath").toString();
|
QString save_path = value("Preferences/Downloads/SavePath").toString();
|
||||||
if (!save_path.isEmpty())
|
if (!save_path.isEmpty())
|
||||||
return fsutils::fromNativePath(save_path);
|
return Utils::Fs::fromNativePath(save_path);
|
||||||
return fsutils::QDesktopServicesDownloadLocation();
|
return Utils::Fs::QDesktopServicesDownloadLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setSavePath(const QString &save_path)
|
void Preferences::setSavePath(const QString &save_path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/SavePath", fsutils::fromNativePath(save_path));
|
setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isTempPathEnabled() const
|
bool Preferences::isTempPathEnabled() const
|
||||||
@@ -381,12 +383,12 @@ void Preferences::setTempPathEnabled(bool enabled)
|
|||||||
QString Preferences::getTempPath() const
|
QString Preferences::getTempPath() const
|
||||||
{
|
{
|
||||||
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
|
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
|
||||||
return fsutils::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setTempPath(const QString &path)
|
void Preferences::setTempPath(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/TempPath", fsutils::fromNativePath(path));
|
setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::useIncompleteFilesExtension() const
|
bool Preferences::useIncompleteFilesExtension() const
|
||||||
@@ -411,12 +413,12 @@ void Preferences::setAppendTorrentLabel(bool b)
|
|||||||
|
|
||||||
QString Preferences::lastLocationPath() const
|
QString Preferences::lastLocationPath() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setLastLocationPath(const QString &path)
|
void Preferences::setLastLocationPath(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/LastLocationPath", fsutils::fromNativePath(path));
|
setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::fromNativePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::preAllocateAllFiles() const
|
bool Preferences::preAllocateAllFiles() const
|
||||||
@@ -467,7 +469,7 @@ QStringList Preferences::getScanDirs() const
|
|||||||
|
|
||||||
QStringList newList;
|
QStringList newList;
|
||||||
foreach (const QString& s, originalList)
|
foreach (const QString& s, originalList)
|
||||||
newList << fsutils::fromNativePath(s);
|
newList << Utils::Fs::fromNativePath(s);
|
||||||
return newList;
|
return newList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,28 +479,28 @@ void Preferences::setScanDirs(const QStringList &dirs)
|
|||||||
QStringList newList;
|
QStringList newList;
|
||||||
if (!dirs.isEmpty())
|
if (!dirs.isEmpty())
|
||||||
foreach (const QString& s, dirs)
|
foreach (const QString& s, dirs)
|
||||||
newList << fsutils::fromNativePath(s);
|
newList << Utils::Fs::fromNativePath(s);
|
||||||
setValue("Preferences/Downloads/ScanDirs", newList);
|
setValue("Preferences/Downloads/ScanDirs", newList);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<bool> Preferences::getDownloadInScanDirs() const
|
QList<bool> Preferences::getDownloadInScanDirs() const
|
||||||
{
|
{
|
||||||
return misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList());
|
return Utils::Misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setDownloadInScanDirs(const QList<bool> &list)
|
void Preferences::setDownloadInScanDirs(const QList<bool> &list)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/DownloadInScanDirs", misc::toStringList(list));
|
setValue("Preferences/Downloads/DownloadInScanDirs", Utils::Misc::toStringList(list));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Preferences::getScanDirsLastPath() const
|
QString Preferences::getScanDirsLastPath() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString());
|
return Utils::Fs::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setScanDirsLastPath(const QString &path)
|
void Preferences::setScanDirsLastPath(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/ScanDirsLastPath", fsutils::fromNativePath(path));
|
setValue("Preferences/Downloads/ScanDirsLastPath", Utils::Fs::fromNativePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isTorrentExportEnabled() const
|
bool Preferences::isTorrentExportEnabled() const
|
||||||
@@ -508,12 +510,12 @@ bool Preferences::isTorrentExportEnabled() const
|
|||||||
|
|
||||||
QString Preferences::getTorrentExportDir() const
|
QString Preferences::getTorrentExportDir() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString());
|
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setTorrentExportDir(QString path)
|
void Preferences::setTorrentExportDir(QString path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/TorrentExportDir", fsutils::fromNativePath(path.trimmed()));
|
setValue("Preferences/Downloads/TorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isFinishedTorrentExportEnabled() const
|
bool Preferences::isFinishedTorrentExportEnabled() const
|
||||||
@@ -523,12 +525,12 @@ bool Preferences::isFinishedTorrentExportEnabled() const
|
|||||||
|
|
||||||
QString Preferences::getFinishedTorrentExportDir() const
|
QString Preferences::getFinishedTorrentExportDir() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString());
|
return Utils::Fs::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setFinishedTorrentExportDir(QString path)
|
void Preferences::setFinishedTorrentExportDir(QString path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Downloads/FinishedTorrentExportDir", fsutils::fromNativePath(path.trimmed()));
|
setValue("Preferences/Downloads/FinishedTorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::isMailNotificationEnabled() const
|
bool Preferences::isMailNotificationEnabled() const
|
||||||
@@ -950,12 +952,12 @@ void Preferences::setGlobalMaxRatio(qreal ratio)
|
|||||||
setValue("Preferences/Bittorrent/MaxRatio", ratio);
|
setValue("Preferences/Bittorrent/MaxRatio", ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Preferences::getMaxRatioAction() const
|
MaxRatioAction Preferences::getMaxRatioAction() const
|
||||||
{
|
{
|
||||||
return value("Preferences/Bittorrent/MaxRatioAction", PAUSE_ACTION).toInt();
|
return value("Preferences/Bittorrent/MaxRatioAction", MaxRatioAction::Pause).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setMaxRatioAction(int act)
|
void Preferences::setMaxRatioAction(MaxRatioAction act)
|
||||||
{
|
{
|
||||||
setValue("Preferences/Bittorrent/MaxRatioAction", act);
|
setValue("Preferences/Bittorrent/MaxRatioAction", act);
|
||||||
}
|
}
|
||||||
@@ -973,12 +975,12 @@ void Preferences::setFilteringEnabled(bool enabled)
|
|||||||
|
|
||||||
QString Preferences::getFilter() const
|
QString Preferences::getFilter() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("Preferences/IPFilter/File").toString());
|
return Utils::Fs::fromNativePath(value("Preferences/IPFilter/File").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setFilter(const QString &path)
|
void Preferences::setFilter(const QString &path)
|
||||||
{
|
{
|
||||||
setValue("Preferences/IPFilter/File", fsutils::fromNativePath(path));
|
setValue("Preferences/IPFilter/File", Utils::Fs::fromNativePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Preferences::bannedIPs() const
|
QStringList Preferences::bannedIPs() const
|
||||||
@@ -1273,12 +1275,12 @@ void Preferences::setAutoRunEnabled(bool enabled)
|
|||||||
|
|
||||||
QString Preferences::getAutoRunProgram() const
|
QString Preferences::getAutoRunProgram() const
|
||||||
{
|
{
|
||||||
return fsutils::fromNativePath(value("AutoRun/program").toString());
|
return Utils::Fs::fromNativePath(value("AutoRun/program").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setAutoRunProgram(const QString &program)
|
void Preferences::setAutoRunProgram(const QString &program)
|
||||||
{
|
{
|
||||||
setValue("AutoRun/program", fsutils::fromNativePath(program));
|
setValue("AutoRun/program", Utils::Fs::fromNativePath(program));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::shutdownWhenDownloadsComplete() const
|
bool Preferences::shutdownWhenDownloadsComplete() const
|
||||||
@@ -1786,12 +1788,12 @@ bool Preferences::isMagnetLinkAssocSet()
|
|||||||
|
|
||||||
// Check magnet link assoc
|
// Check magnet link assoc
|
||||||
QRegExp exe_reg("\"([^\"]+)\".*");
|
QRegExp exe_reg("\"([^\"]+)\".*");
|
||||||
QString shell_command = fsutils::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
QString shell_command = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
||||||
if (exe_reg.indexIn(shell_command) < 0)
|
if (exe_reg.indexIn(shell_command) < 0)
|
||||||
return false;
|
return false;
|
||||||
QString assoc_exe = exe_reg.cap(1);
|
QString assoc_exe = exe_reg.cap(1);
|
||||||
qDebug("exe: %s", qPrintable(assoc_exe));
|
qDebug("exe: %s", qPrintable(assoc_exe));
|
||||||
if (assoc_exe.compare(fsutils::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
if (assoc_exe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1827,9 +1829,9 @@ void Preferences::setMagnetLinkAssoc(bool set)
|
|||||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||||
settings.setValue("magnet/URL Protocol", "");
|
settings.setValue("magnet/URL Protocol", "");
|
||||||
settings.setValue("magnet/DefaultIcon/Default", fsutils::toNativePath(icon_str));
|
settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(icon_str));
|
||||||
settings.setValue("magnet/shell/Default", "open");
|
settings.setValue("magnet/shell/Default", "open");
|
||||||
settings.setValue("magnet/shell/open/command/Default", fsutils::toNativePath(command_str));
|
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(command_str));
|
||||||
}
|
}
|
||||||
else if (isMagnetLinkAssocSet()) {
|
else if (isMagnetLinkAssocSet()) {
|
||||||
settings.remove("magnet");
|
settings.remove("magnet");
|
||||||
@@ -2477,3 +2479,9 @@ void Preferences::setHostNameCookies(const QString &host_name, const QList<QByte
|
|||||||
hosts_table.insert(host_name, raw_cookies);
|
hosts_table.insert(host_name, raw_cookies);
|
||||||
setValue("Rss/hosts_cookies", hosts_table);
|
setValue("Rss/hosts_cookies", hosts_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Preferences::apply()
|
||||||
|
{
|
||||||
|
if (save())
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@
|
|||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include "core/types.h"
|
||||||
|
|
||||||
enum scheduler_days
|
enum scheduler_days
|
||||||
{
|
{
|
||||||
EVERY_DAY,
|
EVERY_DAY,
|
||||||
@@ -57,12 +59,6 @@ enum scheduler_days
|
|||||||
SUN
|
SUN
|
||||||
};
|
};
|
||||||
|
|
||||||
enum maxRatioAction
|
|
||||||
{
|
|
||||||
PAUSE_ACTION,
|
|
||||||
REMOVE_ACTION
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Proxy
|
namespace Proxy
|
||||||
{
|
{
|
||||||
enum ProxyType
|
enum ProxyType
|
||||||
@@ -101,7 +97,9 @@ class Preferences: public QObject
|
|||||||
Q_DISABLE_COPY(Preferences)
|
Q_DISABLE_COPY(Preferences)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Preferences();
|
Preferences();
|
||||||
|
~Preferences();
|
||||||
|
|
||||||
static Preferences* m_instance;
|
static Preferences* m_instance;
|
||||||
QHash<QString, QVariant> m_data;
|
QHash<QString, QVariant> m_data;
|
||||||
int m_randomPort;
|
int m_randomPort;
|
||||||
@@ -111,16 +109,16 @@ private:
|
|||||||
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||||
void setValue(const QString &key, const QVariant &value);
|
void setValue(const QString &key, const QVariant &value);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
bool save();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changed();
|
void changed();
|
||||||
|
|
||||||
public slots:
|
|
||||||
void save();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static void initInstance();
|
||||||
|
static void freeInstance();
|
||||||
static Preferences* instance();
|
static Preferences* instance();
|
||||||
static void drop();
|
|
||||||
~Preferences();
|
|
||||||
|
|
||||||
// General options
|
// General options
|
||||||
QString getLocale() const;
|
QString getLocale() const;
|
||||||
@@ -276,8 +274,8 @@ public:
|
|||||||
void setEncryptionSetting(int val);
|
void setEncryptionSetting(int val);
|
||||||
qreal getGlobalMaxRatio() const;
|
qreal getGlobalMaxRatio() const;
|
||||||
void setGlobalMaxRatio(qreal ratio);
|
void setGlobalMaxRatio(qreal ratio);
|
||||||
int getMaxRatioAction() const;
|
MaxRatioAction getMaxRatioAction() const;
|
||||||
void setMaxRatioAction(int act);
|
void setMaxRatioAction(MaxRatioAction act);
|
||||||
|
|
||||||
// IP Filter
|
// IP Filter
|
||||||
bool isFilteringEnabled() const;
|
bool isFilteringEnabled() const;
|
||||||
@@ -536,6 +534,8 @@ public slots:
|
|||||||
void setStatusFilterState(bool checked);
|
void setStatusFilterState(bool checked);
|
||||||
void setLabelFilterState(bool checked);
|
void setLabelFilterState(bool checked);
|
||||||
void setTrackerFilterState(bool checked);
|
void setTrackerFilterState(bool checked);
|
||||||
|
|
||||||
|
void apply();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PREFERENCES_H
|
#endif // PREFERENCES_H
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2014 Ivan Sorokin
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : vanyacpp@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "alertdispatcher.h"
|
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include <QMutexLocker>
|
|
||||||
|
|
||||||
const size_t DEFAULT_ALERTS_CAPACITY = 32;
|
|
||||||
|
|
||||||
struct QAlertDispatcher::Tag {
|
|
||||||
Tag(QAlertDispatcher* dispatcher);
|
|
||||||
|
|
||||||
QAlertDispatcher* dispatcher;
|
|
||||||
QMutex alerts_mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
QAlertDispatcher::Tag::Tag(QAlertDispatcher* dispatcher)
|
|
||||||
: dispatcher(dispatcher)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_session(session)
|
|
||||||
, current_tag(new Tag(this))
|
|
||||||
, event_posted(false)
|
|
||||||
{
|
|
||||||
alerts.reserve(DEFAULT_ALERTS_CAPACITY);
|
|
||||||
m_session->set_alert_dispatch(boost::bind(&QAlertDispatcher::dispatch, current_tag, _1));
|
|
||||||
}
|
|
||||||
|
|
||||||
QAlertDispatcher::~QAlertDispatcher() {
|
|
||||||
// When QAlertDispatcher is destoyed, libtorrent still can call
|
|
||||||
// QAlertDispatcher::dispatch a few times after destruction. This is
|
|
||||||
// handled by passing a "tag". A tag is a object that references QAlertDispatch.
|
|
||||||
// Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag
|
|
||||||
// and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called
|
|
||||||
// with invalid tag it simply discard an alert.
|
|
||||||
|
|
||||||
{
|
|
||||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
|
||||||
current_tag->dispatcher = 0;
|
|
||||||
current_tag.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef boost::function<void (std::auto_ptr<libtorrent::alert>)> dispatch_function_t;
|
|
||||||
m_session->set_alert_dispatch(dispatch_function_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAlertDispatcher::getPendingAlertsNoWait(std::vector<libtorrent::alert*>& out) {
|
|
||||||
Q_ASSERT(out.empty());
|
|
||||||
out.reserve(DEFAULT_ALERTS_CAPACITY);
|
|
||||||
|
|
||||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
|
||||||
alerts.swap(out);
|
|
||||||
event_posted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAlertDispatcher::getPendingAlerts(std::vector<libtorrent::alert*>& out, unsigned long time) {
|
|
||||||
Q_ASSERT(out.empty());
|
|
||||||
out.reserve(DEFAULT_ALERTS_CAPACITY);
|
|
||||||
|
|
||||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
|
||||||
|
|
||||||
while (alerts.empty())
|
|
||||||
alerts_condvar.wait(¤t_tag->alerts_mutex, time);
|
|
||||||
|
|
||||||
alerts.swap(out);
|
|
||||||
event_posted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAlertDispatcher::dispatch(QSharedPointer<Tag> tag,
|
|
||||||
std::auto_ptr<libtorrent::alert> alert_ptr) {
|
|
||||||
QMutexLocker lock(&(tag->alerts_mutex));
|
|
||||||
QAlertDispatcher* that = tag->dispatcher;
|
|
||||||
if (!that)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool was_empty = that->alerts.empty();
|
|
||||||
|
|
||||||
that->alerts.push_back(alert_ptr.get());
|
|
||||||
alert_ptr.release();
|
|
||||||
|
|
||||||
if (was_empty)
|
|
||||||
that->alerts_condvar.wakeAll();
|
|
||||||
|
|
||||||
that->enqueueToMainThread();
|
|
||||||
|
|
||||||
Q_ASSERT(that->current_tag == tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAlertDispatcher::enqueueToMainThread() {
|
|
||||||
if (!event_posted) {
|
|
||||||
event_posted = true;
|
|
||||||
QMetaObject::invokeMethod(this, "deliverSignal", Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QAlertDispatcher::deliverSignal() {
|
|
||||||
emit alertsReceived();
|
|
||||||
|
|
||||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
|
||||||
event_posted = false;
|
|
||||||
|
|
||||||
if (!alerts.empty())
|
|
||||||
enqueueToMainThread();
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#ifndef BANDWIDTHSCHEDULER_H
|
|
||||||
#define BANDWIDTHSCHEDULER_H
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QTime>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include "preferences.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
class BandwidthScheduler: public QTimer {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
BandwidthScheduler(QObject *parent): QTimer(parent) {
|
|
||||||
Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
|
|
||||||
// Signal shot, we call start() again manually
|
|
||||||
setSingleShot(true);
|
|
||||||
// Connect Signals/Slots
|
|
||||||
connect(this, SIGNAL(timeout()), this, SLOT(start()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void start() {
|
|
||||||
const Preferences* const pref = Preferences::instance();
|
|
||||||
Q_ASSERT(pref->isSchedulerEnabled());
|
|
||||||
bool alt_bw_enabled = pref->isAltBandwidthEnabled();
|
|
||||||
|
|
||||||
QTime start = pref->getSchedulerStartTime();
|
|
||||||
QTime end = pref->getSchedulerEndTime();
|
|
||||||
QTime now = QTime::currentTime();
|
|
||||||
int sched_days = pref->getSchedulerDays();
|
|
||||||
int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek();
|
|
||||||
bool new_mode = false;
|
|
||||||
bool reverse = false;
|
|
||||||
|
|
||||||
if (start > end) {
|
|
||||||
QTime temp = start;
|
|
||||||
start = end;
|
|
||||||
end = temp;
|
|
||||||
reverse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start <= now && end >= now) {
|
|
||||||
switch(sched_days) {
|
|
||||||
case EVERY_DAY:
|
|
||||||
new_mode = true;
|
|
||||||
break;
|
|
||||||
case WEEK_ENDS:
|
|
||||||
if (day == 6 || day == 7)
|
|
||||||
new_mode = true;
|
|
||||||
break;
|
|
||||||
case WEEK_DAYS:
|
|
||||||
if (day != 6 && day != 7)
|
|
||||||
new_mode = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (day == sched_days - 2)
|
|
||||||
new_mode = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reverse)
|
|
||||||
new_mode = !new_mode;
|
|
||||||
|
|
||||||
if (new_mode != alt_bw_enabled)
|
|
||||||
emit switchToAlternativeMode(new_mode);
|
|
||||||
|
|
||||||
// Timeout regularly to accomodate for external system clock changes
|
|
||||||
// eg from the user or from a timesync utility
|
|
||||||
QTimer::start(1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void switchToAlternativeMode(bool alternative);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BANDWIDTHSCHEDULER_H
|
|
||||||
@@ -1,394 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "filterparserthread.h"
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QHostAddress>
|
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
|
||||||
#include <libtorrent/ip_filter.hpp>
|
|
||||||
|
|
||||||
FilterParserThread::FilterParserThread(QObject* parent, libtorrent::session *s) : QThread(parent), s(s), abort(false) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
FilterParserThread::~FilterParserThread() {
|
|
||||||
abort = true;
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser for eMule ip filter in DAT format
|
|
||||||
int FilterParserThread::parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
|
||||||
int ruleCount = 0;
|
|
||||||
QFile file(filePath);
|
|
||||||
if (file.exists()) {
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
unsigned int nbLine = 0;
|
|
||||||
while (!file.atEnd() && !abort) {
|
|
||||||
++nbLine;
|
|
||||||
QByteArray line = file.readLine();
|
|
||||||
// Ignoring empty lines
|
|
||||||
line = line.trimmed();
|
|
||||||
if (line.isEmpty()) continue;
|
|
||||||
// Ignoring commented lines
|
|
||||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
|
||||||
|
|
||||||
// Line should be splitted by commas
|
|
||||||
QList<QByteArray> partsList = line.split(',');
|
|
||||||
const uint nbElem = partsList.size();
|
|
||||||
|
|
||||||
// IP Range should be splitted by a dash
|
|
||||||
QList<QByteArray> IPs = partsList.first().split('-');
|
|
||||||
if (IPs.size() != 2) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Line was %s", line.constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_code ec;
|
|
||||||
const QString strStartIP = cleanupIPAddress(IPs.at(0));
|
|
||||||
if (strStartIP.isEmpty()) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
|
|
||||||
if (ec) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const QString strEndIP = cleanupIPAddress(IPs.at(1));
|
|
||||||
if (strEndIP.isEmpty()) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
|
|
||||||
if (ec) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
|
||||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
|
||||||
qDebug("One IP is IPv4 and the other is IPv6!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there is an access value (apparently not mandatory)
|
|
||||||
int nbAccess = 0;
|
|
||||||
if (nbElem > 1) {
|
|
||||||
// There is possibly one
|
|
||||||
nbAccess = partsList.at(1).trimmed().toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbAccess > 127) {
|
|
||||||
// Ignoring this rule because access value is too high
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Now Add to the filter
|
|
||||||
try {
|
|
||||||
filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked);
|
|
||||||
++ruleCount;
|
|
||||||
}catch(exception) {
|
|
||||||
qDebug("Bad line in filter file, avoided crash...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser for PeerGuardian ip filter in p2p format
|
|
||||||
int FilterParserThread::parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
|
||||||
int ruleCount = 0;
|
|
||||||
QFile file(filePath);
|
|
||||||
if (file.exists()) {
|
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
unsigned int nbLine = 0;
|
|
||||||
while (!file.atEnd() && !abort) {
|
|
||||||
++nbLine;
|
|
||||||
QByteArray line = file.readLine().trimmed();
|
|
||||||
if (line.isEmpty()) continue;
|
|
||||||
// Ignoring commented lines
|
|
||||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
|
||||||
// Line is splitted by :
|
|
||||||
QList<QByteArray> partsList = line.split(':');
|
|
||||||
if (partsList.size() < 2) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Get IP range
|
|
||||||
QList<QByteArray> IPs = partsList.last().split('-');
|
|
||||||
if (IPs.size() != 2) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("line was: %s", line.constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
boost::system::error_code ec;
|
|
||||||
QString strStartIP = cleanupIPAddress(IPs.at(0));
|
|
||||||
if (strStartIP.isEmpty()) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
|
|
||||||
if (ec) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QString strEndIP = cleanupIPAddress(IPs.at(1));
|
|
||||||
if (strEndIP.isEmpty()) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
|
|
||||||
if (ec) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Line was: %s", line.constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked);
|
|
||||||
++ruleCount;
|
|
||||||
} catch(std::exception&) {
|
|
||||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
|
||||||
qDebug("Line was: %s", line.constData());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FilterParserThread::getlineInStream(QDataStream& stream, string& name, char delim) {
|
|
||||||
char c;
|
|
||||||
int total_read = 0;
|
|
||||||
int read;
|
|
||||||
do {
|
|
||||||
read = stream.readRawData(&c, 1);
|
|
||||||
total_read += read;
|
|
||||||
if (read > 0) {
|
|
||||||
if (c != delim) {
|
|
||||||
name += c;
|
|
||||||
} else {
|
|
||||||
// Delim found
|
|
||||||
return total_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while(read > 0);
|
|
||||||
return total_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser for PeerGuardian ip filter in p2p format
|
|
||||||
int FilterParserThread::parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
|
||||||
int ruleCount = 0;
|
|
||||||
QFile file(filePath);
|
|
||||||
if (file.exists()) {
|
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
|
||||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
QDataStream stream(&file);
|
|
||||||
// Read header
|
|
||||||
char buf[7];
|
|
||||||
unsigned char version;
|
|
||||||
if (
|
|
||||||
!stream.readRawData(buf, sizeof(buf)) ||
|
|
||||||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
|
|
||||||
!stream.readRawData((char*)&version, sizeof(version))
|
|
||||||
) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version==1 || version==2) {
|
|
||||||
qDebug ("p2b version 1 or 2");
|
|
||||||
unsigned int start, end;
|
|
||||||
|
|
||||||
string name;
|
|
||||||
while(getlineInStream(stream, name, '\0') && !abort) {
|
|
||||||
if (
|
|
||||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
|
||||||
!stream.readRawData((char*)&end, sizeof(end))
|
|
||||||
) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
// Network byte order to Host byte order
|
|
||||||
// asio address_v4 contructor expects it
|
|
||||||
// that way
|
|
||||||
libtorrent::address_v4 first(ntohl(start));
|
|
||||||
libtorrent::address_v4 last(ntohl(end));
|
|
||||||
// Apply to bittorrent session
|
|
||||||
try {
|
|
||||||
filter.add_rule(first, last, libtorrent::ip_filter::blocked);
|
|
||||||
++ruleCount;
|
|
||||||
} catch(std::exception&) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (version==3) {
|
|
||||||
qDebug ("p2b version 3");
|
|
||||||
unsigned int namecount;
|
|
||||||
if (!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
namecount=ntohl(namecount);
|
|
||||||
// Reading names although, we don't really care about them
|
|
||||||
for (unsigned int i=0; i<namecount; i++) {
|
|
||||||
string name;
|
|
||||||
if (!getlineInStream(stream, name, '\0')) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
if (abort) return ruleCount;
|
|
||||||
}
|
|
||||||
// Reading the ranges
|
|
||||||
unsigned int rangecount;
|
|
||||||
if (!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
rangecount=ntohl(rangecount);
|
|
||||||
|
|
||||||
unsigned int name, start, end;
|
|
||||||
|
|
||||||
for (unsigned int i=0; i<rangecount; i++) {
|
|
||||||
if (
|
|
||||||
!stream.readRawData((char*)&name, sizeof(name)) ||
|
|
||||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
|
||||||
!stream.readRawData((char*)&end, sizeof(end))
|
|
||||||
) {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
// Network byte order to Host byte order
|
|
||||||
// asio address_v4 contructor expects it
|
|
||||||
// that way
|
|
||||||
libtorrent::address_v4 first(ntohl(start));
|
|
||||||
libtorrent::address_v4 last(ntohl(end));
|
|
||||||
// Apply to bittorrent session
|
|
||||||
try {
|
|
||||||
filter.add_rule(first, last, libtorrent::ip_filter::blocked);
|
|
||||||
++ruleCount;
|
|
||||||
} catch(std::exception&) {}
|
|
||||||
if (abort) return ruleCount;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
return ruleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process ip filter file
|
|
||||||
// Supported formats:
|
|
||||||
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
|
||||||
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
|
||||||
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
|
|
||||||
void FilterParserThread::processFilterFile(QString _filePath) {
|
|
||||||
if (isRunning()) {
|
|
||||||
// Already parsing a filter, abort first
|
|
||||||
abort = true;
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
abort = false;
|
|
||||||
filePath = _filePath;
|
|
||||||
// Run it
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilterParserThread::processFilterList(libtorrent::session *s, const QStringList& IPs) {
|
|
||||||
// First, import current filter
|
|
||||||
libtorrent::ip_filter filter = s->get_ip_filter();
|
|
||||||
foreach (const QString &ip, IPs) {
|
|
||||||
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
|
|
||||||
boost::system::error_code ec;
|
|
||||||
libtorrent::address addr = libtorrent::address::from_string(ip.toLocal8Bit().constData(), ec);
|
|
||||||
Q_ASSERT(!ec);
|
|
||||||
if (!ec)
|
|
||||||
filter.add_rule(addr, addr, libtorrent::ip_filter::blocked);
|
|
||||||
}
|
|
||||||
s->set_ip_filter(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FilterParserThread::cleanupIPAddress(QString _ip) {
|
|
||||||
QHostAddress ip(_ip.trimmed());
|
|
||||||
if (ip.isNull()) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return ip.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilterParserThread::run() {
|
|
||||||
qDebug("Processing filter file");
|
|
||||||
libtorrent::ip_filter filter = s->get_ip_filter();
|
|
||||||
int ruleCount = 0;
|
|
||||||
if (filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
|
||||||
// PeerGuardian p2p file
|
|
||||||
ruleCount = parseP2PFilterFile(filePath, filter);
|
|
||||||
} else {
|
|
||||||
if (filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
|
|
||||||
// PeerGuardian p2b file
|
|
||||||
ruleCount = parseP2BFilterFile(filePath, filter);
|
|
||||||
} else {
|
|
||||||
// Default: eMule DAT format
|
|
||||||
ruleCount = parseDATFilterFile(filePath, filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (abort)
|
|
||||||
return;
|
|
||||||
try {
|
|
||||||
s->set_ip_filter(filter);
|
|
||||||
emit IPFilterParsed(ruleCount);
|
|
||||||
} catch(std::exception&) {
|
|
||||||
emit IPFilterError();
|
|
||||||
}
|
|
||||||
qDebug("IP Filter thread: finished parsing, filter applied");
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
#ifndef __BITTORRENT_H__
|
|
||||||
#define __BITTORRENT_H__
|
|
||||||
|
|
||||||
#include <QMap>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QPointer>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QNetworkCookie>
|
|
||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
|
|
||||||
#include "qtorrenthandle.h"
|
|
||||||
#include "trackerinfos.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
#include "rssdownloadrule.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace libtorrent {
|
|
||||||
struct add_torrent_params;
|
|
||||||
struct pe_settings;
|
|
||||||
struct proxy_settings;
|
|
||||||
class session;
|
|
||||||
struct session_status;
|
|
||||||
|
|
||||||
class alert;
|
|
||||||
struct torrent_finished_alert;
|
|
||||||
struct save_resume_data_alert;
|
|
||||||
struct file_renamed_alert;
|
|
||||||
struct torrent_deleted_alert;
|
|
||||||
struct storage_moved_alert;
|
|
||||||
struct storage_moved_failed_alert;
|
|
||||||
struct metadata_received_alert;
|
|
||||||
struct file_error_alert;
|
|
||||||
struct file_completed_alert;
|
|
||||||
struct torrent_paused_alert;
|
|
||||||
struct tracker_error_alert;
|
|
||||||
struct tracker_reply_alert;
|
|
||||||
struct tracker_warning_alert;
|
|
||||||
struct portmap_error_alert;
|
|
||||||
struct portmap_alert;
|
|
||||||
struct peer_blocked_alert;
|
|
||||||
struct peer_ban_alert;
|
|
||||||
struct fastresume_rejected_alert;
|
|
||||||
struct url_seed_alert;
|
|
||||||
struct listen_succeeded_alert;
|
|
||||||
struct listen_failed_alert;
|
|
||||||
struct torrent_checked_alert;
|
|
||||||
struct external_ip_alert;
|
|
||||||
struct state_update_alert;
|
|
||||||
struct stats_alert;
|
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
class upnp;
|
|
||||||
class natpmp;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
class DownloadThread;
|
|
||||||
class FilterParserThread;
|
|
||||||
class BandwidthScheduler;
|
|
||||||
class ScanFoldersModel;
|
|
||||||
class TorrentSpeedMonitor;
|
|
||||||
class TorrentStatistics;
|
|
||||||
class QAlertDispatcher;
|
|
||||||
|
|
||||||
enum TorrentExportFolder {
|
|
||||||
RegularTorrentExportFolder,
|
|
||||||
FinishedTorrentExportFolder
|
|
||||||
};
|
|
||||||
|
|
||||||
class QTracker;
|
|
||||||
|
|
||||||
class QBtSession : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(QBtSession)
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const qreal MAX_RATIO;
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit QBtSession();
|
|
||||||
static QBtSession* m_instance;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static QBtSession* instance();
|
|
||||||
static void drop();
|
|
||||||
~QBtSession();
|
|
||||||
QTorrentHandle getTorrentHandle(const QString &hash) const;
|
|
||||||
std::vector<libtorrent::torrent_handle> getTorrents() const;
|
|
||||||
qreal getPayloadDownloadRate() const;
|
|
||||||
qreal getPayloadUploadRate() const;
|
|
||||||
libtorrent::session_status getSessionStatus() const;
|
|
||||||
int getListenPort() const;
|
|
||||||
qreal getRealRatio(const libtorrent::torrent_status &status) const;
|
|
||||||
QHash<QString, TrackerInfos> getTrackersInfo(const QString &hash) const;
|
|
||||||
bool hasActiveTorrents() const;
|
|
||||||
bool hasDownloadingTorrents() const;
|
|
||||||
//int getMaximumActiveDownloads() const;
|
|
||||||
//int getMaximumActiveTorrents() const;
|
|
||||||
inline libtorrent::session* getSession() const { return s; }
|
|
||||||
inline bool useTemporaryFolder() const { return !defaultTempPath.isEmpty(); }
|
|
||||||
inline QString getDefaultSavePath() const { return defaultSavePath; }
|
|
||||||
inline ScanFoldersModel* getScanFoldersModel() const { return m_scanFolders; }
|
|
||||||
inline bool isDHTEnabled() const { return DHTEnabled; }
|
|
||||||
inline bool isLSDEnabled() const { return LSDEnabled; }
|
|
||||||
inline bool isPexEnabled() const { return PeXEnabled; }
|
|
||||||
inline bool isQueueingEnabled() const { return queueingEnabled; }
|
|
||||||
quint64 getAlltimeDL() const;
|
|
||||||
quint64 getAlltimeUL() const;
|
|
||||||
void postTorrentUpdate();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false, bool imported = false);
|
|
||||||
QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false, bool fromScanDir=false, const QString &filePath=QString());
|
|
||||||
void loadSessionState();
|
|
||||||
void saveSessionState();
|
|
||||||
void downloadFromUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
|
||||||
void deleteTorrent(const QString &hash, bool delete_local_files = false);
|
|
||||||
void startUpTorrents();
|
|
||||||
void recheckTorrent(const QString &hash);
|
|
||||||
void useAlternativeSpeedsLimit(bool alternative);
|
|
||||||
qlonglong getETA(const QString& hash, const libtorrent::torrent_status &status) const;
|
|
||||||
/* Needed by Web UI */
|
|
||||||
void pauseAllTorrents();
|
|
||||||
void pauseTorrent(const QString &hash);
|
|
||||||
void resumeTorrent(const QString &hash, const bool force = false);
|
|
||||||
void resumeAllTorrents();
|
|
||||||
/* End Web UI */
|
|
||||||
void preAllocateAllFiles(bool b);
|
|
||||||
void saveFastResumeData();
|
|
||||||
void enableIPFilter(const QString &filter_path, bool force=false);
|
|
||||||
void disableIPFilter();
|
|
||||||
void setQueueingEnabled(bool enable);
|
|
||||||
void handleDownloadFailure(QString url, QString reason);
|
|
||||||
void handleMagnetRedirect(const QString &url_new, const QString &url_old);
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(),
|
|
||||||
const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>(),
|
|
||||||
const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL);
|
|
||||||
#else
|
|
||||||
void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(),
|
|
||||||
const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
|
||||||
#endif
|
|
||||||
// Session configuration - Setters
|
|
||||||
void setListeningPort(int port);
|
|
||||||
void setMaxConnectionsPerTorrent(int max);
|
|
||||||
void setMaxUploadsPerTorrent(int max);
|
|
||||||
void setDownloadRateLimit(long rate);
|
|
||||||
void setUploadRateLimit(long rate);
|
|
||||||
void setGlobalMaxRatio(qreal ratio);
|
|
||||||
qreal getGlobalMaxRatio() const { return global_ratio_limit; }
|
|
||||||
void setMaxRatioPerTorrent(const QString &hash, qreal ratio);
|
|
||||||
qreal getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const;
|
|
||||||
void removeRatioPerTorrent(const QString &hash);
|
|
||||||
void setDefaultSavePath(const QString &savepath);
|
|
||||||
void setDefaultTempPath(const QString &temppath);
|
|
||||||
void setAppendLabelToSavePath(bool append);
|
|
||||||
void appendLabelToTorrentSavePath(const QTorrentHandle &h);
|
|
||||||
void changeLabelInTorrentSavePath(const QTorrentHandle &h, QString old_label, QString new_label);
|
|
||||||
void appendqBextensionToTorrent(const QTorrentHandle &h, bool append);
|
|
||||||
void setAppendqBExtension(bool append);
|
|
||||||
void setDownloadLimit(QString hash, long val);
|
|
||||||
void setUploadLimit(QString hash, long val);
|
|
||||||
void enableUPnP(bool b);
|
|
||||||
void enableLSD(bool b);
|
|
||||||
void enableDHT(bool b);
|
|
||||||
void processDownloadedFile(QString, QString);
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(),
|
|
||||||
const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL, const QString &uri_old = QString());
|
|
||||||
#else
|
|
||||||
void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(), const QString &uri_old = QString());
|
|
||||||
#endif
|
|
||||||
void addMagnetInteractive(const QString& uri);
|
|
||||||
void downloadFromURLList(const QStringList& urls);
|
|
||||||
void banIP(QString ip);
|
|
||||||
void recursiveTorrentDownload(const QTorrentHandle &h);
|
|
||||||
void unhideMagnet(const QString &hash);
|
|
||||||
void addTrackersAndUrlSeeds(const QString &hash, const QStringList &trackers, const QStringList& urlSeeds);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void applyEncryptionSettings(libtorrent::pe_settings se);
|
|
||||||
void setProxySettings(libtorrent::proxy_settings proxySettings);
|
|
||||||
void setSessionSettings(const libtorrent::session_settings &sessionSettings);
|
|
||||||
QString getSavePath(const QString &hash, bool fromScanDir = false, QString filePath = QString::null, bool imported = false);
|
|
||||||
bool loadFastResumeData(const QString &hash, std::vector<char> &buf);
|
|
||||||
void loadTorrentSettings(QTorrentHandle &h);
|
|
||||||
void loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet);
|
|
||||||
void initializeAddTorrentParams(const QString &hash, libtorrent::add_torrent_params &p);
|
|
||||||
void updateRatioTimer();
|
|
||||||
void recoverPersistentData(const QString &hash, const std::vector<char> &buf);
|
|
||||||
void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data);
|
|
||||||
void handleAlert(libtorrent::alert* a);
|
|
||||||
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert* p);
|
|
||||||
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert* p);
|
|
||||||
void handleFileRenamedAlert(libtorrent::file_renamed_alert* p);
|
|
||||||
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert* p);
|
|
||||||
void handleStorageMovedAlert(libtorrent::storage_moved_alert* p);
|
|
||||||
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert* p);
|
|
||||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert* p);
|
|
||||||
void handleFileErrorAlert(libtorrent::file_error_alert* p);
|
|
||||||
void handleFileCompletedAlert(libtorrent::file_completed_alert* p);
|
|
||||||
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert* p);
|
|
||||||
void handleTrackerErrorAlert(libtorrent::tracker_error_alert* p);
|
|
||||||
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert* p);
|
|
||||||
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert* p);
|
|
||||||
void handlePortmapWarningAlert(libtorrent::portmap_error_alert* p);
|
|
||||||
void handlePortmapAlert(libtorrent::portmap_alert* p);
|
|
||||||
void handlePeerBlockedAlert(libtorrent::peer_blocked_alert* p);
|
|
||||||
void handlePeerBanAlert(libtorrent::peer_ban_alert* p);
|
|
||||||
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert* p);
|
|
||||||
void handleUrlSeedAlert(libtorrent::url_seed_alert* p);
|
|
||||||
void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p);
|
|
||||||
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
|
|
||||||
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert* p);
|
|
||||||
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
|
|
||||||
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
|
|
||||||
void handleStatsAlert(libtorrent::stats_alert *p);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void addTorrentsFromScanFolder(QStringList&);
|
|
||||||
void readAlerts();
|
|
||||||
void processBigRatios();
|
|
||||||
void exportTorrentFiles(QString path);
|
|
||||||
void saveTempFastResumeData();
|
|
||||||
void sendNotificationEmail(const QTorrentHandle &h);
|
|
||||||
void autoRunExternalProgram(const QTorrentHandle &h);
|
|
||||||
void mergeTorrents(const QTorrentHandle &h, const boost::intrusive_ptr<libtorrent::torrent_info> t);
|
|
||||||
void mergeTorrents(const QTorrentHandle &h, const QString &magnet_uri);
|
|
||||||
void mergeTorrents_impl(const QTorrentHandle &h, const QStringList &trackers, const QStringList& urlSeeds);
|
|
||||||
void exportTorrentFile(const QTorrentHandle &h, TorrentExportFolder folder = RegularTorrentExportFolder);
|
|
||||||
void handleIPFilterParsed(int ruleCount);
|
|
||||||
void handleIPFilterError();
|
|
||||||
void configureSession();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void addedTorrent(const QTorrentHandle& h);
|
|
||||||
void torrentAboutToBeRemoved(const QTorrentHandle &h);
|
|
||||||
void pausedTorrent(const QTorrentHandle& h);
|
|
||||||
void resumedTorrent(const QTorrentHandle& h);
|
|
||||||
void finishedTorrent(const QTorrentHandle& h);
|
|
||||||
void fullDiskError(const QTorrentHandle& h, QString msg);
|
|
||||||
void trackerSuccess(const QString &hash, const QString &tracker);
|
|
||||||
void trackerError(const QString &hash, const QString &tracker);
|
|
||||||
void trackerWarning(const QString &hash, const QString &tracker);
|
|
||||||
void trackerAuthenticationRequired(const QTorrentHandle& h);
|
|
||||||
void newDownloadedTorrent(QString path, QString url);
|
|
||||||
void newDownloadedTorrentFromRss(QString url);
|
|
||||||
void newMagnetLink(const QString& link);
|
|
||||||
void updateFileSize(const QString &hash);
|
|
||||||
void downloadFromUrlFailure(QString url, QString reason);
|
|
||||||
void torrentFinishedChecking(const QTorrentHandle& h);
|
|
||||||
void metadataReceived(const QTorrentHandle &h);
|
|
||||||
void savePathChanged(const QTorrentHandle &h);
|
|
||||||
void alternativeSpeedsModeChanged(bool alternative);
|
|
||||||
void recursiveTorrentDownloadPossible(const QTorrentHandle &h);
|
|
||||||
void ipFilterParsed(bool error, int ruleCount);
|
|
||||||
void metadataReceivedHidden(const QTorrentHandle &h);
|
|
||||||
void stateUpdate(const std::vector<libtorrent::torrent_status> &statuses);
|
|
||||||
void statsReceived(const libtorrent::stats_alert&);
|
|
||||||
void trackersAdded(const QStringList &trackers, const QString &hash);
|
|
||||||
void trackerlessChange(bool trackerless, const QString &hash);
|
|
||||||
void reloadTrackersAndUrlSeeds(const QTorrentHandle &h);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Bittorrent
|
|
||||||
libtorrent::session *s;
|
|
||||||
QPointer<BandwidthScheduler> bd_scheduler;
|
|
||||||
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl)
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
QMap<QUrl, RssDownloadRule::AddPausedState> addpaused_fromurl;
|
|
||||||
#endif
|
|
||||||
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
|
||||||
QHash<QString, QString> savePathsToRemove;
|
|
||||||
QStringList torrentsToPausedAfterChecking;
|
|
||||||
QTimer resumeDataTimer;
|
|
||||||
// Ratio
|
|
||||||
QPointer<QTimer> BigRatioTimer;
|
|
||||||
// HTTP
|
|
||||||
DownloadThread* downloader;
|
|
||||||
// File System
|
|
||||||
ScanFoldersModel *m_scanFolders;
|
|
||||||
// Settings
|
|
||||||
bool preAllocateAll;
|
|
||||||
qreal global_ratio_limit;
|
|
||||||
int high_ratio_action;
|
|
||||||
bool LSDEnabled;
|
|
||||||
bool DHTEnabled;
|
|
||||||
bool PeXEnabled;
|
|
||||||
bool queueingEnabled;
|
|
||||||
bool appendLabelToSavePath;
|
|
||||||
bool m_torrentExportEnabled;
|
|
||||||
bool m_finishedTorrentExportEnabled;
|
|
||||||
bool appendqBExtension;
|
|
||||||
QString defaultSavePath;
|
|
||||||
QString defaultTempPath;
|
|
||||||
// IP filtering
|
|
||||||
QPointer<FilterParserThread> filterParser;
|
|
||||||
QString filterPath;
|
|
||||||
QList<QUrl> url_skippingDlg;
|
|
||||||
// GeoIP
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
bool geoipDBLoaded;
|
|
||||||
bool resolve_countries;
|
|
||||||
#endif
|
|
||||||
// Tracker
|
|
||||||
QPointer<QTracker> m_tracker;
|
|
||||||
TorrentSpeedMonitor *m_speedMonitor;
|
|
||||||
shutDownAction m_shutdownAct;
|
|
||||||
// Port forwarding
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
libtorrent::upnp *m_upnp;
|
|
||||||
libtorrent::natpmp *m_natpmp;
|
|
||||||
#endif
|
|
||||||
QAlertDispatcher* m_alertDispatcher;
|
|
||||||
TorrentStatistics* m_torrentStatistics;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
INCLUDEPATH += $$PWD
|
|
||||||
|
|
||||||
HEADERS += $$PWD/qbtsession.h \
|
|
||||||
$$PWD/qtorrenthandle.h \
|
|
||||||
$$PWD/bandwidthscheduler.h \
|
|
||||||
$$PWD/trackerinfos.h \
|
|
||||||
$$PWD/torrentspeedmonitor.h \
|
|
||||||
$$PWD/filterparserthread.h \
|
|
||||||
$$PWD/alertdispatcher.h \
|
|
||||||
$$PWD/torrentstatistics.h
|
|
||||||
|
|
||||||
SOURCES += $$PWD/qbtsession.cpp \
|
|
||||||
$$PWD/qtorrenthandle.cpp \
|
|
||||||
$$PWD/torrentspeedmonitor.cpp \
|
|
||||||
$$PWD/alertdispatcher.cpp \
|
|
||||||
$$PWD/torrentstatistics.cpp \
|
|
||||||
$$PWD/filterparserthread.cpp
|
|
||||||
|
|
||||||
!contains(DEFINES, DISABLE_GUI) {
|
|
||||||
HEADERS += $$PWD/torrentmodel.h \
|
|
||||||
$$PWD/shutdownconfirm.h
|
|
||||||
|
|
||||||
SOURCES += $$PWD/torrentmodel.cpp \
|
|
||||||
$$PWD/shutdownconfirm.cpp
|
|
||||||
}
|
|
||||||
@@ -1,856 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <math.h>
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "qtorrenthandle.h"
|
|
||||||
#include "torrentpersistentdata.h"
|
|
||||||
#include "qbtsession.h"
|
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
|
||||||
#include <libtorrent/bencode.hpp>
|
|
||||||
#include <libtorrent/entry.hpp>
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <Windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static QPair<int, int> get_file_extremity_pieces(const torrent_info& t, int file_index)
|
|
||||||
{
|
|
||||||
const int num_pieces = t.num_pieces();
|
|
||||||
const int piece_size = t.piece_length();
|
|
||||||
const file_entry& file = t.file_at(file_index);
|
|
||||||
|
|
||||||
// Determine the first and last piece of the file
|
|
||||||
int first_piece = floor((file.offset + 1) / (float) piece_size);
|
|
||||||
Q_ASSERT(first_piece >= 0 && first_piece < num_pieces);
|
|
||||||
|
|
||||||
int num_pieces_in_file = ceil(file.size / (float) piece_size);
|
|
||||||
int last_piece = first_piece + num_pieces_in_file - 1;
|
|
||||||
Q_ASSERT(last_piece >= 0 && last_piece < num_pieces);
|
|
||||||
|
|
||||||
return qMakePair(first_piece, last_piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTorrentHandle::QTorrentHandle(const torrent_handle& h): torrent_handle(h)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Getters
|
|
||||||
//
|
|
||||||
|
|
||||||
QString QTorrentHandle::hash() const
|
|
||||||
{
|
|
||||||
return misc::toQString(torrent_handle::info_hash());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::name() const
|
|
||||||
{
|
|
||||||
QString name = TorrentPersistentData::instance()->getName(hash());
|
|
||||||
if (name.isEmpty()) {
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
name = misc::toQStringU(torrent_handle::name());
|
|
||||||
#else
|
|
||||||
name = misc::toQStringU(status(query_name).name);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::creation_date() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
boost::optional<time_t> t = torrent_handle::get_torrent_info().creation_date();
|
|
||||||
#else
|
|
||||||
boost::optional<time_t> t = torrent_handle::torrent_file()->creation_date();
|
|
||||||
#endif
|
|
||||||
return t ? misc::toQString(*t) : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong QTorrentHandle::creation_date_unix() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
boost::optional<time_t> t = torrent_handle::get_torrent_info().creation_date();
|
|
||||||
#else
|
|
||||||
boost::optional<time_t> t = torrent_handle::torrent_file()->creation_date();
|
|
||||||
#endif
|
|
||||||
return t ? *t : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::current_tracker() const
|
|
||||||
{
|
|
||||||
return misc::toQString(status(0x0).current_tracker);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_paused() const
|
|
||||||
{
|
|
||||||
return is_paused(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_queued() const
|
|
||||||
{
|
|
||||||
return is_queued(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type QTorrentHandle::total_size() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return torrent_handle::get_torrent_info().total_size();
|
|
||||||
#else
|
|
||||||
return torrent_handle::torrent_file()->total_size();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type QTorrentHandle::piece_length() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return torrent_handle::get_torrent_info().piece_length();
|
|
||||||
#else
|
|
||||||
return torrent_handle::torrent_file()->piece_length();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int QTorrentHandle::num_pieces() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return torrent_handle::get_torrent_info().num_pieces();
|
|
||||||
#else
|
|
||||||
return torrent_handle::torrent_file()->num_pieces();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::first_last_piece_first() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
torrent_info const* t = &get_torrent_info();
|
|
||||||
#else
|
|
||||||
boost::intrusive_ptr<torrent_info const> t = torrent_file();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Get int first media file
|
|
||||||
int index = 0;
|
|
||||||
for (index = 0; index < t->num_files(); ++index) {
|
|
||||||
QString path = misc::toQStringU(t->file_at(index).path);
|
|
||||||
const QString ext = fsutils::fileExtension(path);
|
|
||||||
if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= t->num_files()) // No media file
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QPair<int, int> extremities = get_file_extremity_pieces(*t, index);
|
|
||||||
|
|
||||||
return (torrent_handle::piece_priority(extremities.first) == 7)
|
|
||||||
&& (torrent_handle::piece_priority(extremities.second) == 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::save_path() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path()));
|
|
||||||
#else
|
|
||||||
return fsutils::fromNativePath(misc::toQStringU(status(torrent_handle::query_save_path).save_path));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::save_path_parsed() const
|
|
||||||
{
|
|
||||||
QString p;
|
|
||||||
if (has_metadata() && num_files() == 1) {
|
|
||||||
p = firstFileSavePath();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
p = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash()));
|
|
||||||
if (p.isEmpty())
|
|
||||||
p = save_path();
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList QTorrentHandle::url_seeds() const
|
|
||||||
{
|
|
||||||
QStringList res;
|
|
||||||
try {
|
|
||||||
const std::set<std::string> existing_seeds = torrent_handle::url_seeds();
|
|
||||||
|
|
||||||
std::set<std::string>::const_iterator it = existing_seeds.begin();
|
|
||||||
std::set<std::string>::const_iterator itend = existing_seeds.end();
|
|
||||||
for (; it != itend; ++it) {
|
|
||||||
qDebug("URL Seed: %s", it->c_str());
|
|
||||||
res << misc::toQString(*it);
|
|
||||||
}
|
|
||||||
} catch(std::exception &e) {
|
|
||||||
std::cout << "ERROR: Failed to convert the URL seed" << std::endl;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the size of the torrent without the filtered files
|
|
||||||
size_type QTorrentHandle::actual_size() const
|
|
||||||
{
|
|
||||||
return status(query_accurate_download_counters).total_wanted;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::has_filtered_pieces() const
|
|
||||||
{
|
|
||||||
const std::vector<int> piece_priorities = torrent_handle::piece_priorities();
|
|
||||||
foreach (const int priority, piece_priorities)
|
|
||||||
if (priority == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QTorrentHandle::num_files() const
|
|
||||||
{
|
|
||||||
if (!has_metadata())
|
|
||||||
return -1;
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return torrent_handle::get_torrent_info().num_files();
|
|
||||||
#else
|
|
||||||
return torrent_handle::torrent_file()->num_files();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::filename_at(unsigned int index) const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files());
|
|
||||||
#else
|
|
||||||
Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files());
|
|
||||||
#endif
|
|
||||||
return fsutils::fileName(filepath_at(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
size_type QTorrentHandle::filesize_at(unsigned int index) const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files());
|
|
||||||
return torrent_handle::get_torrent_info().files().file_size(index);
|
|
||||||
#else
|
|
||||||
Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files());
|
|
||||||
return torrent_handle::torrent_file()->files().file_size(index);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::filepath_at(unsigned int index) const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return filepath_at(torrent_handle::get_torrent_info(), index);
|
|
||||||
#else
|
|
||||||
return filepath_at(*torrent_handle::torrent_file(), index);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::orig_filepath_at(unsigned int index) const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::get_torrent_info().orig_files().file_path(index)));
|
|
||||||
#else
|
|
||||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::torrent_file()->orig_files().file_path(index)));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent_status::state_t QTorrentHandle::state() const
|
|
||||||
{
|
|
||||||
return status(0x0).state;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::creator() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return misc::toQStringU(torrent_handle::get_torrent_info().creator());
|
|
||||||
#else
|
|
||||||
return misc::toQStringU(torrent_handle::torrent_file()->creator());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::comment() const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return misc::toQStringU(torrent_handle::get_torrent_info().comment());
|
|
||||||
#else
|
|
||||||
return misc::toQStringU(torrent_handle::torrent_file()->comment());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_checking() const
|
|
||||||
{
|
|
||||||
return is_checking(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a list of absolute paths corresponding
|
|
||||||
// to all files in a torrent
|
|
||||||
QStringList QTorrentHandle::absolute_files_path() const
|
|
||||||
{
|
|
||||||
QDir saveDir(save_path());
|
|
||||||
QStringList res;
|
|
||||||
for (int i = 0; i<num_files(); ++i)
|
|
||||||
res << fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i)));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList QTorrentHandle::absolute_files_path_uneeded() const
|
|
||||||
{
|
|
||||||
QDir saveDir(save_path());
|
|
||||||
QStringList res;
|
|
||||||
std::vector<int> fp = torrent_handle::file_priorities();
|
|
||||||
for (uint i = 0; i < fp.size(); ++i) {
|
|
||||||
if (fp[i] == 0) {
|
|
||||||
const QString file_path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i)));
|
|
||||||
if (file_path.contains(".unwanted"))
|
|
||||||
res << file_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::has_missing_files() const
|
|
||||||
{
|
|
||||||
const QStringList paths = absolute_files_path();
|
|
||||||
foreach (const QString &path, paths)
|
|
||||||
if (!QFile::exists(path)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QTorrentHandle::queue_position() const
|
|
||||||
{
|
|
||||||
return queue_position(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_seed() const
|
|
||||||
{
|
|
||||||
// Affected by bug http://code.rasterbar.com/libtorrent/ticket/402
|
|
||||||
//return torrent_handle::is_seed();
|
|
||||||
// May suffer from approximation problems
|
|
||||||
//return (progress() == 1.);
|
|
||||||
// This looks safe
|
|
||||||
return is_seed(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_sequential_download() const
|
|
||||||
{
|
|
||||||
return status(0x0).sequential_download;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::priv() const
|
|
||||||
{
|
|
||||||
if (!has_metadata())
|
|
||||||
return false;
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
return torrent_handle::get_torrent_info().priv();
|
|
||||||
#else
|
|
||||||
return torrent_handle::torrent_file()->priv();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::firstFileSavePath() const
|
|
||||||
{
|
|
||||||
Q_ASSERT(has_metadata());
|
|
||||||
QString fsave_path = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash()));
|
|
||||||
if (fsave_path.isEmpty())
|
|
||||||
fsave_path = save_path();
|
|
||||||
if (!fsave_path.endsWith("/"))
|
|
||||||
fsave_path += "/";
|
|
||||||
fsave_path += filepath_at(0);
|
|
||||||
// Remove .!qB extension
|
|
||||||
if (fsave_path.endsWith(".!qB", Qt::CaseInsensitive))
|
|
||||||
fsave_path.chop(4);
|
|
||||||
return fsave_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::root_path() const
|
|
||||||
{
|
|
||||||
if (num_files() < 2)
|
|
||||||
return save_path();
|
|
||||||
QString first_filepath = filepath_at(0);
|
|
||||||
const int slashIndex = first_filepath.indexOf("/");
|
|
||||||
if (slashIndex >= 0)
|
|
||||||
return QDir(save_path()).absoluteFilePath(first_filepath.left(slashIndex));
|
|
||||||
return save_path();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::has_error() const
|
|
||||||
{
|
|
||||||
return has_error(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::error() const
|
|
||||||
{
|
|
||||||
return misc::toQString(status(0x0).error);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::downloading_pieces(bitfield &bf) const
|
|
||||||
{
|
|
||||||
std::vector<partial_piece_info> queue;
|
|
||||||
torrent_handle::get_download_queue(queue);
|
|
||||||
|
|
||||||
std::vector<partial_piece_info>::const_iterator it = queue.begin();
|
|
||||||
std::vector<partial_piece_info>::const_iterator itend = queue.end();
|
|
||||||
for (; it!= itend; ++it)
|
|
||||||
bf.set_bit(it->piece_index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::has_metadata() const
|
|
||||||
{
|
|
||||||
return status(0x0).has_metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::file_progress(std::vector<size_type>& fp) const
|
|
||||||
{
|
|
||||||
torrent_handle::file_progress(fp, torrent_handle::piece_granularity);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTorrentState QTorrentHandle::torrentState() const
|
|
||||||
{
|
|
||||||
QTorrentState state = QTorrentState::Unknown;
|
|
||||||
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
|
||||||
|
|
||||||
if (is_paused(s)) {
|
|
||||||
if (has_error(s))
|
|
||||||
state = QTorrentState::Error;
|
|
||||||
else
|
|
||||||
state = is_seed(s) ? QTorrentState::PausedUploading : QTorrentState::PausedDownloading;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (QBtSession::instance()->isQueueingEnabled() && is_queued(s)) {
|
|
||||||
state = is_seed(s) ? QTorrentState::QueuedUploading : QTorrentState::QueuedDownloading;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (s.state) {
|
|
||||||
case torrent_status::finished:
|
|
||||||
case torrent_status::seeding:
|
|
||||||
state = s.upload_payload_rate > 0 ? QTorrentState::Uploading : QTorrentState::StalledUploading;
|
|
||||||
break;
|
|
||||||
case torrent_status::allocating:
|
|
||||||
case torrent_status::checking_files:
|
|
||||||
case torrent_status::queued_for_checking:
|
|
||||||
case torrent_status::checking_resume_data:
|
|
||||||
state = is_seed(s) ? QTorrentState::CheckingUploading : QTorrentState::CheckingDownloading;
|
|
||||||
break;
|
|
||||||
case torrent_status::downloading:
|
|
||||||
case torrent_status::downloading_metadata:
|
|
||||||
state = s.download_payload_rate > 0 ? QTorrentState::Downloading : QTorrentState::StalledDownloading;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qWarning("Unrecognized torrent status, should not happen!!! status was %d", this->state());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
qulonglong QTorrentHandle::eta() const
|
|
||||||
{
|
|
||||||
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
|
||||||
return QBtSession::instance()->getETA(hash(), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::toggleSequentialDownload()
|
|
||||||
{
|
|
||||||
if (is_valid() && has_metadata()) {
|
|
||||||
bool was_sequential = is_sequential_download();
|
|
||||||
set_sequential_download(!was_sequential);
|
|
||||||
if (!was_sequential)
|
|
||||||
prioritize_first_last_piece(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::toggleFirstLastPiecePrio()
|
|
||||||
{
|
|
||||||
if (is_valid() && has_metadata())
|
|
||||||
prioritize_first_last_piece(!first_last_piece_first());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_forced() const
|
|
||||||
{
|
|
||||||
return is_forced(status(0x0));
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Setters
|
|
||||||
//
|
|
||||||
|
|
||||||
void QTorrentHandle::pause() const
|
|
||||||
{
|
|
||||||
torrent_handle::auto_managed(false);
|
|
||||||
torrent_handle::pause();
|
|
||||||
if (!TorrentPersistentData::instance()->getHasMissingFiles(this->hash()))
|
|
||||||
torrent_handle::save_resume_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::resume(const bool force) const
|
|
||||||
{
|
|
||||||
if (has_error())
|
|
||||||
torrent_handle::clear_error();
|
|
||||||
torrent_handle::set_upload_mode(false);
|
|
||||||
|
|
||||||
const QString torrent_hash = hash();
|
|
||||||
TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance();
|
|
||||||
bool has_persistant_error = TorPersistent->hasError(torrent_hash);
|
|
||||||
TorPersistent->setErrorState(torrent_hash, false);
|
|
||||||
bool temp_path_enabled = Preferences::instance()->isTempPathEnabled();
|
|
||||||
if (has_persistant_error && temp_path_enabled) {
|
|
||||||
// Torrent was supposed to be seeding, checking again in final destination
|
|
||||||
qDebug("Resuming a torrent with error...");
|
|
||||||
const QString final_save_path = TorPersistent->getSavePath(torrent_hash);
|
|
||||||
qDebug("Torrent final path is: %s", qPrintable(final_save_path));
|
|
||||||
if (!final_save_path.isEmpty())
|
|
||||||
move_storage(final_save_path);
|
|
||||||
}
|
|
||||||
torrent_handle::auto_managed(!force);
|
|
||||||
torrent_handle::resume();
|
|
||||||
if (has_persistant_error && temp_path_enabled)
|
|
||||||
// Force recheck
|
|
||||||
torrent_handle::force_recheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::remove_url_seed(const QString& seed) const
|
|
||||||
{
|
|
||||||
torrent_handle::remove_url_seed(seed.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::add_url_seed(const QString& seed) const
|
|
||||||
{
|
|
||||||
const std::string str_seed = seed.toStdString();
|
|
||||||
qDebug("calling torrent_handle::add_url_seed(%s)", str_seed.c_str());
|
|
||||||
torrent_handle::add_url_seed(str_seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::set_tracker_login(const QString& username, const QString& password) const
|
|
||||||
{
|
|
||||||
torrent_handle::set_tracker_login(std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::move_storage(const QString& new_path) const
|
|
||||||
{
|
|
||||||
QString hashstr = hash();
|
|
||||||
|
|
||||||
if (TorrentTempData::isMoveInProgress(hashstr)) {
|
|
||||||
qDebug("enqueue move storage to %s", qPrintable(new_path));
|
|
||||||
TorrentTempData::enqueueMove(hashstr, new_path);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
QString old_path = save_path();
|
|
||||||
|
|
||||||
qDebug("move storage: %s to %s", qPrintable(old_path), qPrintable(new_path));
|
|
||||||
|
|
||||||
if (QDir(old_path) == QDir(new_path))
|
|
||||||
return;
|
|
||||||
|
|
||||||
TorrentTempData::startMove(hashstr, old_path, new_path);
|
|
||||||
|
|
||||||
// Create destination directory if necessary
|
|
||||||
// or move_storage() will fail...
|
|
||||||
QDir().mkpath(new_path);
|
|
||||||
// Actually move the storage
|
|
||||||
torrent_handle::move_storage(fsutils::toNativePath(new_path).toUtf8().constData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::save_torrent_file(const QString& path) const
|
|
||||||
{
|
|
||||||
if (!has_metadata()) return false;
|
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
torrent_info const* t = &get_torrent_info();
|
|
||||||
#else
|
|
||||||
boost::intrusive_ptr<torrent_info const> t = torrent_file();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
entry meta = bdecode(t->metadata().get(),
|
|
||||||
t->metadata().get() + t->metadata_size());
|
|
||||||
entry torrent_entry(entry::dictionary_t);
|
|
||||||
torrent_entry["info"] = meta;
|
|
||||||
if (!torrent_handle::trackers().empty())
|
|
||||||
torrent_entry["announce"] = torrent_handle::trackers().front().url;
|
|
||||||
|
|
||||||
vector<char> out;
|
|
||||||
bencode(back_inserter(out), torrent_entry);
|
|
||||||
QFile torrent_file(path);
|
|
||||||
if (!out.empty() && torrent_file.open(QIODevice::WriteOnly)) {
|
|
||||||
torrent_file.write(&out[0], out.size());
|
|
||||||
torrent_file.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::file_priority(int index, int priority) const
|
|
||||||
{
|
|
||||||
vector<int> priorities = torrent_handle::file_priorities();
|
|
||||||
if (priorities[index] != priority) {
|
|
||||||
priorities[index] = priority;
|
|
||||||
prioritize_files(priorities);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::prioritize_files(const vector<int> &files) const
|
|
||||||
{
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
torrent_info const& info = torrent_handle::get_torrent_info();
|
|
||||||
#else
|
|
||||||
boost::intrusive_ptr<torrent_info const> info_ptr = torrent_handle::torrent_file();
|
|
||||||
torrent_info const& info = *info_ptr;
|
|
||||||
#endif
|
|
||||||
if ((int)files.size() != info.num_files()) return;
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
bool was_seed = is_seed();
|
|
||||||
qDebug() << Q_FUNC_INFO << "Changing files priorities...";
|
|
||||||
torrent_handle::prioritize_files(files);
|
|
||||||
qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely...";
|
|
||||||
|
|
||||||
QString spath = save_path();
|
|
||||||
|
|
||||||
for (uint i = 0; i < files.size(); ++i) {
|
|
||||||
QString filepath = filepath_at(info, i);
|
|
||||||
// Move unwanted files to a .unwanted subfolder
|
|
||||||
if (files[i] == 0) {
|
|
||||||
QString old_abspath = QDir(spath).absoluteFilePath(filepath);
|
|
||||||
QString parent_abspath = fsutils::branchPath(old_abspath);
|
|
||||||
// Make sure the file does not already exists
|
|
||||||
if (QDir(parent_abspath).dirName() != ".unwanted") {
|
|
||||||
QString unwanted_abspath = parent_abspath + "/.unwanted";
|
|
||||||
QString new_abspath = unwanted_abspath + "/" + fsutils::fileName(filepath);
|
|
||||||
qDebug() << "Unwanted path is" << unwanted_abspath;
|
|
||||||
if (QFile::exists(new_abspath)) {
|
|
||||||
qWarning() << "File" << new_abspath << "already exists at destination.";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bool created = QDir().mkpath(unwanted_abspath);
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
qDebug() << "unwanted folder was created:" << created;
|
|
||||||
if (created) {
|
|
||||||
// Hide the folder on Windows
|
|
||||||
qDebug() << "Hiding folder (Windows)";
|
|
||||||
wstring win_path = fsutils::toNativePath(unwanted_abspath).toStdWString();
|
|
||||||
DWORD dwAttrs = GetFileAttributesW(win_path.c_str());
|
|
||||||
bool ret = SetFileAttributesW(win_path.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
|
|
||||||
Q_ASSERT(ret != 0); Q_UNUSED(ret);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Q_UNUSED(created);
|
|
||||||
#endif
|
|
||||||
QString parent_path = fsutils::branchPath(filepath);
|
|
||||||
if (!parent_path.isEmpty() && !parent_path.endsWith("/"))
|
|
||||||
parent_path += "/";
|
|
||||||
rename_file(i, parent_path + ".unwanted/" + fsutils::fileName(filepath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Move wanted files back to their original folder
|
|
||||||
if (files[i] > 0) {
|
|
||||||
QString parent_relpath = fsutils::branchPath(filepath);
|
|
||||||
if (QDir(parent_relpath).dirName() == ".unwanted") {
|
|
||||||
QString old_name = fsutils::fileName(filepath);
|
|
||||||
QString new_relpath = fsutils::branchPath(parent_relpath);
|
|
||||||
if (new_relpath.isEmpty())
|
|
||||||
rename_file(i, old_name);
|
|
||||||
else
|
|
||||||
rename_file(i, QDir(new_relpath).filePath(old_name));
|
|
||||||
// Remove .unwanted directory if empty
|
|
||||||
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + new_relpath).absoluteFilePath(".unwanted");
|
|
||||||
QDir(spath + "/" + new_relpath).rmdir(".unwanted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (was_seed && !is_seed()) {
|
|
||||||
qDebug() << "Torrent is no longer SEEDING";
|
|
||||||
// Save seed status
|
|
||||||
TorrentPersistentData::instance()->saveSeedStatus(*this);
|
|
||||||
// Move to temp folder if necessary
|
|
||||||
const Preferences* const pref = Preferences::instance();
|
|
||||||
if (pref->isTempPathEnabled()) {
|
|
||||||
QString tmp_path = pref->getTempPath();
|
|
||||||
qDebug() << "tmp folder is enabled, move torrent to " << tmp_path << " from " << spath;
|
|
||||||
move_storage(tmp_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::prioritize_first_last_piece(int file_index, bool b) const
|
|
||||||
{
|
|
||||||
// Determine the priority to set
|
|
||||||
int prio = b ? 7 : torrent_handle::file_priority(file_index);
|
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
|
||||||
torrent_info const* tf = &get_torrent_info();
|
|
||||||
#else
|
|
||||||
boost::intrusive_ptr<torrent_info const> tf = torrent_file();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QPair<int, int> extremities = get_file_extremity_pieces(*tf, file_index);
|
|
||||||
piece_priority(extremities.first, prio);
|
|
||||||
piece_priority(extremities.second, prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::prioritize_first_last_piece(bool b) const
|
|
||||||
{
|
|
||||||
if (!has_metadata()) return;
|
|
||||||
// Download first and last pieces first for all media files in the torrent
|
|
||||||
const uint nbfiles = num_files();
|
|
||||||
for (uint index = 0; index < nbfiles; ++index) {
|
|
||||||
const QString path = filepath_at(index);
|
|
||||||
const QString ext = fsutils::fileExtension(path);
|
|
||||||
if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) {
|
|
||||||
qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first";
|
|
||||||
prioritize_first_last_piece(index, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QTorrentHandle::rename_file(int index, const QString& name) const
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO << index << name;
|
|
||||||
torrent_handle::rename_file(index, std::string(fsutils::toNativePath(name).toUtf8().constData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Operators
|
|
||||||
//
|
|
||||||
|
|
||||||
bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const
|
|
||||||
{
|
|
||||||
return info_hash() == new_h.info_hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return status.paused && !status.auto_managed;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QTorrentHandle::queue_position(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
if (status.queue_position < 0)
|
|
||||||
return -1;
|
|
||||||
return status.queue_position + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return status.paused && status.auto_managed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return status.state == torrent_status::finished
|
|
||||||
|| status.state == torrent_status::seeding;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return status.state == torrent_status::checking_files
|
|
||||||
|| status.state == torrent_status::checking_resume_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::has_error(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return status.paused && !status.error.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
float QTorrentHandle::progress(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
if (!status.total_wanted)
|
|
||||||
return 0.;
|
|
||||||
if (status.total_wanted_done == status.total_wanted)
|
|
||||||
return 1.;
|
|
||||||
float progress = (float) status.total_wanted_done / (float) status.total_wanted;
|
|
||||||
Q_ASSERT(progress >= 0.f && progress <= 1.f);
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsigned int index)
|
|
||||||
{
|
|
||||||
return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTorrentHandle::is_forced(const libtorrent::torrent_status &status)
|
|
||||||
{
|
|
||||||
return !status.paused && !status.auto_managed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QTorrentState::QTorrentState(int value)
|
|
||||||
: m_value(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QTorrentState::toString() const
|
|
||||||
{
|
|
||||||
switch (m_value) {
|
|
||||||
case Error:
|
|
||||||
return "error";
|
|
||||||
case Uploading:
|
|
||||||
return "uploading";
|
|
||||||
case PausedUploading:
|
|
||||||
return "pausedUP";
|
|
||||||
case QueuedUploading:
|
|
||||||
return "queuedUP";
|
|
||||||
case StalledUploading:
|
|
||||||
return "stalledUP";
|
|
||||||
case CheckingUploading:
|
|
||||||
return "checkingUP";
|
|
||||||
case Downloading:
|
|
||||||
return "downloading";
|
|
||||||
case PausedDownloading:
|
|
||||||
return "pausedDL";
|
|
||||||
case QueuedDownloading:
|
|
||||||
return "queuedDL";
|
|
||||||
case StalledDownloading:
|
|
||||||
return "stalledDL";
|
|
||||||
case CheckingDownloading:
|
|
||||||
return "checkingDL";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QTorrentState::operator int() const
|
|
||||||
{
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef QTORRENTHANDLE_H
|
|
||||||
#define QTORRENTHANDLE_H
|
|
||||||
|
|
||||||
#include <libtorrent/torrent_handle.hpp>
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QStringList;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class QTorrentState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Unknown = -1,
|
|
||||||
|
|
||||||
Error,
|
|
||||||
|
|
||||||
Uploading,
|
|
||||||
PausedUploading,
|
|
||||||
QueuedUploading,
|
|
||||||
StalledUploading,
|
|
||||||
CheckingUploading,
|
|
||||||
|
|
||||||
Downloading,
|
|
||||||
PausedDownloading,
|
|
||||||
QueuedDownloading,
|
|
||||||
StalledDownloading,
|
|
||||||
CheckingDownloading
|
|
||||||
};
|
|
||||||
|
|
||||||
QTorrentState(int value);
|
|
||||||
|
|
||||||
operator int() const;
|
|
||||||
QString toString() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A wrapper for torrent_handle in libtorrent
|
|
||||||
// to interact well with Qt types
|
|
||||||
class QTorrentHandle: public libtorrent::torrent_handle
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
//
|
|
||||||
// Constructors
|
|
||||||
//
|
|
||||||
|
|
||||||
QTorrentHandle() {}
|
|
||||||
explicit QTorrentHandle(const libtorrent::torrent_handle& h);
|
|
||||||
|
|
||||||
//
|
|
||||||
// Getters
|
|
||||||
//
|
|
||||||
QString hash() const;
|
|
||||||
QString name() const;
|
|
||||||
QString current_tracker() const;
|
|
||||||
bool is_paused() const;
|
|
||||||
bool has_filtered_pieces() const;
|
|
||||||
libtorrent::size_type total_size() const;
|
|
||||||
libtorrent::size_type piece_length() const;
|
|
||||||
int num_pieces() const;
|
|
||||||
QString save_path() const;
|
|
||||||
QString save_path_parsed() const;
|
|
||||||
QStringList url_seeds() const;
|
|
||||||
libtorrent::size_type actual_size() const;
|
|
||||||
int num_files() const;
|
|
||||||
int queue_position() const;
|
|
||||||
bool is_queued() const;
|
|
||||||
QString filename_at(unsigned int index) const;
|
|
||||||
libtorrent::size_type filesize_at(unsigned int index) const;
|
|
||||||
QString filepath_at(unsigned int index) const;
|
|
||||||
QString orig_filepath_at(unsigned int index) const;
|
|
||||||
libtorrent::torrent_status::state_t state() const;
|
|
||||||
QString creator() const;
|
|
||||||
QString comment() const;
|
|
||||||
QStringList absolute_files_path() const;
|
|
||||||
QStringList absolute_files_path_uneeded() const;
|
|
||||||
bool has_missing_files() const;
|
|
||||||
bool is_seed() const;
|
|
||||||
bool is_checking() const;
|
|
||||||
bool is_sequential_download() const;
|
|
||||||
QString creation_date() const;
|
|
||||||
qlonglong creation_date_unix() const;
|
|
||||||
bool priv() const;
|
|
||||||
bool first_last_piece_first() const;
|
|
||||||
QString root_path() const;
|
|
||||||
QString firstFileSavePath() const;
|
|
||||||
bool has_error() const;
|
|
||||||
QString error() const;
|
|
||||||
void downloading_pieces(libtorrent::bitfield& bf) const;
|
|
||||||
bool has_metadata() const;
|
|
||||||
void file_progress(std::vector<libtorrent::size_type>& fp) const;
|
|
||||||
QTorrentState torrentState() const;
|
|
||||||
qulonglong eta() const;
|
|
||||||
void toggleSequentialDownload();
|
|
||||||
void toggleFirstLastPiecePrio();
|
|
||||||
bool is_forced() const;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Setters
|
|
||||||
//
|
|
||||||
void pause() const;
|
|
||||||
void resume(const bool force = false) const;
|
|
||||||
void remove_url_seed(const QString& seed) const;
|
|
||||||
void add_url_seed(const QString& seed) const;
|
|
||||||
void set_tracker_login(const QString& username, const QString& password) const;
|
|
||||||
void move_storage(const QString& path) const;
|
|
||||||
void prioritize_first_last_piece(bool b) const;
|
|
||||||
void rename_file(int index, const QString& name) const;
|
|
||||||
bool save_torrent_file(const QString& path) const;
|
|
||||||
void prioritize_files(const std::vector<int>& files) const;
|
|
||||||
void file_priority(int index, int priority) const;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Operators
|
|
||||||
//
|
|
||||||
bool operator ==(const QTorrentHandle& new_h) const;
|
|
||||||
|
|
||||||
static bool is_paused(const libtorrent::torrent_status &status);
|
|
||||||
static int queue_position(const libtorrent::torrent_status &status);
|
|
||||||
static bool is_queued(const libtorrent::torrent_status &status);
|
|
||||||
static bool is_seed(const libtorrent::torrent_status &status);
|
|
||||||
static bool is_checking(const libtorrent::torrent_status &status);
|
|
||||||
static bool has_error(const libtorrent::torrent_status &status);
|
|
||||||
static float progress(const libtorrent::torrent_status &status);
|
|
||||||
static QString filepath_at(const libtorrent::torrent_info &info, unsigned int index);
|
|
||||||
static bool is_forced(const libtorrent::torrent_status &status);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void prioritize_first_last_piece(int file_index, bool b) const;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,716 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QPalette>
|
|
||||||
|
|
||||||
#include "torrentmodel.h"
|
|
||||||
#include "torrentpersistentdata.h"
|
|
||||||
#include "qbtsession.h"
|
|
||||||
#include "fs_utils.h"
|
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
QIcon get_paused_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/paused.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_queued_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/queued.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_downloading_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/downloading.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_stalled_downloading_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/stalledDL.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_uploading_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/uploading.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_stalled_uploading_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/stalledUP.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_completed_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/completed.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_checking_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/checking.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon get_error_icon() {
|
|
||||||
static QIcon cached = QIcon(":/icons/skin/error.png");
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDarkTheme()
|
|
||||||
{
|
|
||||||
QPalette pal = QApplication::palette();
|
|
||||||
// QPalette::Base is used for the background of the Treeview
|
|
||||||
QColor color = pal.color(QPalette::Active, QPalette::Base);
|
|
||||||
return (color.lightness() < 127);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentStatusReport::TorrentStatusReport()
|
|
||||||
: nb_downloading(0)
|
|
||||||
, nb_seeding(0)
|
|
||||||
, nb_completed(0)
|
|
||||||
, nb_active(0)
|
|
||||||
, nb_inactive(0)
|
|
||||||
, nb_paused(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentModelItem::TorrentModelItem(const QTorrentHandle &h)
|
|
||||||
: m_torrent(h)
|
|
||||||
, m_lastStatus(h.status(torrent_handle::query_accurate_download_counters))
|
|
||||||
, m_addedTime(TorrentPersistentData::instance()->getAddedDate(h.hash()))
|
|
||||||
, m_label(TorrentPersistentData::instance()->getLabel(h.hash()))
|
|
||||||
, m_name(TorrentPersistentData::instance()->getName(h.hash()))
|
|
||||||
, m_hash(h.hash())
|
|
||||||
{
|
|
||||||
if (m_name.isEmpty())
|
|
||||||
m_name = h.name();
|
|
||||||
// If name is empty show the hash. This happens when magnet isn't retrieved.
|
|
||||||
if (m_name.isEmpty())
|
|
||||||
m_name = h.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModelItem::refreshStatus(libtorrent::torrent_status const& status) {
|
|
||||||
m_lastStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentModelItem::State TorrentModelItem::state() const {
|
|
||||||
try {
|
|
||||||
// Pause or Queued
|
|
||||||
if (m_torrent.is_paused(m_lastStatus)) {
|
|
||||||
if (TorrentPersistentData::instance()->getHasMissingFiles(misc::toQString(m_lastStatus.info_hash)))
|
|
||||||
return STATE_PAUSED_MISSING;
|
|
||||||
else
|
|
||||||
return m_torrent.is_seed(m_lastStatus) ? STATE_PAUSED_UP : STATE_PAUSED_DL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_torrent.is_queued(m_lastStatus)
|
|
||||||
&& m_lastStatus.state != torrent_status::queued_for_checking
|
|
||||||
&& m_lastStatus.state != torrent_status::checking_resume_data
|
|
||||||
&& m_lastStatus.state != torrent_status::checking_files)
|
|
||||||
return m_torrent.is_seed(m_lastStatus) ? STATE_QUEUED_UP : STATE_QUEUED_DL;
|
|
||||||
|
|
||||||
// Other states
|
|
||||||
switch(m_lastStatus.state) {
|
|
||||||
case torrent_status::allocating:
|
|
||||||
return STATE_ALLOCATING;
|
|
||||||
case torrent_status::downloading_metadata:
|
|
||||||
return STATE_DOWNLOADING_META;
|
|
||||||
case torrent_status::downloading:
|
|
||||||
if (!m_torrent.is_forced(m_lastStatus))
|
|
||||||
return m_lastStatus.download_payload_rate > 0 ? STATE_DOWNLOADING : STATE_STALLED_DL;
|
|
||||||
else
|
|
||||||
return STATE_FORCED_DL;
|
|
||||||
case torrent_status::finished:
|
|
||||||
case torrent_status::seeding:
|
|
||||||
if (!m_torrent.is_forced(m_lastStatus))
|
|
||||||
return m_lastStatus.upload_payload_rate > 0 ? STATE_SEEDING : STATE_STALLED_UP;
|
|
||||||
else
|
|
||||||
return STATE_FORCED_UP;
|
|
||||||
case torrent_status::queued_for_checking:
|
|
||||||
return STATE_QUEUED_CHECK;
|
|
||||||
case torrent_status::checking_resume_data:
|
|
||||||
return STATE_QUEUED_FASTCHECK;
|
|
||||||
case torrent_status::checking_files:
|
|
||||||
return m_torrent.is_seed(m_lastStatus) ? STATE_CHECKING_UP : STATE_CHECKING_DL;
|
|
||||||
default:
|
|
||||||
return STATE_INVALID;
|
|
||||||
}
|
|
||||||
} catch(invalid_handle&) {
|
|
||||||
return STATE_INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon TorrentModelItem::getIconByState(State state) {
|
|
||||||
switch (state) {
|
|
||||||
case STATE_DOWNLOADING:
|
|
||||||
case STATE_DOWNLOADING_META:
|
|
||||||
case STATE_FORCED_DL:
|
|
||||||
return get_downloading_icon();
|
|
||||||
case STATE_ALLOCATING:
|
|
||||||
case STATE_STALLED_DL:
|
|
||||||
return get_stalled_downloading_icon();
|
|
||||||
case STATE_STALLED_UP:
|
|
||||||
return get_stalled_uploading_icon();
|
|
||||||
case STATE_SEEDING:
|
|
||||||
case STATE_FORCED_UP:
|
|
||||||
return get_uploading_icon();
|
|
||||||
case STATE_PAUSED_DL:
|
|
||||||
return get_paused_icon();
|
|
||||||
case STATE_PAUSED_UP:
|
|
||||||
return get_completed_icon();
|
|
||||||
case STATE_QUEUED_DL:
|
|
||||||
case STATE_QUEUED_UP:
|
|
||||||
return get_queued_icon();
|
|
||||||
case STATE_CHECKING_UP:
|
|
||||||
case STATE_CHECKING_DL:
|
|
||||||
case STATE_QUEUED_CHECK:
|
|
||||||
case STATE_QUEUED_FASTCHECK:
|
|
||||||
return get_checking_icon();
|
|
||||||
case STATE_INVALID:
|
|
||||||
case STATE_PAUSED_MISSING:
|
|
||||||
return get_error_icon();
|
|
||||||
default:
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return get_error_icon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor TorrentModelItem::getColorByState(State state) {
|
|
||||||
bool dark = isDarkTheme();
|
|
||||||
switch (state) {
|
|
||||||
case STATE_DOWNLOADING:
|
|
||||||
case STATE_DOWNLOADING_META:
|
|
||||||
case STATE_FORCED_DL:
|
|
||||||
return QColor(34, 139, 34); // Forest Green
|
|
||||||
case STATE_ALLOCATING:
|
|
||||||
case STATE_STALLED_DL:
|
|
||||||
case STATE_STALLED_UP:
|
|
||||||
if (!dark)
|
|
||||||
return QColor(0, 0, 0); // Black
|
|
||||||
else
|
|
||||||
return QColor(255, 255, 255); // White
|
|
||||||
case STATE_SEEDING:
|
|
||||||
case STATE_FORCED_UP:
|
|
||||||
if (!dark)
|
|
||||||
return QColor(65, 105, 225); // Royal Blue
|
|
||||||
else
|
|
||||||
return QColor(100, 149, 237); // Cornflower Blue
|
|
||||||
case STATE_PAUSED_DL:
|
|
||||||
return QColor(250, 128, 114); // Salmon
|
|
||||||
case STATE_PAUSED_UP:
|
|
||||||
if (!dark)
|
|
||||||
return QColor(0, 0, 139); // Dark Blue
|
|
||||||
else
|
|
||||||
return QColor(65, 105, 225); // Royal Blue
|
|
||||||
case STATE_PAUSED_MISSING:
|
|
||||||
return QColor(255, 0, 0); // red
|
|
||||||
case STATE_QUEUED_DL:
|
|
||||||
case STATE_QUEUED_UP:
|
|
||||||
case STATE_CHECKING_UP:
|
|
||||||
case STATE_CHECKING_DL:
|
|
||||||
case STATE_QUEUED_CHECK:
|
|
||||||
case STATE_QUEUED_FASTCHECK:
|
|
||||||
return QColor(0, 128, 128); // Teal
|
|
||||||
case STATE_INVALID:
|
|
||||||
return QColor(255, 0, 0); // red
|
|
||||||
default:
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return QColor(255, 0, 0); // red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentModelItem::setData(int column, const QVariant &value, int role)
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO << column << value;
|
|
||||||
if (role != Qt::DisplayRole) return false;
|
|
||||||
// Label, seed date and Name columns can be edited
|
|
||||||
switch(column) {
|
|
||||||
case TR_NAME:
|
|
||||||
m_name = value.toString();
|
|
||||||
TorrentPersistentData::instance()->saveName(m_torrent.hash(), m_name);
|
|
||||||
return true;
|
|
||||||
case TR_LABEL: {
|
|
||||||
QString new_label = value.toString();
|
|
||||||
if (m_label != new_label) {
|
|
||||||
QString old_label = m_label;
|
|
||||||
m_label = new_label;
|
|
||||||
TorrentPersistentData::instance()->saveLabel(m_torrent.hash(), new_label);
|
|
||||||
emit labelChanged(old_label, new_label);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant TorrentModelItem::data(int column, int role) const
|
|
||||||
{
|
|
||||||
if (role == Qt::DecorationRole && column == TR_NAME) {
|
|
||||||
return getIconByState(state());
|
|
||||||
}
|
|
||||||
if (role == Qt::ForegroundRole) {
|
|
||||||
return getColorByState(state());
|
|
||||||
}
|
|
||||||
if (role != Qt::DisplayRole && role != Qt::UserRole) return QVariant();
|
|
||||||
switch(column) {
|
|
||||||
case TR_NAME:
|
|
||||||
return m_name.isEmpty() ? m_torrent.name() : m_name;
|
|
||||||
case TR_PRIORITY: {
|
|
||||||
int pos = m_torrent.queue_position(m_lastStatus);
|
|
||||||
if (pos > -1)
|
|
||||||
return pos - HiddenData::getSize();
|
|
||||||
else
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
case TR_SIZE:
|
|
||||||
return m_lastStatus.has_metadata ? static_cast<qlonglong>(m_lastStatus.total_wanted) : -1;
|
|
||||||
case TR_PROGRESS:
|
|
||||||
return m_torrent.progress(m_lastStatus);
|
|
||||||
case TR_STATUS:
|
|
||||||
return state();
|
|
||||||
case TR_SEEDS: {
|
|
||||||
return (role == Qt::DisplayRole) ? m_lastStatus.num_seeds : m_lastStatus.num_complete;
|
|
||||||
}
|
|
||||||
case TR_PEERS: {
|
|
||||||
return (role == Qt::DisplayRole) ? (m_lastStatus.num_peers-m_lastStatus.num_seeds) : m_lastStatus.num_incomplete;
|
|
||||||
}
|
|
||||||
case TR_DLSPEED:
|
|
||||||
return m_lastStatus.download_payload_rate;
|
|
||||||
case TR_UPSPEED:
|
|
||||||
return m_lastStatus.upload_payload_rate;
|
|
||||||
case TR_ETA: {
|
|
||||||
// XXX: Is this correct?
|
|
||||||
if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_queued(m_lastStatus)) return MAX_ETA;
|
|
||||||
return QBtSession::instance()->getETA(m_hash, m_lastStatus);
|
|
||||||
}
|
|
||||||
case TR_RATIO:
|
|
||||||
return QBtSession::instance()->getRealRatio(m_lastStatus);
|
|
||||||
case TR_LABEL:
|
|
||||||
return m_label;
|
|
||||||
case TR_ADD_DATE:
|
|
||||||
return m_addedTime;
|
|
||||||
case TR_SEED_DATE:
|
|
||||||
return m_lastStatus.completed_time ? QDateTime::fromTime_t(m_lastStatus.completed_time) : QDateTime();
|
|
||||||
case TR_TRACKER:
|
|
||||||
return misc::toQString(m_lastStatus.current_tracker);
|
|
||||||
case TR_DLLIMIT:
|
|
||||||
return m_torrent.download_limit();
|
|
||||||
case TR_UPLIMIT:
|
|
||||||
return m_torrent.upload_limit();
|
|
||||||
case TR_AMOUNT_DOWNLOADED:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.all_time_download);
|
|
||||||
case TR_AMOUNT_UPLOADED:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.all_time_upload);
|
|
||||||
case TR_AMOUNT_DOWNLOADED_SESSION:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.total_payload_download);
|
|
||||||
case TR_AMOUNT_UPLOADED_SESSION:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.total_payload_upload);
|
|
||||||
case TR_AMOUNT_LEFT:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.total_wanted - m_lastStatus.total_wanted_done);
|
|
||||||
case TR_TIME_ELAPSED:
|
|
||||||
return (role == Qt::DisplayRole) ? m_lastStatus.active_time : m_lastStatus.seeding_time;
|
|
||||||
case TR_SAVE_PATH:
|
|
||||||
return fsutils::toNativePath(m_torrent.save_path_parsed());
|
|
||||||
case TR_COMPLETED:
|
|
||||||
return static_cast<qlonglong>(m_lastStatus.total_wanted_done);
|
|
||||||
case TR_RATIO_LIMIT: {
|
|
||||||
QString hash = misc::toQString(m_lastStatus.info_hash);
|
|
||||||
return QBtSession::instance()->getMaxRatioPerTorrent(hash, NULL);
|
|
||||||
}
|
|
||||||
case TR_SEEN_COMPLETE_DATE:
|
|
||||||
return m_lastStatus.last_seen_complete ? QDateTime::fromTime_t(m_lastStatus.last_seen_complete) : QDateTime();
|
|
||||||
case TR_LAST_ACTIVITY:
|
|
||||||
if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_checking(m_lastStatus))
|
|
||||||
return -1;
|
|
||||||
if (m_lastStatus.time_since_upload < m_lastStatus.time_since_download)
|
|
||||||
return m_lastStatus.time_since_upload;
|
|
||||||
else
|
|
||||||
return m_lastStatus.time_since_download;
|
|
||||||
case TR_TOTAL_SIZE:
|
|
||||||
return m_lastStatus.has_metadata ? static_cast<qlonglong>(m_torrent.total_size()) : -1;
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QTorrentHandle TorrentModelItem::torrentHandle() const
|
|
||||||
{
|
|
||||||
return m_torrent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TORRENT MODEL
|
|
||||||
|
|
||||||
TorrentModel::TorrentModel(QObject *parent) :
|
|
||||||
QAbstractListModel(parent), m_refreshInterval(2000)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::populate() {
|
|
||||||
// Load the torrents
|
|
||||||
std::vector<torrent_handle> torrents = QBtSession::instance()->getSession()->get_torrents();
|
|
||||||
|
|
||||||
std::vector<torrent_handle>::const_iterator it = torrents.begin();
|
|
||||||
std::vector<torrent_handle>::const_iterator itend = torrents.end();
|
|
||||||
for ( ; it != itend; ++it) {
|
|
||||||
const QTorrentHandle h(*it);
|
|
||||||
if (HiddenData::hasData(h.hash()))
|
|
||||||
continue;
|
|
||||||
addTorrent(h);
|
|
||||||
}
|
|
||||||
// Refresh timer
|
|
||||||
connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(forceModelRefresh()));
|
|
||||||
m_refreshTimer.start(m_refreshInterval);
|
|
||||||
// Listen for torrent changes
|
|
||||||
connect(QBtSession::instance(), SIGNAL(addedTorrent(QTorrentHandle)), SLOT(addTorrent(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(torrentAboutToBeRemoved(QTorrentHandle)), SLOT(handleTorrentAboutToBeRemoved(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(finishedTorrent(QTorrentHandle)), SLOT(handleFinishedTorrent(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(resumedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(torrentFinishedChecking(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
|
|
||||||
connect(QBtSession::instance(), SIGNAL(stateUpdate(std::vector<libtorrent::torrent_status>)), SLOT(stateUpdated(std::vector<libtorrent::torrent_status>)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentModel::~TorrentModel() {
|
|
||||||
qDebug() << Q_FUNC_INFO << "ENTER";
|
|
||||||
qDeleteAll(m_torrents);
|
|
||||||
m_torrents.clear();
|
|
||||||
qDebug() << Q_FUNC_INFO << "EXIT";
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant TorrentModel::headerData(int section, Qt::Orientation orientation,
|
|
||||||
int role) const
|
|
||||||
{
|
|
||||||
if (orientation == Qt::Horizontal) {
|
|
||||||
if (role == Qt::DisplayRole) {
|
|
||||||
switch(section) {
|
|
||||||
case TorrentModelItem::TR_NAME: return tr("Name", "i.e: torrent name");
|
|
||||||
case TorrentModelItem::TR_PRIORITY: return "#";
|
|
||||||
case TorrentModelItem::TR_SIZE: return tr("Size", "i.e: torrent size");
|
|
||||||
case TorrentModelItem::TR_PROGRESS: return tr("Done", "% Done");
|
|
||||||
case TorrentModelItem::TR_STATUS: return tr("Status", "Torrent status (e.g. downloading, seeding, paused)");
|
|
||||||
case TorrentModelItem::TR_SEEDS: return tr("Seeds", "i.e. full sources (often untranslated)");
|
|
||||||
case TorrentModelItem::TR_PEERS: return tr("Peers", "i.e. partial sources (often untranslated)");
|
|
||||||
case TorrentModelItem::TR_DLSPEED: return tr("Down Speed", "i.e: Download speed");
|
|
||||||
case TorrentModelItem::TR_UPSPEED: return tr("Up Speed", "i.e: Upload speed");
|
|
||||||
case TorrentModelItem::TR_RATIO: return tr("Ratio", "Share ratio");
|
|
||||||
case TorrentModelItem::TR_ETA: return tr("ETA", "i.e: Estimated Time of Arrival / Time left");
|
|
||||||
case TorrentModelItem::TR_LABEL: return tr("Label");
|
|
||||||
case TorrentModelItem::TR_ADD_DATE: return tr("Added On", "Torrent was added to transfer list on 01/01/2010 08:00");
|
|
||||||
case TorrentModelItem::TR_SEED_DATE: return tr("Completed On", "Torrent was completed on 01/01/2010 08:00");
|
|
||||||
case TorrentModelItem::TR_TRACKER: return tr("Tracker");
|
|
||||||
case TorrentModelItem::TR_DLLIMIT: return tr("Down Limit", "i.e: Download limit");
|
|
||||||
case TorrentModelItem::TR_UPLIMIT: return tr("Up Limit", "i.e: Upload limit");
|
|
||||||
case TorrentModelItem::TR_AMOUNT_DOWNLOADED: return tr("Downloaded", "Amount of data downloaded (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_AMOUNT_UPLOADED: return tr("Uploaded", "Amount of data uploaded (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_AMOUNT_DOWNLOADED_SESSION: return tr("Session Download", "Amount of data downloaded since program open (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_AMOUNT_UPLOADED_SESSION: return tr("Session Upload", "Amount of data uploaded since program open (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_AMOUNT_LEFT: return tr("Remaining", "Amount of data left to download (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_TIME_ELAPSED: return tr("Time Active", "Time (duration) the torrent is active (not paused)");
|
|
||||||
case TorrentModelItem::TR_SAVE_PATH: return tr("Save path", "Torrent save path");
|
|
||||||
case TorrentModelItem::TR_COMPLETED: return tr("Completed", "Amount of data completed (e.g. in MB)");
|
|
||||||
case TorrentModelItem::TR_RATIO_LIMIT: return tr("Ratio Limit", "Upload share ratio limit");
|
|
||||||
case TorrentModelItem::TR_SEEN_COMPLETE_DATE: return tr("Last Seen Complete", "Indicates the time when the torrent was last seen complete/whole");
|
|
||||||
case TorrentModelItem::TR_LAST_ACTIVITY: return tr("Last Activity", "Time passed since a chunk was downloaded/uploaded");
|
|
||||||
case TorrentModelItem::TR_TOTAL_SIZE: return tr("Total Size", "i.e. Size including unwanted data");
|
|
||||||
default:
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (role == Qt::TextAlignmentRole) {
|
|
||||||
switch(section) {
|
|
||||||
case TorrentModelItem::TR_AMOUNT_DOWNLOADED:
|
|
||||||
case TorrentModelItem::TR_AMOUNT_UPLOADED:
|
|
||||||
case TorrentModelItem::TR_AMOUNT_DOWNLOADED_SESSION:
|
|
||||||
case TorrentModelItem::TR_AMOUNT_UPLOADED_SESSION:
|
|
||||||
case TorrentModelItem::TR_AMOUNT_LEFT:
|
|
||||||
case TorrentModelItem::TR_COMPLETED:
|
|
||||||
case TorrentModelItem::TR_SIZE:
|
|
||||||
case TorrentModelItem::TR_TOTAL_SIZE:
|
|
||||||
case TorrentModelItem::TR_ETA:
|
|
||||||
case TorrentModelItem::TR_SEEDS:
|
|
||||||
case TorrentModelItem::TR_PEERS:
|
|
||||||
case TorrentModelItem::TR_UPSPEED:
|
|
||||||
case TorrentModelItem::TR_DLSPEED:
|
|
||||||
case TorrentModelItem::TR_UPLIMIT:
|
|
||||||
case TorrentModelItem::TR_DLLIMIT:
|
|
||||||
case TorrentModelItem::TR_RATIO_LIMIT:
|
|
||||||
case TorrentModelItem::TR_RATIO:
|
|
||||||
case TorrentModelItem::TR_PRIORITY:
|
|
||||||
case TorrentModelItem::TR_LAST_ACTIVITY:
|
|
||||||
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
|
|
||||||
default:
|
|
||||||
return QAbstractListModel::headerData(section, orientation, role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant TorrentModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid()) return QVariant();
|
|
||||||
try {
|
|
||||||
if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount())
|
|
||||||
return m_torrents[index.row()]->data(index.column(), role);
|
|
||||||
} catch(invalid_handle&) {}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO << value;
|
|
||||||
if (!index.isValid() || role != Qt::DisplayRole) return false;
|
|
||||||
qDebug("Index is valid and role is DisplayRole");
|
|
||||||
try {
|
|
||||||
if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount()) {
|
|
||||||
bool change = m_torrents[index.row()]->setData(index.column(), value, role);
|
|
||||||
if (change)
|
|
||||||
notifyTorrentChanged(index.row());
|
|
||||||
return change;
|
|
||||||
}
|
|
||||||
} catch(invalid_handle&) {}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TorrentModel::torrentRow(const QString &hash) const
|
|
||||||
{
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
|
|
||||||
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
|
|
||||||
for ( ; it != itend; ++it) {
|
|
||||||
if ((*it)->hash() == hash) return row;
|
|
||||||
++row;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::addTorrent(const QTorrentHandle &h)
|
|
||||||
{
|
|
||||||
if (torrentRow(h.hash()) < 0) {
|
|
||||||
beginInsertTorrent(m_torrents.size());
|
|
||||||
TorrentModelItem *item = new TorrentModelItem(h);
|
|
||||||
connect(item, SIGNAL(labelChanged(QString,QString)), SLOT(handleTorrentLabelChange(QString,QString)));
|
|
||||||
m_torrents << item;
|
|
||||||
emit torrentAdded(item);
|
|
||||||
endInsertTorrent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::beginInsertTorrent(int row)
|
|
||||||
{
|
|
||||||
beginInsertRows(QModelIndex(), row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::endInsertTorrent()
|
|
||||||
{
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::beginRemoveTorrent(int row)
|
|
||||||
{
|
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::endRemoveTorrent()
|
|
||||||
{
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::handleTorrentUpdate(const QTorrentHandle &h)
|
|
||||||
{
|
|
||||||
const int row = torrentRow(h.hash());
|
|
||||||
if (row >= 0) {
|
|
||||||
// This line changes the torrent name when magnet is retrieved.
|
|
||||||
// When magnet link is added, "dn" parameter is used as name, but when metadata is retrieved
|
|
||||||
// we change the name with the retrieved torrent name.
|
|
||||||
m_torrents[row]->setData(TorrentModelItem::TR_NAME, h.name(), Qt::DisplayRole);
|
|
||||||
|
|
||||||
m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters));
|
|
||||||
notifyTorrentChanged(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::handleFinishedTorrent(const QTorrentHandle& h)
|
|
||||||
{
|
|
||||||
const int row = torrentRow(h.hash());
|
|
||||||
if (row < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Update completion date
|
|
||||||
m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters));
|
|
||||||
notifyTorrentChanged(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::notifyTorrentChanged(int row)
|
|
||||||
{
|
|
||||||
emit dataChanged(index(row, 0), index(row, columnCount()-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::setRefreshInterval(int refreshInterval)
|
|
||||||
{
|
|
||||||
if (m_refreshInterval != refreshInterval) {
|
|
||||||
m_refreshInterval = refreshInterval;
|
|
||||||
m_refreshTimer.stop();
|
|
||||||
m_refreshTimer.start(m_refreshInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::forceModelRefresh()
|
|
||||||
{
|
|
||||||
QBtSession::instance()->postTorrentUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentStatusReport TorrentModel::getTorrentStatusReport() const
|
|
||||||
{
|
|
||||||
TorrentStatusReport report;
|
|
||||||
|
|
||||||
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
|
|
||||||
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
|
|
||||||
for ( ; it != itend; ++it) {
|
|
||||||
switch((*it)->state()) {
|
|
||||||
case TorrentModelItem::STATE_DOWNLOADING:
|
|
||||||
case TorrentModelItem::STATE_FORCED_DL:
|
|
||||||
++report.nb_active;
|
|
||||||
++report.nb_downloading;
|
|
||||||
break;
|
|
||||||
case TorrentModelItem::STATE_DOWNLOADING_META:
|
|
||||||
++report.nb_downloading;
|
|
||||||
break;
|
|
||||||
case TorrentModelItem::STATE_PAUSED_DL:
|
|
||||||
case TorrentModelItem::STATE_PAUSED_MISSING:
|
|
||||||
++report.nb_paused;
|
|
||||||
case TorrentModelItem::STATE_STALLED_DL:
|
|
||||||
case TorrentModelItem::STATE_CHECKING_DL:
|
|
||||||
case TorrentModelItem::STATE_QUEUED_DL: {
|
|
||||||
++report.nb_inactive;
|
|
||||||
++report.nb_downloading;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TorrentModelItem::STATE_SEEDING:
|
|
||||||
case TorrentModelItem::STATE_FORCED_UP:
|
|
||||||
++report.nb_active;
|
|
||||||
++report.nb_seeding;
|
|
||||||
++report.nb_completed;
|
|
||||||
break;
|
|
||||||
case TorrentModelItem::STATE_STALLED_UP:
|
|
||||||
case TorrentModelItem::STATE_CHECKING_UP:
|
|
||||||
case TorrentModelItem::STATE_QUEUED_UP:
|
|
||||||
++report.nb_seeding;
|
|
||||||
case TorrentModelItem::STATE_PAUSED_UP:
|
|
||||||
++report.nb_completed;
|
|
||||||
++report.nb_inactive;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags TorrentModel::flags(const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return 0;
|
|
||||||
// Explicitely mark as editable
|
|
||||||
return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::handleTorrentLabelChange(QString previous, QString current)
|
|
||||||
{
|
|
||||||
emit torrentChangedLabel(static_cast<TorrentModelItem*>(sender()), previous, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentModel::torrentHash(int row) const
|
|
||||||
{
|
|
||||||
if (row >= 0 && row < rowCount())
|
|
||||||
return m_torrents.at(row)->hash();
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::handleTorrentAboutToBeRemoved(const QTorrentHandle &h)
|
|
||||||
{
|
|
||||||
const int row = torrentRow(h.hash());
|
|
||||||
qDebug() << Q_FUNC_INFO << row;
|
|
||||||
if (row >= 0) {
|
|
||||||
emit torrentAboutToBeRemoved(m_torrents.at(row));
|
|
||||||
|
|
||||||
beginRemoveTorrent(row);
|
|
||||||
delete m_torrents[row];
|
|
||||||
m_torrents.removeAt(row);
|
|
||||||
endRemoveTorrent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentModel::stateUpdated(const std::vector<libtorrent::torrent_status> &statuses) {
|
|
||||||
typedef std::vector<libtorrent::torrent_status> statuses_t;
|
|
||||||
|
|
||||||
for (statuses_t::const_iterator i = statuses.begin(), end = statuses.end(); i != end; ++i) {
|
|
||||||
libtorrent::torrent_status const& status = *i;
|
|
||||||
|
|
||||||
const int row = torrentRow(misc::toQString(status.info_hash));
|
|
||||||
if (row >= 0) {
|
|
||||||
m_torrents[row]->refreshStatus(status);
|
|
||||||
notifyTorrentChanged(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit modelRefreshed();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentModel::inhibitSystem()
|
|
||||||
{
|
|
||||||
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();
|
|
||||||
QList<TorrentModelItem*>::const_iterator itend = m_torrents.constEnd();
|
|
||||||
for ( ; it != itend; ++it) {
|
|
||||||
switch((*it)->data(TorrentModelItem::TR_STATUS).toInt()) {
|
|
||||||
case TorrentModelItem::STATE_DOWNLOADING:
|
|
||||||
case TorrentModelItem::STATE_DOWNLOADING_META:
|
|
||||||
case TorrentModelItem::STATE_FORCED_DL:
|
|
||||||
case TorrentModelItem::STATE_STALLED_DL:
|
|
||||||
case TorrentModelItem::STATE_SEEDING:
|
|
||||||
case TorrentModelItem::STATE_FORCED_UP:
|
|
||||||
case TorrentModelItem::STATE_STALLED_UP:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2011 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
#include "qbtsession.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "torrentspeedmonitor.h"
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template<class T> struct Sample {
|
|
||||||
Sample()
|
|
||||||
: download()
|
|
||||||
, upload()
|
|
||||||
{}
|
|
||||||
|
|
||||||
Sample(T download, T upload)
|
|
||||||
: download(download)
|
|
||||||
, upload(upload)
|
|
||||||
{}
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
explicit Sample(Sample<U> other)
|
|
||||||
: download(static_cast<U>(other.download))
|
|
||||||
, upload(static_cast<U>(other.upload))
|
|
||||||
{}
|
|
||||||
|
|
||||||
T download;
|
|
||||||
T upload;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T>& operator+=(Sample<T>& lhs, Sample<T> const& rhs) {
|
|
||||||
lhs.download += rhs.download;
|
|
||||||
lhs.upload += rhs.upload;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T>& operator-=(Sample<T>& lhs, Sample<T> const& rhs) {
|
|
||||||
lhs.download -= rhs.download;
|
|
||||||
lhs.upload -= rhs.upload;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T> operator+(Sample<T> const& lhs, Sample<T> const& rhs) {
|
|
||||||
return Sample<T>(lhs.download + rhs.download, lhs.upload + rhs.upload);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T> operator-(Sample<T> const& lhs, Sample<T> const& rhs) {
|
|
||||||
return Sample<T>(lhs.download - rhs.download, lhs.upload - rhs.upload);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T> operator*(Sample<T> const& lhs, T rhs) {
|
|
||||||
return Sample<T>(lhs.download * rhs, lhs.upload * rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T> operator*(T lhs,Sample<T> const& rhs) {
|
|
||||||
return Sample<T>(lhs * rhs.download, lhs * rhs.upload);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Sample<T> operator/(Sample<T> const& lhs, T rhs) {
|
|
||||||
return Sample<T>(lhs.download / rhs, lhs.upload / rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SpeedSample {
|
|
||||||
|
|
||||||
public:
|
|
||||||
SpeedSample() {}
|
|
||||||
void addSample(Sample<int> const& item);
|
|
||||||
Sample<qreal> average() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const int max_samples = 30;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<Sample<int> > m_speedSamples;
|
|
||||||
Sample<long long> m_sum;
|
|
||||||
};
|
|
||||||
|
|
||||||
TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session)
|
|
||||||
: m_session(session)
|
|
||||||
{
|
|
||||||
connect(m_session, SIGNAL(torrentAboutToBeRemoved(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle)));
|
|
||||||
connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle)));
|
|
||||||
connect(m_session, SIGNAL(statsReceived(libtorrent::stats_alert)), SLOT(statsReceived(libtorrent::stats_alert)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentSpeedMonitor::~TorrentSpeedMonitor()
|
|
||||||
{}
|
|
||||||
|
|
||||||
void SpeedSample::addSample(Sample<int> const& item)
|
|
||||||
{
|
|
||||||
m_speedSamples.push_back(item);
|
|
||||||
m_sum += Sample<long long>(item);
|
|
||||||
if (m_speedSamples.size() > max_samples) {
|
|
||||||
m_sum -= Sample<long long>(m_speedSamples.front());
|
|
||||||
m_speedSamples.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Sample<qreal> SpeedSample::average() const
|
|
||||||
{
|
|
||||||
if (m_speedSamples.empty())
|
|
||||||
return Sample<qreal>();
|
|
||||||
|
|
||||||
return Sample<qreal>(m_sum) * (qreal(1.) / m_speedSamples.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) {
|
|
||||||
try {
|
|
||||||
m_samples.remove(h.hash());
|
|
||||||
} catch(invalid_handle&) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong TorrentSpeedMonitor::getETA(const QString &hash, const libtorrent::torrent_status &status) const
|
|
||||||
{
|
|
||||||
if (QTorrentHandle::is_paused(status))
|
|
||||||
return MAX_ETA;
|
|
||||||
|
|
||||||
QHash<QString, SpeedSample>::const_iterator i = m_samples.find(hash);
|
|
||||||
if (i == m_samples.end())
|
|
||||||
return MAX_ETA;
|
|
||||||
|
|
||||||
const Sample<qreal> speed_average = i->average();
|
|
||||||
|
|
||||||
if (QTorrentHandle::is_seed(status)) {
|
|
||||||
if (!speed_average.upload)
|
|
||||||
return MAX_ETA;
|
|
||||||
|
|
||||||
bool _unused;
|
|
||||||
qreal max_ratio = m_session->getMaxRatioPerTorrent(hash, &_unused);
|
|
||||||
if (max_ratio < 0)
|
|
||||||
return MAX_ETA;
|
|
||||||
|
|
||||||
libtorrent::size_type realDL = status.all_time_download;
|
|
||||||
if (realDL <= 0)
|
|
||||||
realDL = status.total_wanted;
|
|
||||||
|
|
||||||
return (realDL * max_ratio - status.all_time_upload) / speed_average.upload;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!speed_average.download)
|
|
||||||
return MAX_ETA;
|
|
||||||
|
|
||||||
return (status.total_wanted - status.total_wanted_done) / speed_average.download;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentSpeedMonitor::statsReceived(const stats_alert &stats)
|
|
||||||
{
|
|
||||||
Q_ASSERT(stats.interval >= 1000);
|
|
||||||
|
|
||||||
Sample<int> transferred(stats.transferred[stats_alert::download_payload],
|
|
||||||
stats.transferred[stats_alert::upload_payload]);
|
|
||||||
|
|
||||||
Sample<int> normalized = Sample<int>(Sample<long long>(transferred) * 1000LL / static_cast<long long>(stats.interval));
|
|
||||||
|
|
||||||
m_samples[misc::toQString(stats.handle.info_hash())].addSample(normalized);
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef QTRACKER_H
|
|
||||||
#define QTRACKER_H
|
|
||||||
|
|
||||||
#include <libtorrent/entry.hpp>
|
|
||||||
#include <QHash>
|
|
||||||
#include "http/types.h"
|
|
||||||
#include "http/responsebuilder.h"
|
|
||||||
#include "http/irequesthandler.h"
|
|
||||||
|
|
||||||
struct QPeer
|
|
||||||
{
|
|
||||||
QString ip;
|
|
||||||
QString peer_id;
|
|
||||||
int port;
|
|
||||||
|
|
||||||
bool operator!=(const QPeer &other) const;
|
|
||||||
bool operator==(const QPeer &other) const;
|
|
||||||
QString qhash() const;
|
|
||||||
libtorrent::entry toEntry(bool no_peer_id) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TrackerAnnounceRequest
|
|
||||||
{
|
|
||||||
QString info_hash;
|
|
||||||
QString event;
|
|
||||||
int numwant;
|
|
||||||
QPeer peer;
|
|
||||||
// Extensions
|
|
||||||
bool no_peer_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
// static limits
|
|
||||||
const int MAX_TORRENTS = 100;
|
|
||||||
const int MAX_PEERS_PER_TORRENT = 1000;
|
|
||||||
const int ANNOUNCE_INTERVAL = 1800; // 30min
|
|
||||||
|
|
||||||
typedef QHash<QString, QPeer> PeerList;
|
|
||||||
typedef QHash<QString, PeerList> TorrentList;
|
|
||||||
|
|
||||||
namespace Http { class Server; }
|
|
||||||
|
|
||||||
/* Basic Bittorrent tracker implementation in Qt */
|
|
||||||
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
|
||||||
class QTracker : public Http::ResponseBuilder, public Http::IRequestHandler
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(QTracker)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit QTracker(QObject *parent = 0);
|
|
||||||
~QTracker();
|
|
||||||
|
|
||||||
bool start();
|
|
||||||
Http::Response processRequest(const Http::Request &request, const Http::Environment &env);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void respondToAnnounceRequest();
|
|
||||||
void replyWithPeerList(const TrackerAnnounceRequest &annonce_req);
|
|
||||||
|
|
||||||
Http::Server *m_server;
|
|
||||||
TorrentList m_torrents;
|
|
||||||
|
|
||||||
Http::Request m_request;
|
|
||||||
Http::Environment m_env;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // QTRACKER_H
|
|
||||||
295
src/core/scanfoldersmodel.cpp
Normal file
295
src/core/scanfoldersmodel.cpp
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include "utils/misc.h"
|
||||||
|
#include "utils/fs.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "filesystemwatcher.h"
|
||||||
|
#include "bittorrent/session.h"
|
||||||
|
#include "scanfoldersmodel.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const int PathColumn = 0;
|
||||||
|
const int DownloadAtTorrentColumn = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScanFoldersModel::PathData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PathData(const QString &path)
|
||||||
|
: path(path)
|
||||||
|
, downloadAtPath(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PathData(const QString &path, bool download_at_path)
|
||||||
|
: path(path)
|
||||||
|
, downloadAtPath(download_at_path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path;
|
||||||
|
bool downloadAtPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScanFoldersModel *ScanFoldersModel::m_instance = 0;
|
||||||
|
|
||||||
|
bool ScanFoldersModel::initInstance(QObject *parent)
|
||||||
|
{
|
||||||
|
if (!m_instance) {
|
||||||
|
m_instance = new ScanFoldersModel(parent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::freeInstance()
|
||||||
|
{
|
||||||
|
if (m_instance) {
|
||||||
|
delete m_instance;
|
||||||
|
m_instance = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel *ScanFoldersModel::instance()
|
||||||
|
{
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::ScanFoldersModel(QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_fsWatcher(0)
|
||||||
|
{
|
||||||
|
configure();
|
||||||
|
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::~ScanFoldersModel()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_pathList);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return parent.isValid() ? 0 : m_pathList.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const PathData *pathData = m_pathList.at(index.row());
|
||||||
|
if ((index.column() == PathColumn) && (role == Qt::DisplayRole))
|
||||||
|
return Utils::Fs::toNativePath(pathData->path);
|
||||||
|
|
||||||
|
if ((index.column() == DownloadAtTorrentColumn) && (role == Qt::CheckStateRole))
|
||||||
|
return (pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked);
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount()))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (section == PathColumn)
|
||||||
|
return tr("Watched Folder");
|
||||||
|
|
||||||
|
return tr("Download here");
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn))
|
||||||
|
return QAbstractTableModel::flags(index);
|
||||||
|
|
||||||
|
return (QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadAtTorrentColumn) || (role != Qt::CheckStateRole))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Q_ASSERT(index.column() == DownloadAtTorrentColumn);
|
||||||
|
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath)
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
if (!dir.exists()) return DoesNotExist;
|
||||||
|
if (!dir.isReadable()) return CannotRead;
|
||||||
|
|
||||||
|
const QString &canonicalPath = dir.canonicalPath();
|
||||||
|
if (findPathData(canonicalPath) != -1) return AlreadyInList;
|
||||||
|
|
||||||
|
if (!m_fsWatcher) {
|
||||||
|
m_fsWatcher = new FileSystemWatcher(this);
|
||||||
|
connect(m_fsWatcher, SIGNAL(torrentsAdded(const QStringList &)), this, SLOT(addTorrentsToSession(const QStringList &)));
|
||||||
|
}
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||||
|
m_pathList << new PathData(canonicalPath, downloadAtPath);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
// Start scanning
|
||||||
|
m_fsWatcher->addPath(canonicalPath);
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::removePath(int row)
|
||||||
|
{
|
||||||
|
Q_ASSERT((row >= 0) && (row < rowCount()));
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_fsWatcher->removePath(m_pathList.at(row)->path);
|
||||||
|
m_pathList.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::removePath(const QString &path)
|
||||||
|
{
|
||||||
|
const int row = findPathData(path);
|
||||||
|
if (row == -1) return false;
|
||||||
|
|
||||||
|
removePath(row);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath)
|
||||||
|
{
|
||||||
|
Q_ASSERT((row >= 0) && (row < rowCount()));
|
||||||
|
|
||||||
|
bool &oldValue = m_pathList[row]->downloadAtPath;
|
||||||
|
if (oldValue != downloadAtPath) {
|
||||||
|
if (downloadAtPath) {
|
||||||
|
QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile");
|
||||||
|
if (!testFile.open()) return CannotWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldValue = downloadAtPath;
|
||||||
|
const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn);
|
||||||
|
emit dataChanged(changedIndex, changedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const
|
||||||
|
{
|
||||||
|
const int row = findPathData(QFileInfo(filePath).dir().path());
|
||||||
|
Q_ASSERT(row != -1);
|
||||||
|
return m_pathList.at(row)->downloadAtPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScanFoldersModel::findPathData(const QString &path) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_pathList.count(); ++i)
|
||||||
|
if (m_pathList.at(i)->path == Utils::Fs::fromNativePath(path))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::makePersistent()
|
||||||
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
QStringList paths;
|
||||||
|
QList<bool> downloadInFolderInfo;
|
||||||
|
foreach (const PathData *pathData, m_pathList) {
|
||||||
|
paths << pathData->path;
|
||||||
|
downloadInFolderInfo << pathData->downloadAtPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
pref->setScanDirs(paths);
|
||||||
|
pref->setDownloadInScanDirs(downloadInFolderInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::configure()
|
||||||
|
{
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
QList<bool> downloadInDirList = pref->getDownloadInScanDirs();
|
||||||
|
foreach (const QString &dir, pref->getScanDirs()) {
|
||||||
|
bool downloadInDir = downloadInDirList.value(i, false);
|
||||||
|
addPath(dir, downloadInDir);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
|
||||||
|
{
|
||||||
|
foreach (const QString &file, pathList) {
|
||||||
|
qDebug("File %s added", qPrintable(file));
|
||||||
|
if (file.endsWith(".magnet")) {
|
||||||
|
QFile f(file);
|
||||||
|
if (f.open(QIODevice::ReadOnly)) {
|
||||||
|
BitTorrent::Session::instance()->addTorrent(QString::fromLocal8Bit(f.readAll()));
|
||||||
|
f.remove();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug("Failed to open magnet file: %s", qPrintable(f.errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BitTorrent::AddTorrentParams params;
|
||||||
|
if (downloadInTorrentFolder(file))
|
||||||
|
params.savePath = QFileInfo(file).dir().path();
|
||||||
|
|
||||||
|
BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(file);
|
||||||
|
if (torrentInfo.isValid()) {
|
||||||
|
BitTorrent::Session::instance()->addTorrent(torrentInfo, params);
|
||||||
|
Utils::Fs::forceRemove(file);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug("Ignoring incomplete torrent file: %s", qPrintable(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/core/scanfoldersmodel.h
Normal file
95
src/core/scanfoldersmodel.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCANFOLDERSMODEL_H
|
||||||
|
#define SCANFOLDERSMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QStringList;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class FileSystemWatcher;
|
||||||
|
|
||||||
|
class ScanFoldersModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(ScanFoldersModel)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum PathStatus
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
DoesNotExist,
|
||||||
|
CannotRead,
|
||||||
|
CannotWrite,
|
||||||
|
AlreadyInList
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool initInstance(QObject *parent = 0);
|
||||||
|
static void freeInstance();
|
||||||
|
static ScanFoldersModel *instance();
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
// TODO: removePaths(); singular version becomes private helper functions;
|
||||||
|
// also: remove functions should take modelindexes
|
||||||
|
PathStatus addPath(const QString &path, bool downloadAtPath);
|
||||||
|
void removePath(int row);
|
||||||
|
bool removePath(const QString &path);
|
||||||
|
PathStatus setDownloadAtPath(int row, bool downloadAtPath);
|
||||||
|
|
||||||
|
bool downloadInTorrentFolder(const QString &filePath) const;
|
||||||
|
void makePersistent();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void configure();
|
||||||
|
void addTorrentsToSession(const QStringList &pathList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit ScanFoldersModel(QObject *parent = 0);
|
||||||
|
~ScanFoldersModel();
|
||||||
|
|
||||||
|
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||||
|
static ScanFoldersModel *m_instance;
|
||||||
|
class PathData;
|
||||||
|
int findPathData(const QString &path) const;
|
||||||
|
|
||||||
|
QList<PathData*> m_pathList;
|
||||||
|
FileSystemWatcher *m_fsWatcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCANFOLDERSMODEL_H
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "scannedfoldersmodel.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "filesystemwatcher.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QString>
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
const int PathColumn = 0;
|
|
||||||
const int DownloadAtTorrentColumn = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScanFoldersModel::PathData {
|
|
||||||
public:
|
|
||||||
PathData(const QString &path) : path(path), downloadAtPath(false) {}
|
|
||||||
PathData(const QString &path, bool download_at_path) : path(path), downloadAtPath(download_at_path) {}
|
|
||||||
const QString path;
|
|
||||||
bool downloadAtPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) {
|
|
||||||
//Q_ASSERT(!parent != !m_instance);
|
|
||||||
if (!m_instance)
|
|
||||||
m_instance = new ScanFoldersModel(parent);
|
|
||||||
return m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::ScanFoldersModel(QObject *parent) :
|
|
||||||
QAbstractTableModel(parent), m_fsWatcher(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
ScanFoldersModel::~ScanFoldersModel() {
|
|
||||||
qDeleteAll(m_pathList);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::rowCount(const QModelIndex &parent) const {
|
|
||||||
return parent.isValid() ? 0 : m_pathList.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::columnCount(const QModelIndex &parent) const {
|
|
||||||
Q_UNUSED(parent);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
const PathData* pathData = m_pathList.at(index.row());
|
|
||||||
if (index.column() == PathColumn && role == Qt::DisplayRole) {
|
|
||||||
|
|
||||||
return fsutils::toNativePath(pathData->path);
|
|
||||||
}
|
|
||||||
if (index.column() == DownloadAtTorrentColumn && role == Qt::CheckStateRole)
|
|
||||||
return pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked;
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
|
||||||
if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
if (section == PathColumn)
|
|
||||||
return tr("Watched Folder");
|
|
||||||
return tr("Download here");
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount() || index.column() != DownloadAtTorrentColumn)
|
|
||||||
return QAbstractTableModel::flags(index);
|
|
||||||
return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) {
|
|
||||||
if (!index.isValid() || index.row() >= rowCount() || index.column() > DownloadAtTorrentColumn || role != Qt::CheckStateRole)
|
|
||||||
return false;
|
|
||||||
Q_ASSERT(index.column() == DownloadAtTorrentColumn);
|
|
||||||
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
|
|
||||||
emit dataChanged(index, index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool download_at_path) {
|
|
||||||
QDir dir(path);
|
|
||||||
if (!dir.exists())
|
|
||||||
return DoesNotExist;
|
|
||||||
if (!dir.isReadable())
|
|
||||||
return CannotRead;
|
|
||||||
const QString &canonicalPath = dir.canonicalPath();
|
|
||||||
if (findPathData(canonicalPath) != -1)
|
|
||||||
return AlreadyInList;
|
|
||||||
if (!m_fsWatcher) {
|
|
||||||
m_fsWatcher = new FileSystemWatcher(this);
|
|
||||||
connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&)));
|
|
||||||
}
|
|
||||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
|
||||||
m_pathList << new PathData(canonicalPath, download_at_path);
|
|
||||||
endInsertRows();
|
|
||||||
// Start scanning
|
|
||||||
m_fsWatcher->addPath(canonicalPath);
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScanFoldersModel::removePath(int row) {
|
|
||||||
Q_ASSERT(row >= 0 && row < rowCount());
|
|
||||||
beginRemoveRows(QModelIndex(), row, row);
|
|
||||||
m_fsWatcher->removePath(m_pathList.at(row)->path);
|
|
||||||
m_pathList.removeAt(row);
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::removePath(const QString &path) {
|
|
||||||
const int row = findPathData(path);
|
|
||||||
if (row == -1)
|
|
||||||
return false;
|
|
||||||
removePath(row);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath) {
|
|
||||||
Q_ASSERT(row >= 0 && row < rowCount());
|
|
||||||
|
|
||||||
bool &oldValue = m_pathList[row]->downloadAtPath;
|
|
||||||
if (oldValue != downloadAtPath) {
|
|
||||||
if (downloadAtPath) {
|
|
||||||
QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile");
|
|
||||||
if (!testFile.open())
|
|
||||||
return CannotWrite;
|
|
||||||
}
|
|
||||||
oldValue = downloadAtPath;
|
|
||||||
const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn);
|
|
||||||
emit dataChanged(changedIndex, changedIndex);
|
|
||||||
}
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const {
|
|
||||||
const int row = findPathData(QFileInfo(filePath).dir().path());
|
|
||||||
Q_ASSERT(row != -1);
|
|
||||||
return m_pathList.at(row)->downloadAtPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScanFoldersModel::findPathData(const QString &path) const {
|
|
||||||
for (int i = 0; i < m_pathList.count(); ++i) {
|
|
||||||
const PathData* pathData = m_pathList.at(i);
|
|
||||||
if (pathData->path == fsutils::fromNativePath(path))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScanFoldersModel::makePersistent() {
|
|
||||||
Preferences* const pref = Preferences::instance();
|
|
||||||
QStringList paths;
|
|
||||||
QList<bool> downloadInFolderInfo;
|
|
||||||
foreach (const PathData* pathData, m_pathList) {
|
|
||||||
paths << pathData->path;
|
|
||||||
downloadInFolderInfo << pathData->downloadAtPath;
|
|
||||||
}
|
|
||||||
pref->setScanDirs(paths);
|
|
||||||
pref->setDownloadInScanDirs(downloadInFolderInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanFoldersModel *ScanFoldersModel::m_instance = 0;
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2010 Christian Kandeler, Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SCANNEDFOLDERSMODEL_H
|
|
||||||
#define SCANNEDFOLDERSMODEL_H
|
|
||||||
|
|
||||||
#include <QAbstractTableModel>
|
|
||||||
#include <QList>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
class FileSystemWatcher;
|
|
||||||
|
|
||||||
class ScanFoldersModel : public QAbstractTableModel {
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(ScanFoldersModel)
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum PathStatus { Ok, DoesNotExist, CannotRead, CannotWrite, AlreadyInList };
|
|
||||||
static ScanFoldersModel *instance(QObject *parent = 0);
|
|
||||||
virtual ~ScanFoldersModel();
|
|
||||||
|
|
||||||
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
|
||||||
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
|
||||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
|
||||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
|
|
||||||
|
|
||||||
// TODO: removePaths(); singular version becomes private helper functions;
|
|
||||||
// also: remove functions should take modelindexes
|
|
||||||
PathStatus addPath(const QString &path, bool download_at_path);
|
|
||||||
void removePath(int row);
|
|
||||||
bool removePath(const QString &path);
|
|
||||||
PathStatus setDownloadAtPath(int row, bool downloadAtPath);
|
|
||||||
|
|
||||||
bool downloadInTorrentFolder(const QString &filePath) const;
|
|
||||||
void makePersistent();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
// The absolute paths of new torrent files in the scanned directories.
|
|
||||||
void torrentsAdded(QStringList &pathList);
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit ScanFoldersModel(QObject *parent);
|
|
||||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
|
||||||
static ScanFoldersModel *m_instance;
|
|
||||||
class PathData;
|
|
||||||
int findPathData(const QString &path) const;
|
|
||||||
|
|
||||||
QList<PathData*> m_pathList;
|
|
||||||
FileSystemWatcher *m_fsWatcher;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SCANNEDFOLDERSMODEL_H
|
|
||||||
@@ -1,480 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2011 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This code is based on QxtSmtp from libqxt (http://libqxt.org)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "smtp.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
#include <QTextStream>
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
#include <QSslSocket>
|
|
||||||
#else
|
|
||||||
#include <QTcpSocket>
|
|
||||||
#endif
|
|
||||||
#include <QTextCodec>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QHostInfo>
|
|
||||||
#include <QNetworkInterface>
|
|
||||||
#include <QCryptographicHash>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
const short DEFAULT_PORT = 25;
|
|
||||||
const short DEFAULT_PORT_SSL = 465;
|
|
||||||
|
|
||||||
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
|
|
||||||
{
|
|
||||||
const int blockSize = 64; // HMAC-MD5 block size
|
|
||||||
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
|
||||||
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
|
||||||
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
|
||||||
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
|
||||||
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
|
||||||
|
|
||||||
for (int i = 0; i < key.length(); i++) {
|
|
||||||
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
|
||||||
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
|
||||||
}
|
|
||||||
|
|
||||||
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
|
|
||||||
QByteArray total = outerPadding;
|
|
||||||
QByteArray part = innerPadding;
|
|
||||||
part.append(msg);
|
|
||||||
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
|
|
||||||
return QCryptographicHash::hash(total, QCryptographicHash::Md5);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray determineFQDN()
|
|
||||||
{
|
|
||||||
QString hostname = QHostInfo::localHostName();
|
|
||||||
if (hostname.isEmpty())
|
|
||||||
hostname = "localhost";
|
|
||||||
|
|
||||||
return hostname.toLocal8Bit();
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Smtp::Smtp(QObject *parent): QObject(parent),
|
|
||||||
state(Init), use_ssl(false) {
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
socket = new QSslSocket(this);
|
|
||||||
#else
|
|
||||||
socket = new QTcpSocket(this);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
|
|
||||||
connect(socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
|
||||||
|
|
||||||
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
|
||||||
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
|
||||||
== "750c783e6ab0b503eaa86e310a5db738");
|
|
||||||
Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"),
|
|
||||||
"Hi There").toHex()
|
|
||||||
== "9294727a3638bb1c13f48ef8158bfc9d");
|
|
||||||
}
|
|
||||||
|
|
||||||
Smtp::~Smtp() {
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body) {
|
|
||||||
const Preferences* const pref = Preferences::instance();
|
|
||||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
|
||||||
message = "";
|
|
||||||
message += encode_mime_header("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
|
||||||
message += encode_mime_header("From", from, latin1);
|
|
||||||
message += encode_mime_header("Subject", subject, latin1);
|
|
||||||
message += encode_mime_header("To", to, latin1);
|
|
||||||
message += "MIME-Version: 1.0\r\n";
|
|
||||||
message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
|
||||||
message += "Content-Transfer-Encoding: base64\r\n";
|
|
||||||
message += "\r\n";
|
|
||||||
// Encode the body in base64
|
|
||||||
QString crlf_body = body;
|
|
||||||
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
|
||||||
int ct = b.length();
|
|
||||||
for (int i = 0; i < ct; i += 78)
|
|
||||||
{
|
|
||||||
message += b.mid(i, 78);
|
|
||||||
}
|
|
||||||
this->from = from;
|
|
||||||
rcpt = to;
|
|
||||||
// Authentication
|
|
||||||
if (pref->getMailNotificationSMTPAuth()) {
|
|
||||||
username = pref->getMailNotificationSMTPUsername();
|
|
||||||
password = pref->getMailNotificationSMTPPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to SMTP server
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
if (pref->getMailNotificationSMTPSSL()) {
|
|
||||||
socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL);
|
|
||||||
use_ssl = true;
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
|
||||||
use_ssl = false;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::readyRead()
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
// SMTP is line-oriented
|
|
||||||
buffer += socket->readAll();
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int pos = buffer.indexOf("\r\n");
|
|
||||||
if (pos < 0) return; // Loop exit condition
|
|
||||||
QByteArray line = buffer.left(pos);
|
|
||||||
buffer = buffer.mid(pos + 2);
|
|
||||||
qDebug() << "Response line:" << line;
|
|
||||||
// Extract reponse code
|
|
||||||
QByteArray code = line.left(3);
|
|
||||||
|
|
||||||
switch(state) {
|
|
||||||
case Init: {
|
|
||||||
if (code[0] == '2') {
|
|
||||||
// The server may send a multiline greeting/INIT/220 response.
|
|
||||||
// We wait until it finishes.
|
|
||||||
if (line[3] != ' ')
|
|
||||||
break;
|
|
||||||
// Connection was successful
|
|
||||||
ehlo();
|
|
||||||
} else {
|
|
||||||
logError("Connection failed, unrecognized reply: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EhloSent:
|
|
||||||
case HeloSent:
|
|
||||||
case EhloGreetReceived:
|
|
||||||
parseEhloResponse(code, line[3] != ' ', line.mid(4));
|
|
||||||
break;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
case StartTLSSent:
|
|
||||||
if (code == "220") {
|
|
||||||
socket->startClientEncryption();
|
|
||||||
ehlo();
|
|
||||||
} else {
|
|
||||||
authenticate();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AuthRequestSent:
|
|
||||||
case AuthUsernameSent:
|
|
||||||
if (authType == AuthPlain) authPlain();
|
|
||||||
else if (authType == AuthLogin) authLogin();
|
|
||||||
else authCramMD5(line.mid(4));
|
|
||||||
break;
|
|
||||||
case AuthSent:
|
|
||||||
case Authenticated:
|
|
||||||
if (code[0] == '2') {
|
|
||||||
qDebug() << "Sending <mail from>...";
|
|
||||||
socket->write("mail from:<" + from.toLatin1() + ">\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = Rcpt;
|
|
||||||
} else {
|
|
||||||
// Authentication failed!
|
|
||||||
logError("Authentication failed, msg: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Rcpt:
|
|
||||||
if (code[0] == '2') {
|
|
||||||
socket->write("rcpt to:<" + rcpt.toLatin1() + ">\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = Data;
|
|
||||||
} else {
|
|
||||||
logError("<mail from> was rejected by server, msg: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Data:
|
|
||||||
if (code[0] == '2') {
|
|
||||||
socket->write("data\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = Body;
|
|
||||||
} else {
|
|
||||||
logError("<Rcpt to> was rejected by server, msg: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Body:
|
|
||||||
if (code[0] == '3') {
|
|
||||||
socket->write(message + "\r\n.\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = Quit;
|
|
||||||
} else {
|
|
||||||
logError("<data> was rejected by server, msg: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Quit:
|
|
||||||
if (code[0] == '2') {
|
|
||||||
socket->write("QUIT\r\n");
|
|
||||||
socket->flush();
|
|
||||||
// here, we just close.
|
|
||||||
state = Close;
|
|
||||||
} else {
|
|
||||||
logError("Message was rejected by the server, error: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qDebug() << "Disconnecting from host";
|
|
||||||
socket->disconnectFromHost();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix)
|
|
||||||
{
|
|
||||||
QByteArray rv = "";
|
|
||||||
QByteArray line = key.toLatin1() + ": ";
|
|
||||||
if (!prefix.isEmpty()) line += prefix;
|
|
||||||
if (!value.contains("=?") && latin1->canEncode(value)) {
|
|
||||||
bool firstWord = true;
|
|
||||||
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
|
|
||||||
if (line.size() > 78) {
|
|
||||||
rv = rv + line + "\r\n";
|
|
||||||
line.clear();
|
|
||||||
}
|
|
||||||
if (firstWord)
|
|
||||||
line += word;
|
|
||||||
else
|
|
||||||
line += " " + word;
|
|
||||||
firstWord = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The text cannot be losslessly encoded as Latin-1. Therefore, we
|
|
||||||
// must use base64 encoding.
|
|
||||||
QByteArray utf8 = value.toUtf8();
|
|
||||||
// Use base64 encoding
|
|
||||||
QByteArray base64 = utf8.toBase64();
|
|
||||||
int ct = base64.length();
|
|
||||||
line += "=?utf-8?b?";
|
|
||||||
for (int i = 0; i < ct; i += 4) {
|
|
||||||
/*if (line.length() > 72) {
|
|
||||||
rv += line + "?\n\r";
|
|
||||||
line = " =?utf-8?b?";
|
|
||||||
}*/
|
|
||||||
line = line + base64.mid(i, 4);
|
|
||||||
}
|
|
||||||
line += "?="; // end encoded-word atom
|
|
||||||
}
|
|
||||||
return rv + line + "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::ehlo()
|
|
||||||
{
|
|
||||||
QByteArray address = determineFQDN();
|
|
||||||
socket->write("ehlo " + address + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = EhloSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::helo()
|
|
||||||
{
|
|
||||||
QByteArray address = determineFQDN();
|
|
||||||
socket->write("helo " + address + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = HeloSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::parseEhloResponse(const QByteArray& code, bool continued, const QString& line)
|
|
||||||
{
|
|
||||||
if (code != "250") {
|
|
||||||
// Error
|
|
||||||
if (state == EhloSent) {
|
|
||||||
// try to send HELO instead of EHLO
|
|
||||||
qDebug() << "EHLO failed, trying HELO instead...";
|
|
||||||
helo();
|
|
||||||
} else {
|
|
||||||
// Both EHLO and HELO failed, chances are this is NOT
|
|
||||||
// a SMTP server
|
|
||||||
logError("Both EHLO and HELO failed, msg: "+line);
|
|
||||||
state = Close;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state != EhloGreetReceived) {
|
|
||||||
if (!continued) {
|
|
||||||
// greeting only, no extensions
|
|
||||||
qDebug() << "No extension";
|
|
||||||
state = EhloDone;
|
|
||||||
} else {
|
|
||||||
// greeting followed by extensions
|
|
||||||
state = EhloGreetReceived;
|
|
||||||
qDebug () << "EHLO greet received";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
|
|
||||||
<< line.section(' ', 1);
|
|
||||||
extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
|
|
||||||
if (!continued)
|
|
||||||
state = EhloDone;
|
|
||||||
}
|
|
||||||
if (state != EhloDone) return;
|
|
||||||
if (extensions.contains("STARTTLS") && use_ssl) {
|
|
||||||
qDebug() << "STARTTLS";
|
|
||||||
startTLS();
|
|
||||||
} else {
|
|
||||||
authenticate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::authenticate()
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
if (!extensions.contains("AUTH") ||
|
|
||||||
username.isEmpty() || password.isEmpty()) {
|
|
||||||
// Skip authentication
|
|
||||||
qDebug() << "Skipping authentication...";
|
|
||||||
state = Authenticated;
|
|
||||||
// At this point the server will not send any response
|
|
||||||
// So fill the buffer with a fake one to pass the tests
|
|
||||||
// in readyRead()
|
|
||||||
buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// AUTH extension is supported, check which
|
|
||||||
// authentication modes are supported by
|
|
||||||
// the server
|
|
||||||
QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
|
|
||||||
if (auth.contains("CRAM-MD5")) {
|
|
||||||
qDebug() << "Using CRAM-MD5 authentication...";
|
|
||||||
authCramMD5();
|
|
||||||
}
|
|
||||||
else if (auth.contains("PLAIN")) {
|
|
||||||
qDebug() << "Using PLAIN authentication...";
|
|
||||||
authPlain();
|
|
||||||
}
|
|
||||||
else if (auth.contains("LOGIN")) {
|
|
||||||
qDebug() << "Using LOGIN authentication...";
|
|
||||||
authLogin();
|
|
||||||
} else {
|
|
||||||
// Skip authentication
|
|
||||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
|
||||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
|
||||||
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
|
||||||
state = Authenticated;
|
|
||||||
// At this point the server will not send any response
|
|
||||||
// So fill the buffer with a fake one to pass the tests
|
|
||||||
// in readyRead()
|
|
||||||
buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::startTLS()
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
#ifndef QT_NO_OPENSSL
|
|
||||||
socket->write("starttls\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = StartTLSSent;
|
|
||||||
#else
|
|
||||||
authenticate();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::authCramMD5(const QByteArray& challenge)
|
|
||||||
{
|
|
||||||
if (state != AuthRequestSent) {
|
|
||||||
socket->write("auth cram-md5\r\n");
|
|
||||||
socket->flush();
|
|
||||||
authType = AuthCramMD5;
|
|
||||||
state = AuthRequestSent;
|
|
||||||
} else {
|
|
||||||
QByteArray response = username.toLatin1() + ' '
|
|
||||||
+ hmacMD5(password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
|
|
||||||
socket->write(response.toBase64() + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = AuthSent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::authPlain()
|
|
||||||
{
|
|
||||||
if (state != AuthRequestSent) {
|
|
||||||
authType = AuthPlain;
|
|
||||||
// Prepare Auth string
|
|
||||||
QByteArray auth;
|
|
||||||
auth += '\0';
|
|
||||||
auth += username.toLatin1();
|
|
||||||
qDebug() << "username: " << username.toLatin1();
|
|
||||||
auth += '\0';
|
|
||||||
auth += password.toLatin1();
|
|
||||||
qDebug() << "password: " << password.toLatin1();
|
|
||||||
// Send it
|
|
||||||
socket->write("auth plain "+ auth.toBase64() + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = AuthSent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::authLogin()
|
|
||||||
{
|
|
||||||
if (state != AuthRequestSent && state != AuthUsernameSent) {
|
|
||||||
socket->write("auth login\r\n");
|
|
||||||
socket->flush();
|
|
||||||
authType = AuthLogin;
|
|
||||||
state = AuthRequestSent;
|
|
||||||
}
|
|
||||||
else if (state == AuthRequestSent) {
|
|
||||||
socket->write(username.toLatin1().toBase64() + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = AuthUsernameSent;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
socket->write(password.toLatin1().toBase64() + "\r\n");
|
|
||||||
socket->flush();
|
|
||||||
state = AuthSent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Smtp::logError(const QString &msg)
|
|
||||||
{
|
|
||||||
qDebug() << "Email Notification Error:" << msg;
|
|
||||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
|
||||||
}
|
|
||||||
159
src/core/torrentfilter.cpp
Normal file
159
src/core/torrentfilter.cpp
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "bittorrent/torrenthandle.h"
|
||||||
|
#include "torrentfilter.h"
|
||||||
|
|
||||||
|
const QString TorrentFilter::AnyLabel;
|
||||||
|
const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString());
|
||||||
|
|
||||||
|
const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading);
|
||||||
|
const TorrentFilter TorrentFilter::SeedingTorrent(TorrentFilter::Seeding);
|
||||||
|
const TorrentFilter TorrentFilter::CompletedTorrent(TorrentFilter::Completed);
|
||||||
|
const TorrentFilter TorrentFilter::PausedTorrent(TorrentFilter::Paused);
|
||||||
|
const TorrentFilter TorrentFilter::ResumedTorrent(TorrentFilter::Resumed);
|
||||||
|
const TorrentFilter TorrentFilter::ActiveTorrent(TorrentFilter::Active);
|
||||||
|
const TorrentFilter TorrentFilter::InactiveTorrent(TorrentFilter::Inactive);
|
||||||
|
|
||||||
|
using BitTorrent::TorrentHandle;
|
||||||
|
using BitTorrent::TorrentState;
|
||||||
|
|
||||||
|
TorrentFilter::TorrentFilter()
|
||||||
|
: m_type(All)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label)
|
||||||
|
: m_type(type)
|
||||||
|
, m_label(label)
|
||||||
|
, m_hashSet(hashSet)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label)
|
||||||
|
: m_type(All)
|
||||||
|
, m_label(label)
|
||||||
|
, m_hashSet(hashSet)
|
||||||
|
{
|
||||||
|
setTypeByName(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::setType(Type type)
|
||||||
|
{
|
||||||
|
if (m_type != type) {
|
||||||
|
m_type = type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::setTypeByName(const QString &filter)
|
||||||
|
{
|
||||||
|
Type type = All;
|
||||||
|
|
||||||
|
if (filter == "downloading")
|
||||||
|
type = Downloading;
|
||||||
|
else if (filter == "seeding")
|
||||||
|
type = Seeding;
|
||||||
|
else if (filter == "completed")
|
||||||
|
type = Completed;
|
||||||
|
else if (filter == "paused")
|
||||||
|
type = Paused;
|
||||||
|
else if (filter == "resumed")
|
||||||
|
type = Resumed;
|
||||||
|
else if (filter == "active")
|
||||||
|
type = Active;
|
||||||
|
else if (filter == "inactive")
|
||||||
|
type = Inactive;
|
||||||
|
|
||||||
|
return setType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::setHashSet(const QStringSet &hashSet)
|
||||||
|
{
|
||||||
|
if (m_hashSet != hashSet) {
|
||||||
|
m_hashSet = hashSet;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::setLabel(const QString &label)
|
||||||
|
{
|
||||||
|
if (m_label != label) {
|
||||||
|
m_label = label;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::match(TorrentHandle *const torrent) const
|
||||||
|
{
|
||||||
|
if (!torrent) return false;
|
||||||
|
|
||||||
|
return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const
|
||||||
|
{
|
||||||
|
switch (m_type) {
|
||||||
|
case All:
|
||||||
|
return true;
|
||||||
|
case Downloading:
|
||||||
|
return torrent->isDownloading();
|
||||||
|
case Seeding:
|
||||||
|
return torrent->isUploading();
|
||||||
|
case Completed:
|
||||||
|
return torrent->isCompleted();
|
||||||
|
case Paused:
|
||||||
|
return torrent->isPaused();
|
||||||
|
case Resumed:
|
||||||
|
return torrent->isResumed();
|
||||||
|
case Active:
|
||||||
|
return torrent->isActive();
|
||||||
|
case Inactive:
|
||||||
|
return torrent->isInactive();
|
||||||
|
default: // All
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const
|
||||||
|
{
|
||||||
|
if (m_hashSet == AnyHash) return true;
|
||||||
|
else return m_hashSet.contains(torrent->hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const
|
||||||
|
{
|
||||||
|
if (m_label == AnyLabel) return true;
|
||||||
|
else return (torrent->label() == m_label);
|
||||||
|
}
|
||||||
93
src/core/torrentfilter.h
Normal file
93
src/core/torrentfilter.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TORRENTFILTER_H
|
||||||
|
#define TORRENTFILTER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
typedef QSet<QString> QStringSet;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
|
||||||
|
class TorrentHandle;
|
||||||
|
class TorrentState;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TorrentFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
All,
|
||||||
|
Downloading,
|
||||||
|
Seeding,
|
||||||
|
Completed,
|
||||||
|
Resumed,
|
||||||
|
Paused,
|
||||||
|
Active,
|
||||||
|
Inactive
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QString AnyLabel;
|
||||||
|
static const QStringSet AnyHash;
|
||||||
|
|
||||||
|
static const TorrentFilter DownloadingTorrent;
|
||||||
|
static const TorrentFilter SeedingTorrent;
|
||||||
|
static const TorrentFilter CompletedTorrent;
|
||||||
|
static const TorrentFilter PausedTorrent;
|
||||||
|
static const TorrentFilter ResumedTorrent;
|
||||||
|
static const TorrentFilter ActiveTorrent;
|
||||||
|
static const TorrentFilter InactiveTorrent;
|
||||||
|
|
||||||
|
TorrentFilter();
|
||||||
|
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
||||||
|
TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||||
|
TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel);
|
||||||
|
|
||||||
|
bool setType(Type type);
|
||||||
|
bool setTypeByName(const QString &filter);
|
||||||
|
bool setHashSet(const QStringSet &hashSet);
|
||||||
|
bool setLabel(const QString &label);
|
||||||
|
|
||||||
|
bool match(BitTorrent::TorrentHandle *const torrent) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool matchState(BitTorrent::TorrentHandle *const torrent) const;
|
||||||
|
bool matchHash(BitTorrent::TorrentHandle *const torrent) const;
|
||||||
|
bool matchLabel(BitTorrent::TorrentHandle *const torrent) const;
|
||||||
|
|
||||||
|
Type m_type;
|
||||||
|
QString m_label;
|
||||||
|
QStringSet m_hashSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TORRENTFILTER_H
|
||||||
@@ -1,527 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "torrentpersistentdata.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
#include "qinisettings.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "qtorrenthandle.h"
|
|
||||||
|
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
|
||||||
|
|
||||||
QHash<QString, TorrentTempData::TorrentData> TorrentTempData::data = QHash<QString, TorrentTempData::TorrentData>();
|
|
||||||
QHash<QString, TorrentTempData::TorrentMoveState> TorrentTempData::torrentMoveStates = QHash<QString, TorrentTempData::TorrentMoveState>();
|
|
||||||
QHash<QString, bool> HiddenData::data = QHash<QString, bool>();
|
|
||||||
unsigned int HiddenData::metadata_counter = 0;
|
|
||||||
TorrentPersistentData* TorrentPersistentData::m_instance = 0;
|
|
||||||
|
|
||||||
TorrentTempData::TorrentData::TorrentData()
|
|
||||||
: sequential(false)
|
|
||||||
, seed(false)
|
|
||||||
, add_paused(Preferences::instance()->addTorrentsInPause())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentTempData::TorrentMoveState::TorrentMoveState(QString oldPath, QString newPath)
|
|
||||||
: oldPath(oldPath)
|
|
||||||
, newPath(newPath)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentTempData::hasTempData(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.contains(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::deleteTempData(const QString &hash)
|
|
||||||
{
|
|
||||||
data.remove(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setFilesPriority(const QString &hash, const std::vector<int> &pp)
|
|
||||||
{
|
|
||||||
data[hash].files_priority = pp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setFilesPath(const QString &hash, const QStringList &path_list)
|
|
||||||
{
|
|
||||||
data[hash].path_list = path_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setSavePath(const QString &hash, const QString &save_path)
|
|
||||||
{
|
|
||||||
data[hash].save_path = save_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setLabel(const QString &hash, const QString &label)
|
|
||||||
{
|
|
||||||
data[hash].label = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setSequential(const QString &hash, const bool &sequential)
|
|
||||||
{
|
|
||||||
data[hash].sequential = sequential;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentTempData::isSequential(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).sequential;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setSeedingMode(const QString &hash, const bool &seed)
|
|
||||||
{
|
|
||||||
data[hash].seed = seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentTempData::isSeedingMode(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentTempData::getSavePath(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).save_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TorrentTempData::getFilesPath(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).path_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentTempData::getLabel(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).label;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::getFilesPriority(const QString &hash, std::vector<int> &fp)
|
|
||||||
{
|
|
||||||
fp = data.value(hash).files_priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentTempData::isMoveInProgress(const QString &hash)
|
|
||||||
{
|
|
||||||
return torrentMoveStates.find(hash) != torrentMoveStates.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::enqueueMove(const QString &hash, const QString &queuedPath)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i == torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
i->queuedPath = queuedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::startMove(const QString &hash, const QString &oldPath, const QString& newPath)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i != torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrentMoveStates.insert(hash, TorrentMoveState(oldPath, newPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::finishMove(const QString &hash)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i == torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
torrentMoveStates.erase(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentTempData::getOldPath(const QString &hash)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i == torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return i->oldPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentTempData::getNewPath(const QString &hash)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i == torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return i->newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentTempData::getQueuedPath(const QString &hash)
|
|
||||||
{
|
|
||||||
QHash<QString, TorrentMoveState>::iterator i = torrentMoveStates.find(hash);
|
|
||||||
if (i == torrentMoveStates.end()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
return i->queuedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentTempData::setAddPaused(const QString &hash, const bool &paused)
|
|
||||||
{
|
|
||||||
data[hash].add_paused = paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentTempData::isAddPaused(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.value(hash).add_paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HiddenData::addData(const QString &hash)
|
|
||||||
{
|
|
||||||
data[hash] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HiddenData::hasData(const QString &hash)
|
|
||||||
{
|
|
||||||
return data.contains(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HiddenData::deleteData(const QString &hash)
|
|
||||||
{
|
|
||||||
if (data.value(hash, false))
|
|
||||||
metadata_counter--;
|
|
||||||
data.remove(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int HiddenData::getSize()
|
|
||||||
{
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int HiddenData::getDownloadingSize()
|
|
||||||
{
|
|
||||||
return data.size() - metadata_counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HiddenData::gotMetadata(const QString &hash)
|
|
||||||
{
|
|
||||||
if (!data.contains(hash))
|
|
||||||
return;
|
|
||||||
data[hash] = true;
|
|
||||||
metadata_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentPersistentData::TorrentPersistentData()
|
|
||||||
: m_data(QIniSettings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")).value("torrents").toHash())
|
|
||||||
, dirty(false)
|
|
||||||
{
|
|
||||||
timer.setSingleShot(true);
|
|
||||||
timer.setInterval(5 * 1000);
|
|
||||||
connect(&timer, SIGNAL(timeout()), SLOT(save()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentPersistentData::~TorrentPersistentData()
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::save()
|
|
||||||
{
|
|
||||||
if (!dirty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume"));
|
|
||||||
settings.setValue("torrents", m_data);
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVariant TorrentPersistentData::value(const QString &key, const QVariant &defaultValue) const
|
|
||||||
{
|
|
||||||
QReadLocker locker(&lock);
|
|
||||||
return m_data.value(key, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::setValue(const QString &key, const QVariant &value)
|
|
||||||
{
|
|
||||||
QWriteLocker locker(&lock);
|
|
||||||
if (m_data.value(key) == value)
|
|
||||||
return;
|
|
||||||
dirty = true;
|
|
||||||
timer.start();
|
|
||||||
m_data.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
TorrentPersistentData* TorrentPersistentData::instance()
|
|
||||||
{
|
|
||||||
if (!m_instance)
|
|
||||||
m_instance = new TorrentPersistentData;
|
|
||||||
|
|
||||||
return m_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::drop()
|
|
||||||
{
|
|
||||||
if (m_instance) {
|
|
||||||
delete m_instance;
|
|
||||||
m_instance = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::isKnownTorrent(QString hash) const
|
|
||||||
{
|
|
||||||
QReadLocker locker(&lock);
|
|
||||||
return m_data.contains(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList TorrentPersistentData::knownTorrents() const
|
|
||||||
{
|
|
||||||
QReadLocker locker(&lock);
|
|
||||||
return m_data.keys();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::setRatioLimit(const QString &hash, const qreal &ratio)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["max_ratio"] = ratio;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal TorrentPersistentData::getRatioLimit(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("max_ratio", USE_GLOBAL_RATIO).toReal();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::hasPerTorrentRatioLimit() const
|
|
||||||
{
|
|
||||||
QReadLocker locker(&lock);
|
|
||||||
QHash<QString, QVariant>::ConstIterator it = m_data.constBegin();
|
|
||||||
QHash<QString, QVariant>::ConstIterator itend = m_data.constEnd();
|
|
||||||
for (; it != itend; ++it)
|
|
||||||
if (it.value().toHash().value("max_ratio", USE_GLOBAL_RATIO).toReal() >= 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::setAddedDate(const QString &hash, const QDateTime &time)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
if (!torrent.contains("add_date")) {
|
|
||||||
torrent["add_date"] = time;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime TorrentPersistentData::getAddedDate(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
QDateTime dt = torrent.value("add_date").toDateTime();
|
|
||||||
if (!dt.isValid())
|
|
||||||
dt = QDateTime::currentDateTime();
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::setErrorState(const QString &hash, const bool has_error)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["has_error"] = has_error;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::hasError(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("has_error", false).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
QDateTime TorrentPersistentData::getSeedDate(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("seed_date").toDateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::deletePersistentData(const QString &hash)
|
|
||||||
{
|
|
||||||
QWriteLocker locker(&lock);
|
|
||||||
if (m_data.contains(hash)) {
|
|
||||||
m_data.remove(hash);
|
|
||||||
dirty = true;
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveTorrentPersistentData(const QTorrentHandle &h, const QString &save_path, const bool is_magnet)
|
|
||||||
{
|
|
||||||
Q_ASSERT(h.is_valid());
|
|
||||||
QString hash = h.hash();
|
|
||||||
qDebug("Saving persistent data for %s", qPrintable(hash));
|
|
||||||
// Save persistent data
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["is_magnet"] = is_magnet;
|
|
||||||
if (is_magnet)
|
|
||||||
torrent["magnet_uri"] = misc::toQString(make_magnet_uri(h));
|
|
||||||
torrent["seed"] = h.is_seed();
|
|
||||||
torrent["priority"] = h.queue_position();
|
|
||||||
if (save_path.isEmpty()) {
|
|
||||||
qDebug("TorrentPersistantData: save path is %s", qPrintable(h.save_path()));
|
|
||||||
torrent["save_path"] = h.save_path();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug("TorrentPersistantData: overriding save path is %s", qPrintable(save_path));
|
|
||||||
torrent["save_path"] = save_path; // Override torrent save path (e.g. because it is a temp dir)
|
|
||||||
}
|
|
||||||
// Label
|
|
||||||
torrent["label"] = TorrentTempData::getLabel(hash);
|
|
||||||
// Save data
|
|
||||||
setValue(hash, torrent);
|
|
||||||
qDebug("TorrentPersistentData: Saving save_path %s, hash: %s", qPrintable(h.save_path()), qPrintable(hash));
|
|
||||||
// Set Added date
|
|
||||||
setAddedDate(hash, QDateTime::currentDateTime());
|
|
||||||
// Finally, remove temp data
|
|
||||||
TorrentTempData::deleteTempData(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveSavePath(const QString &hash, const QString &save_path)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!hash.isEmpty());
|
|
||||||
qDebug("TorrentPersistentData::saveSavePath(%s)", qPrintable(save_path));
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["save_path"] = save_path;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
qDebug("TorrentPersistentData: Saving save_path: %s, hash: %s", qPrintable(save_path), qPrintable(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveLabel(const QString &hash, const QString &label)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!hash.isEmpty());
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["label"] = label;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveName(const QString &hash, const QString &name)
|
|
||||||
{
|
|
||||||
Q_ASSERT(!hash.isEmpty());
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["name"] = name;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::savePriority(const QTorrentHandle &h)
|
|
||||||
{
|
|
||||||
QString hash = h.hash();
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["priority"] = h.queue_position();
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::savePriority(const QString &hash, const int &queue_pos)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["priority"] = queue_pos;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveSeedStatus(const QTorrentHandle &h)
|
|
||||||
{
|
|
||||||
QString hash = h.hash();
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
bool was_seed = torrent.value("seed", false).toBool();
|
|
||||||
if (was_seed != h.is_seed()) {
|
|
||||||
torrent["seed"] = !was_seed;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::saveSeedStatus(const QString &hash, const bool seedStatus)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["seed"] = seedStatus;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentPersistentData::setHasMissingFiles(const QString& hash, bool missing)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
torrent["has_missing_files"] = missing;
|
|
||||||
setValue(hash, torrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentPersistentData::getSavePath(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
//qDebug("TorrentPersistentData: getSavePath %s", data["save_path"].toString().toLocal8Bit().data());
|
|
||||||
return torrent.value("save_path").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentPersistentData::getLabel(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("label", "").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentPersistentData::getName(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("name", "").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
int TorrentPersistentData::getPriority(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("priority", -1).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::isSeed(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("seed", false).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::isMagnet(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("is_magnet", false).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TorrentPersistentData::getMagnetUri(const QString &hash) const
|
|
||||||
{
|
|
||||||
const QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
Q_ASSERT(torrent.value("is_magnet", false).toBool());
|
|
||||||
return torrent.value("magnet_uri").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TorrentPersistentData::getHasMissingFiles(const QString& hash)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> torrent = value(hash).toHash();
|
|
||||||
return torrent.value("has_missing_files").toBool();
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
/*
|
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
|
||||||
*
|
|
||||||
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
|
||||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
||||||
* and distribute the linked executables. 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), 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.
|
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TORRENTPERSISTENTDATA_H
|
|
||||||
#define TORRENTPERSISTENTDATA_H
|
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QReadWriteLock>
|
|
||||||
#include <vector>
|
|
||||||
#include "preferences.h"
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QDateTime;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class QTorrentHandle;
|
|
||||||
|
|
||||||
class TorrentTempData
|
|
||||||
{
|
|
||||||
// This class stores strings w/o modifying separators
|
|
||||||
public:
|
|
||||||
static bool hasTempData(const QString &hash);
|
|
||||||
static void deleteTempData(const QString &hash);
|
|
||||||
static void setFilesPriority(const QString &hash, const std::vector<int> &pp);
|
|
||||||
static void setFilesPath(const QString &hash, const QStringList &path_list);
|
|
||||||
static void setSavePath(const QString &hash, const QString &save_path);
|
|
||||||
static void setLabel(const QString &hash, const QString &label);
|
|
||||||
static void setSequential(const QString &hash, const bool &sequential);
|
|
||||||
static bool isSequential(const QString &hash);
|
|
||||||
static void setSeedingMode(const QString &hash, const bool &seed);
|
|
||||||
static bool isSeedingMode(const QString &hash);
|
|
||||||
static QString getSavePath(const QString &hash);
|
|
||||||
static QStringList getFilesPath(const QString &hash);
|
|
||||||
static QString getLabel(const QString &hash);
|
|
||||||
static void getFilesPriority(const QString &hash, std::vector<int> &fp);
|
|
||||||
static bool isMoveInProgress(const QString &hash);
|
|
||||||
static void enqueueMove(const QString &hash, const QString &queuedPath);
|
|
||||||
static void startMove(const QString &hash, const QString &oldPath, const QString& newPath);
|
|
||||||
static void finishMove(const QString &hash);
|
|
||||||
static QString getOldPath(const QString &hash);
|
|
||||||
static QString getNewPath(const QString &hash);
|
|
||||||
static QString getQueuedPath(const QString &hash);
|
|
||||||
static void setAddPaused(const QString &hash, const bool &paused);
|
|
||||||
static bool isAddPaused(const QString &hash);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct TorrentData
|
|
||||||
{
|
|
||||||
TorrentData();
|
|
||||||
std::vector<int> files_priority;
|
|
||||||
QStringList path_list;
|
|
||||||
QString save_path;
|
|
||||||
QString label;
|
|
||||||
bool sequential;
|
|
||||||
bool seed;
|
|
||||||
bool add_paused;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TorrentMoveState
|
|
||||||
{
|
|
||||||
TorrentMoveState(QString oldPath, QString newPath);
|
|
||||||
// the moving occurs from oldPath to newPath
|
|
||||||
// queuedPath is where files should be moved to, when current moving is completed
|
|
||||||
QString oldPath;
|
|
||||||
QString newPath;
|
|
||||||
QString queuedPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
static QHash<QString, TorrentData> data;
|
|
||||||
static QHash<QString, TorrentMoveState> torrentMoveStates;
|
|
||||||
};
|
|
||||||
|
|
||||||
class HiddenData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void addData(const QString &hash);
|
|
||||||
static bool hasData(const QString &hash);
|
|
||||||
static void deleteData(const QString &hash);
|
|
||||||
static int getSize();
|
|
||||||
static int getDownloadingSize();
|
|
||||||
static void gotMetadata(const QString &hash);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static QHash<QString, bool> data;
|
|
||||||
static unsigned int metadata_counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TorrentPersistentData: QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(TorrentPersistentData)
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum RatioLimit
|
|
||||||
{
|
|
||||||
USE_GLOBAL_RATIO = -2,
|
|
||||||
NO_RATIO_LIMIT = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
static TorrentPersistentData* instance();
|
|
||||||
static void drop();
|
|
||||||
~TorrentPersistentData();
|
|
||||||
|
|
||||||
bool isKnownTorrent(QString hash) const;
|
|
||||||
QStringList knownTorrents() const;
|
|
||||||
void setRatioLimit(const QString &hash, const qreal &ratio);
|
|
||||||
qreal getRatioLimit(const QString &hash) const;
|
|
||||||
bool hasPerTorrentRatioLimit() const;
|
|
||||||
void setAddedDate(const QString &hash, const QDateTime &time);
|
|
||||||
QDateTime getAddedDate(const QString &hash) const;
|
|
||||||
void setErrorState(const QString &hash, const bool has_error);
|
|
||||||
bool hasError(const QString &hash) const;
|
|
||||||
QDateTime getSeedDate(const QString &hash) const;
|
|
||||||
void deletePersistentData(const QString &hash);
|
|
||||||
void saveTorrentPersistentData(const QTorrentHandle &h, const QString &save_path = QString::null, const bool is_magnet = false);
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
void saveSavePath(const QString &hash, const QString &save_path);
|
|
||||||
void saveLabel(const QString &hash, const QString &label);
|
|
||||||
void saveName(const QString &hash, const QString &name);
|
|
||||||
void savePriority(const QTorrentHandle &h);
|
|
||||||
void savePriority(const QString &hash, const int &queue_pos);
|
|
||||||
void saveSeedStatus(const QTorrentHandle &h);
|
|
||||||
void saveSeedStatus(const QString &hash, const bool seedStatus);
|
|
||||||
void setHasMissingFiles(const QString &hash, bool missing);
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
QString getSavePath(const QString &hash) const;
|
|
||||||
QString getLabel(const QString &hash) const;
|
|
||||||
QString getName(const QString &hash) const;
|
|
||||||
int getPriority(const QString &hash) const;
|
|
||||||
bool isSeed(const QString &hash) const;
|
|
||||||
bool isMagnet(const QString &hash) const;
|
|
||||||
QString getMagnetUri(const QString &hash) const;
|
|
||||||
bool getHasMissingFiles(const QString& hash);
|
|
||||||
public slots:
|
|
||||||
void save();
|
|
||||||
|
|
||||||
private:
|
|
||||||
TorrentPersistentData();
|
|
||||||
static TorrentPersistentData* m_instance;
|
|
||||||
QHash<QString, QVariant> m_data;
|
|
||||||
bool dirty;
|
|
||||||
QTimer timer;
|
|
||||||
mutable QReadWriteLock lock;
|
|
||||||
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
|
||||||
void setValue(const QString &key, const QVariant &value);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TORRENTPERSISTENTDATA_H
|
|
||||||
60
src/core/tristatebool.cpp
Normal file
60
src/core/tristatebool.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tristatebool.h"
|
||||||
|
|
||||||
|
TriStateBool::TriStateBool()
|
||||||
|
: m_value(Undefined)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TriStateBool::TriStateBool(bool b)
|
||||||
|
{
|
||||||
|
m_value = (b ? True : False);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriStateBool::TriStateBool(TriStateBool::ValueType value)
|
||||||
|
: m_value(Undefined)
|
||||||
|
{
|
||||||
|
switch (value) {
|
||||||
|
case Undefined:
|
||||||
|
case True:
|
||||||
|
case False:
|
||||||
|
m_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TriStateBool::operator bool() const
|
||||||
|
{
|
||||||
|
return (m_value == True);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriStateBool::operator ValueType() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,36 +24,30 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* modify file(s), 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TRACKERINFOS_H
|
#ifndef TRISTATEBOOL_H
|
||||||
#define TRACKERINFOS_H
|
#define TRISTATEBOOL_H
|
||||||
|
|
||||||
#include <QString>
|
class TriStateBool
|
||||||
|
{
|
||||||
class TrackerInfos {
|
|
||||||
public:
|
public:
|
||||||
QString name_or_url;
|
enum ValueType
|
||||||
QString last_message;
|
|
||||||
unsigned long num_peers;
|
|
||||||
|
|
||||||
//TrackerInfos() {}
|
|
||||||
TrackerInfos(const TrackerInfos &b)
|
|
||||||
: name_or_url(b.name_or_url)
|
|
||||||
, last_message(b.last_message)
|
|
||||||
, num_peers(b.num_peers)
|
|
||||||
{
|
{
|
||||||
Q_ASSERT(!name_or_url.isEmpty());
|
Undefined = -1,
|
||||||
}
|
False = 0,
|
||||||
|
True = 1
|
||||||
|
};
|
||||||
|
|
||||||
TrackerInfos(QString name_or_url)
|
TriStateBool();
|
||||||
: name_or_url(name_or_url)
|
TriStateBool(bool b);
|
||||||
, last_message("")
|
TriStateBool(ValueType value);
|
||||||
, num_peers(0)
|
|
||||||
{
|
operator ValueType() const;
|
||||||
}
|
operator bool() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ValueType m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRACKERINFOS_H
|
#endif // TRISTATEBOOL_H
|
||||||
73
src/core/types.h
Normal file
73
src/core/types.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TYPES_H
|
||||||
|
#define TYPES_H
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#define BEGIN_SCOPED_ENUM(name) class name\
|
||||||
|
{\
|
||||||
|
int m_val;\
|
||||||
|
\
|
||||||
|
public:\
|
||||||
|
name() {}\
|
||||||
|
name(int val) : m_val(val) {}\
|
||||||
|
operator int() const { return m_val; }\
|
||||||
|
operator QVariant() const { return m_val; }\
|
||||||
|
\
|
||||||
|
enum
|
||||||
|
|
||||||
|
#define END_SCOPED_ENUM ; };
|
||||||
|
|
||||||
|
const qlonglong MAX_ETA = 8640000;
|
||||||
|
|
||||||
|
BEGIN_SCOPED_ENUM(MaxRatioAction)
|
||||||
|
{
|
||||||
|
Pause,
|
||||||
|
Remove
|
||||||
|
}
|
||||||
|
END_SCOPED_ENUM
|
||||||
|
|
||||||
|
BEGIN_SCOPED_ENUM(TorrentExportFolder)
|
||||||
|
{
|
||||||
|
Regular,
|
||||||
|
Finished
|
||||||
|
}
|
||||||
|
END_SCOPED_ENUM
|
||||||
|
|
||||||
|
BEGIN_SCOPED_ENUM(ShutdownAction)
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Shutdown,
|
||||||
|
Suspend,
|
||||||
|
Hibernate
|
||||||
|
}
|
||||||
|
END_SCOPED_ENUM
|
||||||
|
|
||||||
|
#endif // TYPES_H
|
||||||
492
src/core/utils/fs.cpp
Normal file
492
src/core/utils/fs.cpp
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2012 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#elif defined(Q_OS_HAIKU)
|
||||||
|
#include <kernel/fs_info.h>
|
||||||
|
#else
|
||||||
|
#include <sys/vfs.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#else
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
#include "fs.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a path to a string suitable for display.
|
||||||
|
* This function makes sure the directory separator used is consistent
|
||||||
|
* with the OS being run.
|
||||||
|
*/
|
||||||
|
QString Utils::Fs::toNativePath(const QString& path)
|
||||||
|
{
|
||||||
|
return QDir::toNativeSeparators(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::fromNativePath(const QString &path)
|
||||||
|
{
|
||||||
|
return QDir::fromNativeSeparators(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file extension part of a file name.
|
||||||
|
*/
|
||||||
|
QString Utils::Fs::fileExtension(const QString &filename)
|
||||||
|
{
|
||||||
|
QString ext = QString(filename).remove(".!qB");
|
||||||
|
const int point_index = ext.lastIndexOf(".");
|
||||||
|
return (point_index >= 0) ? ext.mid(point_index + 1) : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::fileName(const QString& file_path)
|
||||||
|
{
|
||||||
|
QString path = fromNativePath(file_path);
|
||||||
|
const int slash_index = path.lastIndexOf("/");
|
||||||
|
if (slash_index == -1)
|
||||||
|
return path;
|
||||||
|
return path.mid(slash_index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::folderName(const QString& file_path)
|
||||||
|
{
|
||||||
|
QString path = fromNativePath(file_path);
|
||||||
|
const int slash_index = path.lastIndexOf("/");
|
||||||
|
if (slash_index == -1)
|
||||||
|
return path;
|
||||||
|
return path.left(slash_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an empty folder tree.
|
||||||
|
*
|
||||||
|
* This function will also remove .DS_Store files on Mac OS and
|
||||||
|
* Thumbs.db on Windows.
|
||||||
|
*/
|
||||||
|
bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& dir_path)
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO << dir_path;
|
||||||
|
if (dir_path.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QDir dir(dir_path);
|
||||||
|
if (!dir.exists())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Remove Files created by the OS
|
||||||
|
#if defined Q_OS_MAC
|
||||||
|
forceRemove(dir_path + QLatin1String("/.DS_Store"));
|
||||||
|
#elif defined Q_OS_WIN
|
||||||
|
forceRemove(dir_path + QLatin1String("/Thumbs.db"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QFileInfoList sub_files = dir.entryInfoList();
|
||||||
|
foreach (const QFileInfo& info, sub_files) {
|
||||||
|
QString sub_name = info.fileName();
|
||||||
|
if (sub_name == "." || sub_name == "..")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QString sub_path = info.absoluteFilePath();
|
||||||
|
qDebug() << Q_FUNC_INFO << "sub file: " << sub_path;
|
||||||
|
if (info.isDir()) {
|
||||||
|
if (!smartRemoveEmptyFolderTree(sub_path)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (info.isHidden()) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path;
|
||||||
|
if (!forceRemove(sub_path)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path;
|
||||||
|
return QDir().rmdir(dir_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the file with the given file_path.
|
||||||
|
*
|
||||||
|
* This function will try to fix the file permissions before removing it.
|
||||||
|
*/
|
||||||
|
bool Utils::Fs::forceRemove(const QString& file_path)
|
||||||
|
{
|
||||||
|
QFile f(file_path);
|
||||||
|
if (!f.exists())
|
||||||
|
return true;
|
||||||
|
// Make sure we have read/write permissions
|
||||||
|
f.setPermissions(f.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
||||||
|
// Remove the file
|
||||||
|
return f.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of a file.
|
||||||
|
* If the file is a folder, it will compute its size based on its content.
|
||||||
|
*
|
||||||
|
* Returns -1 in case of error.
|
||||||
|
*/
|
||||||
|
qint64 Utils::Fs::computePathSize(const QString& path)
|
||||||
|
{
|
||||||
|
// Check if it is a file
|
||||||
|
QFileInfo fi(path);
|
||||||
|
if (!fi.exists()) return -1;
|
||||||
|
if (fi.isFile()) return fi.size();
|
||||||
|
// Compute folder size based on its content
|
||||||
|
qint64 size = 0;
|
||||||
|
foreach (const QFileInfo &subfi, QDir(path).entryInfoList(QDir::Dirs|QDir::Files)) {
|
||||||
|
if (subfi.fileName().startsWith(".")) continue;
|
||||||
|
if (subfi.isDir())
|
||||||
|
size += computePathSize(subfi.absoluteFilePath());
|
||||||
|
else
|
||||||
|
size += subfi.size();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes deep comparison of two files to make sure they are identical.
|
||||||
|
*/
|
||||||
|
bool Utils::Fs::sameFiles(const QString& path1, const QString& path2)
|
||||||
|
{
|
||||||
|
QFile f1(path1), f2(path2);
|
||||||
|
if (!f1.exists() || !f2.exists()) return false;
|
||||||
|
if (f1.size() != f2.size()) return false;
|
||||||
|
if (!f1.open(QIODevice::ReadOnly)) return false;
|
||||||
|
if (!f2.open(QIODevice::ReadOnly)) {
|
||||||
|
f1.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool same = true;
|
||||||
|
while(!f1.atEnd() && !f2.atEnd()) {
|
||||||
|
if (f1.read(1024) != f2.read(1024)) {
|
||||||
|
same = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f1.close(); f2.close();
|
||||||
|
return same;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::toValidFileSystemName(QString filename)
|
||||||
|
{
|
||||||
|
qDebug("toValidFSName: %s", qPrintable(filename));
|
||||||
|
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||||
|
filename.replace(regex, " ");
|
||||||
|
qDebug("toValidFSName, result: %s", qPrintable(filename));
|
||||||
|
return filename.trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::Fs::isValidFileSystemName(const QString& filename)
|
||||||
|
{
|
||||||
|
if (filename.isEmpty()) return false;
|
||||||
|
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||||
|
return !filename.contains(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path)
|
||||||
|
{
|
||||||
|
if (path.isEmpty()) return -1;
|
||||||
|
QDir dir_path(path);
|
||||||
|
if (!dir_path.exists()) {
|
||||||
|
QStringList parts = path.split("/");
|
||||||
|
while (parts.size() > 1 && !QDir(parts.join("/")).exists()) {
|
||||||
|
parts.removeLast();
|
||||||
|
}
|
||||||
|
dir_path = QDir(parts.join("/"));
|
||||||
|
if (!dir_path.exists()) return -1;
|
||||||
|
}
|
||||||
|
Q_ASSERT(dir_path.exists());
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
unsigned long long available;
|
||||||
|
#ifdef Q_OS_HAIKU
|
||||||
|
const QString statfs_path = dir_path.path() + "/.";
|
||||||
|
dev_t device = dev_for_path (qPrintable(statfs_path));
|
||||||
|
if (device >= 0) {
|
||||||
|
fs_info info;
|
||||||
|
if (fs_stat_dev(device, &info) == B_OK) {
|
||||||
|
available = ((unsigned long long)(info.free_blocks * info.block_size));
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
struct statfs stats;
|
||||||
|
const QString statfs_path = dir_path.path() + "/.";
|
||||||
|
const int ret = statfs(qPrintable(statfs_path), &stats);
|
||||||
|
if (ret == 0) {
|
||||||
|
available = ((unsigned long long)stats.f_bavail)
|
||||||
|
* ((unsigned long long)stats.f_bsize);
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
|
||||||
|
PULARGE_INTEGER,
|
||||||
|
PULARGE_INTEGER,
|
||||||
|
PULARGE_INTEGER);
|
||||||
|
GetDiskFreeSpaceEx_t pGetDiskFreeSpaceEx =
|
||||||
|
(GetDiskFreeSpaceEx_t)::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "GetDiskFreeSpaceExW");
|
||||||
|
if (pGetDiskFreeSpaceEx) {
|
||||||
|
ULARGE_INTEGER bytesFree, bytesTotal;
|
||||||
|
unsigned long long *ret;
|
||||||
|
if (pGetDiskFreeSpaceEx((LPCTSTR)(toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) {
|
||||||
|
ret = (unsigned long long*)&bytesFree;
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::branchPath(const QString& file_path, QString* removed)
|
||||||
|
{
|
||||||
|
QString ret = fromNativePath(file_path);
|
||||||
|
if (ret.endsWith("/"))
|
||||||
|
ret.chop(1);
|
||||||
|
const int slashIndex = ret.lastIndexOf("/");
|
||||||
|
if (slashIndex >= 0) {
|
||||||
|
if (removed)
|
||||||
|
*removed = ret.mid(slashIndex + 1);
|
||||||
|
ret = ret.left(slashIndex);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::Fs::sameFileNames(const QString &first, const QString &second)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||||
|
return QString::compare(first, second, Qt::CaseSensitive) == 0;
|
||||||
|
#else
|
||||||
|
return QString::compare(first, second, Qt::CaseInsensitive) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::expandPath(const QString &path)
|
||||||
|
{
|
||||||
|
QString ret = fromNativePath(path.trimmed());
|
||||||
|
if (ret.isEmpty())
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return QDir::cleanPath(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::expandPathAbs(const QString& path)
|
||||||
|
{
|
||||||
|
QString ret = expandPath(path);
|
||||||
|
|
||||||
|
if (!QDir::isAbsolutePath(ret))
|
||||||
|
ret = QDir(ret).absolutePath();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::QDesktopServicesDataLocation()
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
LPWSTR path=new WCHAR[256];
|
||||||
|
if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
|
||||||
|
result = fromNativePath(QString::fromWCharArray(path));
|
||||||
|
if (!QCoreApplication::applicationName().isEmpty())
|
||||||
|
result += QLatin1String("/") + qApp->applicationName();
|
||||||
|
#else
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
FSRef ref;
|
||||||
|
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
|
||||||
|
if (err)
|
||||||
|
return QString();
|
||||||
|
QString path;
|
||||||
|
QByteArray ba(2048, 0);
|
||||||
|
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
||||||
|
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
||||||
|
result += QLatin1Char('/') + qApp->applicationName();
|
||||||
|
#else
|
||||||
|
QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
|
||||||
|
if (xdgDataHome.isEmpty())
|
||||||
|
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
|
||||||
|
xdgDataHome += QLatin1String("/data/")
|
||||||
|
+ qApp->applicationName();
|
||||||
|
result = xdgDataHome;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (!result.endsWith("/"))
|
||||||
|
result += "/";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::QDesktopServicesCacheLocation()
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||||
|
result = QDesktopServicesDataLocation() + QLatin1String("cache");
|
||||||
|
#else
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
|
||||||
|
FSRef ref;
|
||||||
|
OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref);
|
||||||
|
if (err)
|
||||||
|
return QString();
|
||||||
|
QByteArray ba(2048, 0);
|
||||||
|
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
||||||
|
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
||||||
|
result += QLatin1Char('/') + qApp->applicationName();
|
||||||
|
#else
|
||||||
|
QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
|
||||||
|
if (xdgCacheHome.isEmpty())
|
||||||
|
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
|
||||||
|
xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName();
|
||||||
|
result = xdgCacheHome;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
if (!result.endsWith("/"))
|
||||||
|
result += "/";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::QDesktopServicesDownloadLocation()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||||
|
// as long as it stays WinXP like we do the same on OS/2
|
||||||
|
// TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads
|
||||||
|
// instead of hardcoding "Downloads"
|
||||||
|
// Unfortunately, this would break compatibility with WinXP
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||||
|
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
|
||||||
|
QCoreApplication::translate("fsutils", "Downloads"));
|
||||||
|
#else
|
||||||
|
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
|
||||||
|
QCoreApplication::translate("fsutils", "Downloads"));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||||
|
QString save_path;
|
||||||
|
// Default save path on Linux
|
||||||
|
QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData());
|
||||||
|
if (config_path.isEmpty())
|
||||||
|
config_path = QDir::home().absoluteFilePath(".config");
|
||||||
|
|
||||||
|
QString user_dirs_file = config_path + "/user-dirs.dirs";
|
||||||
|
if (QFile::exists(user_dirs_file)) {
|
||||||
|
QSettings settings(user_dirs_file, QSettings::IniFormat);
|
||||||
|
// We need to force UTF-8 encoding here since this is not
|
||||||
|
// the default for Ini files.
|
||||||
|
settings.setIniCodec("UTF-8");
|
||||||
|
QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString();
|
||||||
|
if (!xdg_download_dir.isEmpty()) {
|
||||||
|
// Resolve $HOME environment variables
|
||||||
|
xdg_download_dir.replace("$HOME", QDir::homePath());
|
||||||
|
save_path = xdg_download_dir;
|
||||||
|
qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
if (!save_path.isEmpty() && !QFile::exists(save_path)) {
|
||||||
|
QDir().mkpath(save_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save_path.isEmpty() || !QFile::exists(save_path)) {
|
||||||
|
save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
||||||
|
qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work";
|
||||||
|
}
|
||||||
|
|
||||||
|
return save_path;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// TODO: How to support this on Mac OS X?
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::searchEngineLocation()
|
||||||
|
{
|
||||||
|
QString folder = "nova";
|
||||||
|
if (Utils::Misc::pythonVersion() >= 3)
|
||||||
|
folder = "nova3";
|
||||||
|
const QString location = expandPathAbs(QDesktopServicesDataLocation() + folder);
|
||||||
|
QDir locationDir(location);
|
||||||
|
if (!locationDir.exists())
|
||||||
|
locationDir.mkpath(locationDir.absolutePath());
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::Fs::cacheLocation()
|
||||||
|
{
|
||||||
|
QString location = expandPathAbs(QDesktopServicesCacheLocation());
|
||||||
|
QDir locationDir(location);
|
||||||
|
if (!locationDir.exists())
|
||||||
|
locationDir.mkpath(locationDir.absolutePath());
|
||||||
|
return location;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2012 Christophe Dumez
|
* Copyright (C) 2012 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -28,46 +28,44 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FS_UTILS_H
|
#ifndef UTILS_FS_H
|
||||||
#define FS_UTILS_H
|
#define UTILS_FS_H
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions related to file system.
|
* Utility functions related to file system.
|
||||||
*/
|
*/
|
||||||
namespace fsutils
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
{
|
{
|
||||||
|
namespace Fs
|
||||||
|
{
|
||||||
|
QString toNativePath(const QString& path);
|
||||||
|
QString fromNativePath(const QString& path);
|
||||||
|
QString fileExtension(const QString& filename);
|
||||||
|
QString fileName(const QString& file_path);
|
||||||
|
QString folderName(const QString& file_path);
|
||||||
|
qint64 computePathSize(const QString& path);
|
||||||
|
bool sameFiles(const QString& path1, const QString& path2);
|
||||||
|
QString toValidFileSystemName(QString filename);
|
||||||
|
bool isValidFileSystemName(const QString& filename);
|
||||||
|
qlonglong freeDiskSpaceOnPath(QString path);
|
||||||
|
QString branchPath(const QString& file_path, QString* removed = 0);
|
||||||
|
bool sameFileNames(const QString& first, const QString& second);
|
||||||
|
QString expandPath(const QString& path);
|
||||||
|
QString expandPathAbs(const QString& path);
|
||||||
|
bool smartRemoveEmptyFolderTree(const QString& dir_path);
|
||||||
|
bool forceRemove(const QString& file_path);
|
||||||
|
|
||||||
QString toNativePath(const QString& path);
|
/* Ported from Qt4 to drop dependency on QtGui */
|
||||||
QString fromNativePath(const QString& path);
|
QString QDesktopServicesDataLocation();
|
||||||
QString fileExtension(const QString& filename);
|
QString QDesktopServicesCacheLocation();
|
||||||
QString fileName(const QString& file_path);
|
QString QDesktopServicesDownloadLocation();
|
||||||
QString folderName(const QString& file_path);
|
/* End of Qt4 code */
|
||||||
qint64 computePathSize(const QString& path);
|
QString searchEngineLocation();
|
||||||
bool sameFiles(const QString& path1, const QString& path2);
|
QString cacheLocation();
|
||||||
QString updateLabelInSavePath(const QString &defaultSavePath, const QString &save_path, const QString& old_label, const QString& new_label);
|
}
|
||||||
QString toValidFileSystemName(QString filename);
|
|
||||||
bool isValidFileSystemName(const QString& filename);
|
|
||||||
long long freeDiskSpaceOnPath(QString path);
|
|
||||||
QString branchPath(const QString& file_path, QString* removed = 0);
|
|
||||||
bool sameFileNames(const QString& first, const QString& second);
|
|
||||||
QString expandPath(const QString& path);
|
|
||||||
QString expandPathAbs(const QString& path);
|
|
||||||
bool isValidTorrentFile(const QString& path);
|
|
||||||
bool smartRemoveEmptyFolderTree(const QString& dir_path);
|
|
||||||
bool forceRemove(const QString& file_path);
|
|
||||||
|
|
||||||
/* Ported from Qt4 to drop dependency on QtGui */
|
|
||||||
QString QDesktopServicesDataLocation();
|
|
||||||
QString QDesktopServicesCacheLocation();
|
|
||||||
QString QDesktopServicesDownloadLocation();
|
|
||||||
/* End of Qt4 code */
|
|
||||||
QString searchEngineLocation();
|
|
||||||
QString BTBackupLocation();
|
|
||||||
QString cacheLocation();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FS_UTILS_H
|
#endif // UTILS_FS_H
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -28,10 +28,6 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -40,7 +36,6 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QLocale>
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
@@ -71,14 +66,8 @@ const int UNLEN = 256;
|
|||||||
#endif
|
#endif
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
#include "core/utils/string.h"
|
||||||
#include <libtorrent/peer_id.hpp>
|
#include "misc.h"
|
||||||
#else
|
|
||||||
#include <libtorrent/sha1_hash.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
|
||||||
|
|
||||||
static struct { const char *source; const char *comment; } units[] = {
|
static struct { const char *source; const char *comment; } units[] = {
|
||||||
QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
|
QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
|
||||||
@@ -88,44 +77,17 @@ static struct { const char *source; const char *comment; } units[] = {
|
|||||||
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
|
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
|
||||||
};
|
};
|
||||||
|
|
||||||
QString misc::toQString(const std::string &str)
|
|
||||||
{
|
|
||||||
return QString::fromLocal8Bit(str.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString misc::toQString(const char* str)
|
|
||||||
{
|
|
||||||
return QString::fromLocal8Bit(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString misc::toQStringU(const std::string &str)
|
|
||||||
{
|
|
||||||
return QString::fromUtf8(str.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString misc::toQStringU(const char* str)
|
|
||||||
{
|
|
||||||
return QString::fromUtf8(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString misc::toQString(const libtorrent::sha1_hash &hash)
|
|
||||||
{
|
|
||||||
char out[41];
|
|
||||||
libtorrent::to_hex((char const*)&hash[0], libtorrent::sha1_hash::size, out);
|
|
||||||
return QString(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
void misc::shutdownComputer(shutDownAction action)
|
void Utils::Misc::shutdownComputer(ShutdownAction action)
|
||||||
{
|
{
|
||||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
|
||||||
// Use dbus to power off / suspend the system
|
// Use dbus to power off / suspend the system
|
||||||
if (action != SHUTDOWN_COMPUTER) {
|
if (action != ShutdownAction::Shutdown) {
|
||||||
// Some recent systems use systemd's logind
|
// Some recent systems use systemd's logind
|
||||||
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
|
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
|
||||||
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
|
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
|
||||||
if (login1Iface.isValid()) {
|
if (login1Iface.isValid()) {
|
||||||
if (action == SUSPEND_COMPUTER)
|
if (action == ShutdownAction::Suspend)
|
||||||
login1Iface.call("Suspend", false);
|
login1Iface.call("Suspend", false);
|
||||||
else
|
else
|
||||||
login1Iface.call("Hibernate", false);
|
login1Iface.call("Hibernate", false);
|
||||||
@@ -135,7 +97,7 @@ void misc::shutdownComputer(shutDownAction action)
|
|||||||
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
|
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
|
||||||
"org.freedesktop.UPower", QDBusConnection::systemBus());
|
"org.freedesktop.UPower", QDBusConnection::systemBus());
|
||||||
if (upowerIface.isValid()) {
|
if (upowerIface.isValid()) {
|
||||||
if (action == SUSPEND_COMPUTER)
|
if (action == ShutdownAction::Suspend)
|
||||||
upowerIface.call("Suspend");
|
upowerIface.call("Suspend");
|
||||||
else
|
else
|
||||||
upowerIface.call("Hibernate");
|
upowerIface.call("Hibernate");
|
||||||
@@ -145,7 +107,7 @@ void misc::shutdownComputer(shutDownAction action)
|
|||||||
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
|
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
|
||||||
"org.freedesktop.Hal.Device.SystemPowerManagement",
|
"org.freedesktop.Hal.Device.SystemPowerManagement",
|
||||||
QDBusConnection::systemBus());
|
QDBusConnection::systemBus());
|
||||||
if (action == SUSPEND_COMPUTER)
|
if (action == ShutdownAction::Suspend)
|
||||||
halIface.call("Suspend", 5);
|
halIface.call("Suspend", 5);
|
||||||
else
|
else
|
||||||
halIface.call("Hibernate");
|
halIface.call("Hibernate");
|
||||||
@@ -174,7 +136,7 @@ void misc::shutdownComputer(shutDownAction action)
|
|||||||
#endif
|
#endif
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
AEEventID EventToSend;
|
AEEventID EventToSend;
|
||||||
if (action != SHUTDOWN_COMPUTER)
|
if (action != ShutdownAction::Shutdown)
|
||||||
EventToSend = kAESleep;
|
EventToSend = kAESleep;
|
||||||
else
|
else
|
||||||
EventToSend = kAEShutDown;
|
EventToSend = kAEShutDown;
|
||||||
@@ -229,9 +191,9 @@ void misc::shutdownComputer(shutDownAction action)
|
|||||||
if (GetLastError() != ERROR_SUCCESS)
|
if (GetLastError() != ERROR_SUCCESS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (action == SUSPEND_COMPUTER)
|
if (action == ShutdownAction::Suspend)
|
||||||
SetSuspendState(false, false, false);
|
SetSuspendState(false, false, false);
|
||||||
else if (action == HIBERNATE_COMPUTER)
|
else if (action == ShutdownAction::Hibernate)
|
||||||
SetSuspendState(true, false, false);
|
SetSuspendState(true, false, false);
|
||||||
else
|
else
|
||||||
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
|
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
|
||||||
@@ -246,7 +208,7 @@ void misc::shutdownComputer(shutDownAction action)
|
|||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
// Get screen center
|
// Get screen center
|
||||||
QPoint misc::screenCenter(QWidget *win)
|
QPoint Utils::Misc::screenCenter(QWidget *win)
|
||||||
{
|
{
|
||||||
int scrn = 0;
|
int scrn = 0;
|
||||||
const QWidget *w = win->window();
|
const QWidget *w = win->window();
|
||||||
@@ -267,7 +229,7 @@ QPoint misc::screenCenter(QWidget *win)
|
|||||||
* Detects the version of python by calling
|
* Detects the version of python by calling
|
||||||
* "python --version" and parsing the output.
|
* "python --version" and parsing the output.
|
||||||
*/
|
*/
|
||||||
int misc::pythonVersion()
|
int Utils::Misc::pythonVersion()
|
||||||
{
|
{
|
||||||
static int version = -1;
|
static int version = -1;
|
||||||
if (version < 0) {
|
if (version < 0) {
|
||||||
@@ -293,7 +255,7 @@ int misc::pythonVersion()
|
|||||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||||
// value must be given in bytes
|
// value must be given in bytes
|
||||||
// to send numbers instead of strings with suffixes
|
// to send numbers instead of strings with suffixes
|
||||||
QString misc::friendlyUnit(qreal val, bool is_speed)
|
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
|
||||||
{
|
{
|
||||||
if (val < 0)
|
if (val < 0)
|
||||||
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
|
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
|
||||||
@@ -304,13 +266,13 @@ QString misc::friendlyUnit(qreal val, bool is_speed)
|
|||||||
if (i == 0)
|
if (i == 0)
|
||||||
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
|
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
|
||||||
else
|
else
|
||||||
ret = accurateDoubleToString(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
|
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
|
||||||
if (is_speed)
|
if (is_speed)
|
||||||
ret += QCoreApplication::translate("misc", "/s", "per second");
|
ret += QCoreApplication::translate("misc", "/s", "per second");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool misc::isPreviewable(const QString& extension)
|
bool Utils::Misc::isPreviewable(const QString& extension)
|
||||||
{
|
{
|
||||||
static QSet<QString> multimedia_extensions;
|
static QSet<QString> multimedia_extensions;
|
||||||
if (multimedia_extensions.empty()) {
|
if (multimedia_extensions.empty()) {
|
||||||
@@ -363,7 +325,7 @@ bool misc::isPreviewable(const QString& extension)
|
|||||||
return multimedia_extensions.contains(extension.toUpper());
|
return multimedia_extensions.contains(extension.toUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString misc::bcLinkToMagnet(QString bc_link)
|
QString Utils::Misc::bcLinkToMagnet(QString bc_link)
|
||||||
{
|
{
|
||||||
QByteArray raw_bc = bc_link.toUtf8();
|
QByteArray raw_bc = bc_link.toUtf8();
|
||||||
raw_bc = raw_bc.mid(8); // skip bc://bt/
|
raw_bc = raw_bc.mid(8); // skip bc://bt/
|
||||||
@@ -378,31 +340,9 @@ QString misc::bcLinkToMagnet(QString bc_link)
|
|||||||
return magnet;
|
return magnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString misc::magnetUriToName(const QString& magnet_uri)
|
|
||||||
{
|
|
||||||
add_torrent_params p;
|
|
||||||
error_code ec;
|
|
||||||
parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec);
|
|
||||||
|
|
||||||
if (ec)
|
|
||||||
return QString::null;
|
|
||||||
return toQStringU(p.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString misc::magnetUriToHash(const QString& magnet_uri)
|
|
||||||
{
|
|
||||||
add_torrent_params p;
|
|
||||||
error_code ec;
|
|
||||||
parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec);
|
|
||||||
|
|
||||||
if (ec)
|
|
||||||
return QString::null;
|
|
||||||
return toQString(p.info_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take a number of seconds and return an user-friendly
|
// Take a number of seconds and return an user-friendly
|
||||||
// time duration like "1d 2h 10m".
|
// time duration like "1d 2h 10m".
|
||||||
QString misc::userFriendlyDuration(qlonglong seconds)
|
QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
|
||||||
{
|
{
|
||||||
if (seconds < 0 || seconds >= MAX_ETA)
|
if (seconds < 0 || seconds >= MAX_ETA)
|
||||||
return QString::fromUtf8("∞");
|
return QString::fromUtf8("∞");
|
||||||
@@ -424,7 +364,7 @@ QString misc::userFriendlyDuration(qlonglong seconds)
|
|||||||
return QString::fromUtf8("∞");
|
return QString::fromUtf8("∞");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString misc::getUserIDString()
|
QString Utils::Misc::getUserIDString()
|
||||||
{
|
{
|
||||||
QString uid = "0";
|
QString uid = "0";
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@@ -438,7 +378,7 @@ QString misc::getUserIDString()
|
|||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList misc::toStringList(const QList<bool> &l)
|
QStringList Utils::Misc::toStringList(const QList<bool> &l)
|
||||||
{
|
{
|
||||||
QStringList ret;
|
QStringList ret;
|
||||||
foreach (const bool &b, l)
|
foreach (const bool &b, l)
|
||||||
@@ -446,7 +386,7 @@ QStringList misc::toStringList(const QList<bool> &l)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> misc::intListfromStringList(const QStringList &l)
|
QList<int> Utils::Misc::intListfromStringList(const QStringList &l)
|
||||||
{
|
{
|
||||||
QList<int> ret;
|
QList<int> ret;
|
||||||
foreach (const QString &s, l)
|
foreach (const QString &s, l)
|
||||||
@@ -454,7 +394,7 @@ QList<int> misc::intListfromStringList(const QStringList &l)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<bool> misc::boolListfromStringList(const QStringList &l)
|
QList<bool> Utils::Misc::boolListfromStringList(const QStringList &l)
|
||||||
{
|
{
|
||||||
QList<bool> ret;
|
QList<bool> ret;
|
||||||
foreach (const QString &s, l)
|
foreach (const QString &s, l)
|
||||||
@@ -462,14 +402,14 @@ QList<bool> misc::boolListfromStringList(const QStringList &l)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool misc::isUrl(const QString &s)
|
bool Utils::Misc::isUrl(const QString &s)
|
||||||
{
|
{
|
||||||
const QString scheme = QUrl(s).scheme();
|
const QString scheme = QUrl(s).scheme();
|
||||||
QRegExp is_url("http[s]?|ftp", Qt::CaseInsensitive);
|
QRegExp is_url("http[s]?|ftp", Qt::CaseInsensitive);
|
||||||
return is_url.exactMatch(scheme);
|
return is_url.exactMatch(scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString misc::parseHtmlLinks(const QString &raw_text)
|
QString Utils::Misc::parseHtmlLinks(const QString &raw_text)
|
||||||
{
|
{
|
||||||
QString result = raw_text;
|
QString result = raw_text;
|
||||||
static QRegExp reURL(
|
static QRegExp reURL(
|
||||||
@@ -530,124 +470,20 @@ QString misc::parseHtmlLinks(const QString &raw_text)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString misc::toQString(time_t t)
|
namespace
|
||||||
{
|
{
|
||||||
return QDateTime::fromTime_t(t).toString(Qt::DefaultLocaleLongDate);
|
// Trick to get a portable sleep() function
|
||||||
}
|
class SleeperThread : public QThread
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
|
||||||
bool misc::naturalSort(QString left, QString right, bool &result) // uses lessThan comparison
|
|
||||||
{ // Return value indicates if functions was successful
|
|
||||||
// result argument will contain actual comparison result if function was successful
|
|
||||||
int posL = 0;
|
|
||||||
int posR = 0;
|
|
||||||
do {
|
|
||||||
for (;; ) {
|
|
||||||
if (posL == left.size() || posR == right.size())
|
|
||||||
return false; // No data
|
|
||||||
|
|
||||||
QChar leftChar = left.at(posL);
|
|
||||||
QChar rightChar = right.at(posR);
|
|
||||||
bool leftCharIsDigit = leftChar.isDigit();
|
|
||||||
bool rightCharIsDigit = rightChar.isDigit();
|
|
||||||
if (leftCharIsDigit != rightCharIsDigit)
|
|
||||||
return false; // Digit positions mismatch
|
|
||||||
|
|
||||||
if (leftCharIsDigit)
|
|
||||||
break; // Both are digit, break this loop and compare numbers
|
|
||||||
|
|
||||||
if (leftChar != rightChar)
|
|
||||||
return false; // Strings' subsets before digit do not match
|
|
||||||
|
|
||||||
++posL;
|
|
||||||
++posR;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString temp;
|
|
||||||
while (posL < left.size()) {
|
|
||||||
if (left.at(posL).isDigit())
|
|
||||||
temp += left.at(posL);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
posL++;
|
|
||||||
}
|
|
||||||
int numL = temp.toInt();
|
|
||||||
temp.clear();
|
|
||||||
|
|
||||||
while (posR < right.size()) {
|
|
||||||
if (right.at(posR).isDigit())
|
|
||||||
temp += right.at(posR);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
posR++;
|
|
||||||
}
|
|
||||||
int numR = temp.toInt();
|
|
||||||
|
|
||||||
if (numL != numR) {
|
|
||||||
result = (numL < numR);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings + digits do match and we haven't hit string end
|
|
||||||
// Do another round
|
|
||||||
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// to send numbers instead of strings with suffixes
|
|
||||||
QString misc::accurateDoubleToString(const double &n, const int &precision)
|
|
||||||
{
|
|
||||||
/* HACK because QString rounds up. Eg QString::number(0.999*100.0, 'f' ,1) == 99.9
|
|
||||||
** but QString::number(0.9999*100.0, 'f' ,1) == 100.0 The problem manifests when
|
|
||||||
** the number has more digits after the decimal than we want AND the digit after
|
|
||||||
** our 'wanted' is >= 5. In this case our last digit gets rounded up. So for each
|
|
||||||
** precision we add an extra 0 behind 1 in the below algorithm. */
|
|
||||||
|
|
||||||
double prec = std::pow(10.0, precision);
|
|
||||||
return QLocale::system().toString(std::floor(n * prec) / prec, 'f', precision);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implements constant-time comparison to protect against timing attacks
|
|
||||||
// Taken from https://crackstation.net/hashing-security.htm
|
|
||||||
bool misc::slowEquals(const QByteArray &a, const QByteArray &b)
|
|
||||||
{
|
|
||||||
int lengthA = a.length();
|
|
||||||
int lengthB = b.length();
|
|
||||||
|
|
||||||
int diff = lengthA ^ lengthB;
|
|
||||||
for(int i = 0; i < lengthA && i < lengthB; i++)
|
|
||||||
diff |= a[i] ^ b[i];
|
|
||||||
|
|
||||||
return (diff == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void misc::loadBencodedFile(const QString &filename, std::vector<char> &buffer, libtorrent::lazy_entry &entry, libtorrent::error_code &ec)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
if (!file.open(QIODevice::ReadOnly)) return;
|
|
||||||
const qint64 content_size = file.bytesAvailable();
|
|
||||||
if (content_size <= 0) return;
|
|
||||||
buffer.resize(content_size);
|
|
||||||
file.read(&buffer[0], content_size);
|
|
||||||
// bdecode
|
|
||||||
lazy_bdecode(&buffer[0], &buffer[0] + buffer.size(), entry, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Trick to get a portable sleep() function
|
|
||||||
class SleeperThread: public QThread {
|
|
||||||
public:
|
|
||||||
static void msleep(unsigned long msecs)
|
|
||||||
{
|
{
|
||||||
QThread::msleep(msecs);
|
public:
|
||||||
}
|
static void msleep(unsigned long msecs)
|
||||||
};
|
{
|
||||||
|
QThread::msleep(msecs);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void misc::msleep(unsigned long msecs)
|
void Utils::Misc::msleep(unsigned long msecs)
|
||||||
{
|
{
|
||||||
SleeperThread::msleep(msecs);
|
SleeperThread::msleep(msecs);
|
||||||
}
|
}
|
||||||
81
src/core/utils/misc.h
Normal file
81
src/core/utils/misc.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILS_MISC_H
|
||||||
|
#define UTILS_MISC_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <ctime>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QUrl>
|
||||||
|
#include "core/types.h"
|
||||||
|
|
||||||
|
/* Miscellaneaous functions that can be useful */
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
|
{
|
||||||
|
namespace Misc
|
||||||
|
{
|
||||||
|
QString parseHtmlLinks(const QString &raw_text);
|
||||||
|
bool isUrl(const QString &s);
|
||||||
|
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
void shutdownComputer(ShutdownAction action);
|
||||||
|
// Get screen center
|
||||||
|
QPoint screenCenter(QWidget *win);
|
||||||
|
#endif
|
||||||
|
int pythonVersion();
|
||||||
|
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
|
||||||
|
// use Binary prefix standards from IEC 60027-2
|
||||||
|
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||||
|
// value must be given in bytes
|
||||||
|
QString friendlyUnit(qreal val, bool is_speed = false);
|
||||||
|
bool isPreviewable(const QString& extension);
|
||||||
|
|
||||||
|
QString bcLinkToMagnet(QString bc_link);
|
||||||
|
// Take a number of seconds and return an user-friendly
|
||||||
|
// time duration like "1d 2h 10m".
|
||||||
|
QString userFriendlyDuration(qlonglong seconds);
|
||||||
|
QString getUserIDString();
|
||||||
|
|
||||||
|
// Convert functions
|
||||||
|
QStringList toStringList(const QList<bool> &l);
|
||||||
|
QList<int> intListfromStringList(const QStringList &l);
|
||||||
|
QList<bool> boolListfromStringList(const QStringList &l);
|
||||||
|
|
||||||
|
void msleep(unsigned long msecs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
134
src/core/utils/string.cpp
Normal file
134
src/core/utils/string.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <cmath>
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
QString Utils::String::fromStdString(const std::string &str)
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::String::toStdString(const QString &str)
|
||||||
|
{
|
||||||
|
QByteArray utf8 = str.toUtf8();
|
||||||
|
return std::string(utf8.constData(), utf8.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// uses lessThan comparison
|
||||||
|
bool Utils::String::naturalSort(QString left, QString right, bool &result)
|
||||||
|
{
|
||||||
|
// Return value indicates if functions was successful
|
||||||
|
// result argument will contain actual comparison result if function was successful
|
||||||
|
int posL = 0;
|
||||||
|
int posR = 0;
|
||||||
|
do {
|
||||||
|
forever {
|
||||||
|
if (posL == left.size() || posR == right.size())
|
||||||
|
return false; // No data
|
||||||
|
|
||||||
|
QChar leftChar = left.at(posL);
|
||||||
|
QChar rightChar = right.at(posR);
|
||||||
|
bool leftCharIsDigit = leftChar.isDigit();
|
||||||
|
bool rightCharIsDigit = rightChar.isDigit();
|
||||||
|
if (leftCharIsDigit != rightCharIsDigit)
|
||||||
|
return false; // Digit positions mismatch
|
||||||
|
|
||||||
|
if (leftCharIsDigit)
|
||||||
|
break; // Both are digit, break this loop and compare numbers
|
||||||
|
|
||||||
|
if (leftChar != rightChar)
|
||||||
|
return false; // Strings' subsets before digit do not match
|
||||||
|
|
||||||
|
++posL;
|
||||||
|
++posR;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString temp;
|
||||||
|
while (posL < left.size()) {
|
||||||
|
if (left.at(posL).isDigit())
|
||||||
|
temp += left.at(posL);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
posL++;
|
||||||
|
}
|
||||||
|
int numL = temp.toInt();
|
||||||
|
temp.clear();
|
||||||
|
|
||||||
|
while (posR < right.size()) {
|
||||||
|
if (right.at(posR).isDigit())
|
||||||
|
temp += right.at(posR);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
posR++;
|
||||||
|
}
|
||||||
|
int numR = temp.toInt();
|
||||||
|
|
||||||
|
if (numL != numR) {
|
||||||
|
result = (numL < numR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings + digits do match and we haven't hit string end
|
||||||
|
// Do another round
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// to send numbers instead of strings with suffixes
|
||||||
|
QString Utils::String::fromDouble(double n, int precision)
|
||||||
|
{
|
||||||
|
/* HACK because QString rounds up. Eg QString::number(0.999*100.0, 'f' ,1) == 99.9
|
||||||
|
** but QString::number(0.9999*100.0, 'f' ,1) == 100.0 The problem manifests when
|
||||||
|
** the number has more digits after the decimal than we want AND the digit after
|
||||||
|
** our 'wanted' is >= 5. In this case our last digit gets rounded up. So for each
|
||||||
|
** precision we add an extra 0 behind 1 in the below algorithm. */
|
||||||
|
|
||||||
|
double prec = std::pow(10.0, precision);
|
||||||
|
return QLocale::system().toString(std::floor(n * prec) / prec, 'f', precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements constant-time comparison to protect against timing attacks
|
||||||
|
// Taken from https://crackstation.net/hashing-security.htm
|
||||||
|
bool Utils::String::slowEquals(const QByteArray &a, const QByteArray &b)
|
||||||
|
{
|
||||||
|
int lengthA = a.length();
|
||||||
|
int lengthB = b.length();
|
||||||
|
|
||||||
|
int diff = lengthA ^ lengthB;
|
||||||
|
for (int i = 0; (i < lengthA) && (i < lengthB); i++)
|
||||||
|
diff |= a[i] ^ b[i];
|
||||||
|
|
||||||
|
return (diff == 0);
|
||||||
|
}
|
||||||
53
src/core/utils/string.h
Normal file
53
src/core/utils/string.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
*
|
||||||
|
* 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 this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. 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), 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTILS_STRING_H
|
||||||
|
#define UTILS_STRING_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QByteArray;
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
|
{
|
||||||
|
namespace String
|
||||||
|
{
|
||||||
|
QString fromStdString(const std::string &str);
|
||||||
|
std::string toStdString(const QString &str);
|
||||||
|
bool naturalSort(QString left, QString right, bool &result);
|
||||||
|
QString fromDouble(double n, int precision);
|
||||||
|
|
||||||
|
// Implements constant-time comparison to protect against timing attacks
|
||||||
|
// Taken from https://crackstation.net/hashing-security.htm
|
||||||
|
bool slowEquals(const QByteArray &a, const QByteArray &b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UTILS_STRING_H
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2012 Christophe Dumez
|
* Copyright (C) 2012 Christophe Dumez
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -28,37 +28,37 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "addnewtorrentdialog.h"
|
|
||||||
#include "ui_addnewtorrentdialog.h"
|
|
||||||
#include "proplistdelegate.h"
|
|
||||||
#include "torrentcontentmodel.h"
|
|
||||||
#include "torrentcontentfiltermodel.h"
|
|
||||||
#include "preferences.h"
|
|
||||||
#include "torrentpersistentdata.h"
|
|
||||||
#include "qbtsession.h"
|
|
||||||
#include "iconprovider.h"
|
|
||||||
#include "fs_utils.h"
|
|
||||||
#include "autoexpandabledialog.h"
|
|
||||||
#include "messageboxraised.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
|
|
||||||
using namespace libtorrent;
|
#include "core/preferences.h"
|
||||||
|
#include "core/net/downloadmanager.h"
|
||||||
|
#include "core/net/downloadhandler.h"
|
||||||
|
#include "core/bittorrent/session.h"
|
||||||
|
#include "core/bittorrent/magneturi.h"
|
||||||
|
#include "core/bittorrent/torrentinfo.h"
|
||||||
|
#include "core/bittorrent/torrenthandle.h"
|
||||||
|
#include "core/utils/fs.h"
|
||||||
|
#include "core/utils/misc.h"
|
||||||
|
#include "guiiconprovider.h"
|
||||||
|
#include "autoexpandabledialog.h"
|
||||||
|
#include "messageboxraised.h"
|
||||||
|
#include "ui_addnewtorrentdialog.h"
|
||||||
|
#include "proplistdelegate.h"
|
||||||
|
#include "torrentcontentmodel.h"
|
||||||
|
#include "torrentcontentfiltermodel.h"
|
||||||
|
#include "addnewtorrentdialog.h"
|
||||||
|
|
||||||
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
|
AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, ui(new Ui::AddNewTorrentDialog)
|
, ui(new Ui::AddNewTorrentDialog)
|
||||||
, m_contentModel(0)
|
, m_contentModel(0)
|
||||||
, m_contentDelegate(0)
|
, m_contentDelegate(0)
|
||||||
, m_isMagnet(false)
|
|
||||||
, m_hasMetadata(false)
|
, m_hasMetadata(false)
|
||||||
, m_hasRenamedFile(false)
|
|
||||||
, m_oldIndex(0)
|
, m_oldIndex(0)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
@@ -68,7 +68,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
|
|||||||
|
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences* const pref = Preferences::instance();
|
||||||
ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause());
|
ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause());
|
||||||
ui->save_path_combo->addItem(fsutils::toNativePath(pref->getSavePath()), pref->getSavePath());
|
ui->save_path_combo->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath());
|
||||||
loadSavePathHistory();
|
loadSavePathHistory();
|
||||||
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
|
connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
|
||||||
connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked()));
|
connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked()));
|
||||||
@@ -123,22 +123,115 @@ void AddNewTorrentDialog::saveState()
|
|||||||
pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked());
|
pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::showTorrent(const QString &torrent_path, const QString& from_url, QWidget *parent)
|
void AddNewTorrentDialog::show(QString source, QWidget *parent)
|
||||||
{
|
{
|
||||||
|
if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||||
|
qDebug("Converting bc link to magnet link");
|
||||||
|
source = Utils::Misc::bcLinkToMagnet(source);
|
||||||
|
}
|
||||||
|
|
||||||
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
|
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
|
||||||
if (dlg->loadTorrent(torrent_path, from_url))
|
|
||||||
dlg->open();
|
if (Utils::Misc::isUrl(source)) {
|
||||||
else
|
// Launch downloader
|
||||||
delete dlg;
|
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, 10485760 /* 10MB */);
|
||||||
|
connect(handler, SIGNAL(downloadFinished(QString, QString)), dlg, SLOT(handleDownloadFinished(QString, QString)));
|
||||||
|
connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString)));
|
||||||
|
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool ok = false;
|
||||||
|
if (source.startsWith("magnet:", Qt::CaseInsensitive))
|
||||||
|
ok = dlg->loadMagnet(source);
|
||||||
|
else
|
||||||
|
ok = dlg->loadTorrent(source);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
dlg->open();
|
||||||
|
else
|
||||||
|
delete dlg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::showMagnet(const QString& link, QWidget *parent)
|
bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path)
|
||||||
{
|
{
|
||||||
AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent);
|
if (torrent_path.startsWith("file://", Qt::CaseInsensitive))
|
||||||
if (dlg->loadMagnet(link))
|
m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile();
|
||||||
dlg->open();
|
|
||||||
else
|
else
|
||||||
delete dlg;
|
m_filePath = torrent_path;
|
||||||
|
|
||||||
|
if (!QFile::exists(m_filePath)) {
|
||||||
|
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hasMetadata = true;
|
||||||
|
QString error;
|
||||||
|
m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(m_filePath, error);
|
||||||
|
if (!m_torrentInfo.isValid()) {
|
||||||
|
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hash = m_torrentInfo.hash();
|
||||||
|
|
||||||
|
// Prevent showing the dialog if download is already present
|
||||||
|
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
|
||||||
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash);
|
||||||
|
if (torrent) {
|
||||||
|
torrent->addTrackers(m_torrentInfo.trackers());
|
||||||
|
torrent->addUrlSeeds(m_torrentInfo.urlSeeds());
|
||||||
|
MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers were merged."), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding state."), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->lblhash->setText(m_hash);
|
||||||
|
setupTreeview();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri)
|
||||||
|
{
|
||||||
|
BitTorrent::MagnetUri magnet(magnet_uri);
|
||||||
|
if (!magnet.isValid()) {
|
||||||
|
MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hash = magnet.hash();
|
||||||
|
// Prevent showing the dialog if download is already present
|
||||||
|
if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) {
|
||||||
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash);
|
||||||
|
if (torrent) {
|
||||||
|
torrent->addTrackers(magnet.trackers());
|
||||||
|
torrent->addUrlSeeds(magnet.urlSeeds());
|
||||||
|
MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Trackers were merged."), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding."), QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(BitTorrent::Session::instance(), SIGNAL(metadataLoaded(BitTorrent::TorrentInfo)), SLOT(updateMetadata(BitTorrent::TorrentInfo)));
|
||||||
|
|
||||||
|
// Set dialog title
|
||||||
|
QString torrent_name = magnet.name();
|
||||||
|
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
|
||||||
|
|
||||||
|
setupTreeview();
|
||||||
|
// Set dialog position
|
||||||
|
setdialogPosition();
|
||||||
|
|
||||||
|
BitTorrent::Session::instance()->loadMetadata(magnet_uri);
|
||||||
|
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
|
||||||
|
ui->lblhash->setText(m_hash);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::showEvent(QShowEvent *event)
|
void AddNewTorrentDialog::showEvent(QShowEvent *event)
|
||||||
@@ -158,7 +251,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show)
|
|||||||
ui->adv_button->setText(QString::fromUtf8("▲"));
|
ui->adv_button->setText(QString::fromUtf8("▲"));
|
||||||
ui->settings_group->setVisible(true);
|
ui->settings_group->setVisible(true);
|
||||||
ui->info_group->setVisible(true);
|
ui->info_group->setVisible(true);
|
||||||
if (m_hasMetadata && (m_torrentInfo->num_files() > 1)) {
|
if (m_hasMetadata && (m_torrentInfo.filesCount() > 1)) {
|
||||||
ui->content_tree->setVisible(true);
|
ui->content_tree->setVisible(true);
|
||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
}
|
}
|
||||||
@@ -178,83 +271,6 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show)
|
|||||||
relayout();
|
relayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path, const QString& from_url)
|
|
||||||
{
|
|
||||||
m_isMagnet = false;
|
|
||||||
m_url = from_url;
|
|
||||||
if (torrent_path.startsWith("file://", Qt::CaseInsensitive))
|
|
||||||
m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile();
|
|
||||||
else
|
|
||||||
m_filePath = torrent_path;
|
|
||||||
|
|
||||||
if (!QFile::exists(m_filePath)) {
|
|
||||||
MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hasMetadata = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::vector<char> buffer;
|
|
||||||
lazy_entry entry;
|
|
||||||
libtorrent::error_code ec;
|
|
||||||
misc::loadBencodedFile(m_filePath, buffer, entry, ec);
|
|
||||||
m_torrentInfo = new torrent_info(entry);
|
|
||||||
m_hash = misc::toQString(m_torrentInfo->info_hash());
|
|
||||||
}
|
|
||||||
catch(const std::exception& e) {
|
|
||||||
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(misc::toQStringU(e.what())));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent showing the dialog if download is already present
|
|
||||||
if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) {
|
|
||||||
MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Merging trackers."), QMessageBox::Ok);
|
|
||||||
QBtSession::instance()->addTorrent(m_filePath, false, m_url);;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->lblhash->setText(m_hash);
|
|
||||||
setupTreeview();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri)
|
|
||||||
{
|
|
||||||
connect(QBtSession::instance(), SIGNAL(metadataReceivedHidden(const QTorrentHandle &)), SLOT(updateMetadata(const QTorrentHandle &)));
|
|
||||||
m_isMagnet = true;
|
|
||||||
m_url = magnet_uri;
|
|
||||||
m_hash = misc::magnetUriToHash(m_url);
|
|
||||||
if (m_hash.isEmpty()) {
|
|
||||||
MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent showing the dialog if download is already present
|
|
||||||
if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) {
|
|
||||||
MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Merging trackers."), QMessageBox::Ok);
|
|
||||||
QBtSession::instance()->addMagnetUri(m_url, false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set dialog title
|
|
||||||
QString torrent_name = misc::magnetUriToName(m_url);
|
|
||||||
setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name);
|
|
||||||
|
|
||||||
setupTreeview();
|
|
||||||
// Set dialog position
|
|
||||||
setdialogPosition();
|
|
||||||
|
|
||||||
// Override save path
|
|
||||||
TorrentTempData::setSavePath(m_hash, QString(QDir::tempPath() + "/" + m_hash));
|
|
||||||
HiddenData::addData(m_hash);
|
|
||||||
QBtSession::instance()->addMagnetUri(m_url, false);
|
|
||||||
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
|
|
||||||
ui->lblhash->setText(m_hash);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddNewTorrentDialog::saveSavePathHistory() const
|
void AddNewTorrentDialog::saveSavePathHistory() const
|
||||||
{
|
{
|
||||||
QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
|
QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
|
||||||
@@ -279,7 +295,7 @@ void AddNewTorrentDialog::saveSavePathHistory() const
|
|||||||
int AddNewTorrentDialog::indexOfSavePath(const QString &save_path)
|
int AddNewTorrentDialog::indexOfSavePath(const QString &save_path)
|
||||||
{
|
{
|
||||||
QDir saveDir(save_path);
|
QDir saveDir(save_path);
|
||||||
for(int i = 0; i<ui->save_path_combo->count() - 1; ++i)
|
for(int i = 0; i < ui->save_path_combo->count(); ++i)
|
||||||
if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir)
|
if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir)
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -287,9 +303,9 @@ int AddNewTorrentDialog::indexOfSavePath(const QString &save_path)
|
|||||||
|
|
||||||
void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename)
|
void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename)
|
||||||
{
|
{
|
||||||
for(int i = 0; i<ui->save_path_combo->count() - 1; ++i) {
|
for(int i = 0; i < ui->save_path_combo->count(); ++i) {
|
||||||
const QDir folder(ui->save_path_combo->itemData(i).toString());
|
const QDir folder(ui->save_path_combo->itemData(i).toString());
|
||||||
ui->save_path_combo->setItemText(i, fsutils::toNativePath(folder.absoluteFilePath(new_filename)));
|
ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(folder.absoluteFilePath(new_filename)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,20 +316,20 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
|
|||||||
|
|
||||||
if (m_hasMetadata) {
|
if (m_hasMetadata) {
|
||||||
if (m_contentModel) {
|
if (m_contentModel) {
|
||||||
const std::vector<int> priorities = m_contentModel->model()->getFilesPriorities();
|
const QVector<int> priorities = m_contentModel->model()->getFilePriorities();
|
||||||
Q_ASSERT(priorities.size() == (uint) m_torrentInfo->num_files());
|
Q_ASSERT(priorities.size() == m_torrentInfo.filesCount());
|
||||||
for (uint i = 0; i<priorities.size(); ++i)
|
for (int i = 0; i < priorities.size(); ++i)
|
||||||
if (priorities[i] > 0)
|
if (priorities[i] > 0)
|
||||||
torrent_size += m_torrentInfo->files().file_size(i);
|
torrent_size += m_torrentInfo.fileSize(i);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
torrent_size = m_torrentInfo->total_size();
|
torrent_size = m_torrentInfo.totalSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString size_string = torrent_size ? misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable."));
|
QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable."));
|
||||||
size_string += " (";
|
size_string += " (";
|
||||||
size_string += tr("Disk space: %1").arg(misc::friendlyUnit(fsutils::freeDiskSpaceOnPath(
|
size_string += tr("Disk space: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
|
||||||
ui->save_path_combo->itemData(
|
ui->save_path_combo->itemData(
|
||||||
ui->save_path_combo->currentIndex()).toString())));
|
ui->save_path_combo->currentIndex()).toString())));
|
||||||
size_string += ")";
|
size_string += ")";
|
||||||
@@ -338,11 +354,11 @@ void AddNewTorrentDialog::browseButton_clicked()
|
|||||||
QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex);
|
QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex);
|
||||||
QString new_path, old_filename, new_filename;
|
QString new_path, old_filename, new_filename;
|
||||||
|
|
||||||
if (m_torrentInfo && m_torrentInfo->num_files() == 1) {
|
if (m_torrentInfo.isValid() && (m_torrentInfo.filesCount() == 1)) {
|
||||||
old_filename = fsutils::fileName(cur_save_path);
|
old_filename = Utils::Fs::fileName(cur_save_path);
|
||||||
new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite);
|
new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite);
|
||||||
if (!new_path.isEmpty())
|
if (!new_path.isEmpty())
|
||||||
new_path = fsutils::branchPath(new_path, &new_filename);
|
new_path = Utils::Fs::branchPath(new_path, &new_filename);
|
||||||
qDebug() << "new_path: " << new_path;
|
qDebug() << "new_path: " << new_path;
|
||||||
qDebug() << "new_filename: " << new_filename;
|
qDebug() << "new_filename: " << new_filename;
|
||||||
}
|
}
|
||||||
@@ -360,15 +376,14 @@ void AddNewTorrentDialog::browseButton_clicked()
|
|||||||
else {
|
else {
|
||||||
// New path, prepend to combo box
|
// New path, prepend to combo box
|
||||||
if (!new_filename.isEmpty())
|
if (!new_filename.isEmpty())
|
||||||
ui->save_path_combo->insertItem(0, fsutils::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path);
|
ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path);
|
||||||
else
|
else
|
||||||
ui->save_path_combo->insertItem(0, fsutils::toNativePath(new_path), new_path);
|
ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(new_path), new_path);
|
||||||
ui->save_path_combo->setCurrentIndex(0);
|
ui->save_path_combo->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
// Update file name in all save_paths
|
// Update file name in all save_paths
|
||||||
if (!new_filename.isEmpty() && !fsutils::sameFileNames(old_filename, new_filename)) {
|
if (!new_filename.isEmpty() && !Utils::Fs::sameFileNames(old_filename, new_filename)) {
|
||||||
m_hasRenamedFile = true;
|
m_torrentInfo.renameFile(0, new_filename);
|
||||||
m_filesPath[0] = new_filename;
|
|
||||||
updateFileNameInSavePaths(new_filename);
|
updateFileNameInSavePaths(new_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +421,7 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
tr("New name:"), QLineEdit::Normal,
|
tr("New name:"), QLineEdit::Normal,
|
||||||
index.data().toString(), &ok).trimmed();
|
index.data().toString(), &ok).trimmed();
|
||||||
if (ok && !new_name_last.isEmpty()) {
|
if (ok && !new_name_last.isEmpty()) {
|
||||||
if (!fsutils::isValidFileSystemName(new_name_last)) {
|
if (!Utils::Fs::isValidFileSystemName(new_name_last)) {
|
||||||
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
||||||
tr("This file name contains forbidden characters, please choose a different one."),
|
tr("This file name contains forbidden characters, please choose a different one."),
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
@@ -415,22 +430,22 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
if (m_contentModel->itemType(index) == TorrentContentModelItem::FileType) {
|
if (m_contentModel->itemType(index) == TorrentContentModelItem::FileType) {
|
||||||
// File renaming
|
// File renaming
|
||||||
const int file_index = m_contentModel->getFileIndex(index);
|
const int file_index = m_contentModel->getFileIndex(index);
|
||||||
QString old_name = fsutils::fromNativePath(m_filesPath.at(file_index));
|
QString old_name = Utils::Fs::fromNativePath(m_torrentInfo.filePath(file_index));
|
||||||
qDebug("Old name: %s", qPrintable(old_name));
|
qDebug("Old name: %s", qPrintable(old_name));
|
||||||
QStringList path_items = old_name.split("/");
|
QStringList path_items = old_name.split("/");
|
||||||
path_items.removeLast();
|
path_items.removeLast();
|
||||||
path_items << new_name_last;
|
path_items << new_name_last;
|
||||||
QString new_name = path_items.join("/");
|
QString new_name = path_items.join("/");
|
||||||
if (fsutils::sameFileNames(old_name, new_name)) {
|
if (Utils::Fs::sameFileNames(old_name, new_name)) {
|
||||||
qDebug("Name did not change");
|
qDebug("Name did not change");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new_name = fsutils::expandPath(new_name);
|
new_name = Utils::Fs::expandPath(new_name);
|
||||||
qDebug("New name: %s", qPrintable(new_name));
|
qDebug("New name: %s", qPrintable(new_name));
|
||||||
// Check if that name is already used
|
// Check if that name is already used
|
||||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
|
||||||
if (i == file_index) continue;
|
if (i == file_index) continue;
|
||||||
if (fsutils::sameFileNames(m_filesPath.at(i), new_name)) {
|
if (Utils::Fs::sameFileNames(m_torrentInfo.filePath(i), new_name)) {
|
||||||
// Display error message
|
// Display error message
|
||||||
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
MessageBoxRaised::warning(this, tr("The file could not be renamed"),
|
||||||
tr("This name is already in use in this folder. Please use a different name."),
|
tr("This name is already in use in this folder. Please use a different name."),
|
||||||
@@ -439,9 +454,7 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
|
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name));
|
||||||
// Rename file in files_path
|
m_torrentInfo.renameFile(file_index, new_name);
|
||||||
m_filesPath.replace(file_index, new_name);
|
|
||||||
m_hasRenamedFile = true;
|
|
||||||
// Rename in torrent files model too
|
// Rename in torrent files model too
|
||||||
m_contentModel->setData(index, new_name_last);
|
m_contentModel->setData(index, new_name_last);
|
||||||
}
|
}
|
||||||
@@ -460,8 +473,8 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
QString new_path = path_items.join("/");
|
QString new_path = path_items.join("/");
|
||||||
if (!new_path.endsWith("/")) new_path += "/";
|
if (!new_path.endsWith("/")) new_path += "/";
|
||||||
// Check for overwriting
|
// Check for overwriting
|
||||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
|
||||||
const QString ¤t_name = m_filesPath.at(i);
|
const QString ¤t_name = m_torrentInfo.filePath(i);
|
||||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||||
if (current_name.startsWith(new_path, Qt::CaseSensitive)) {
|
if (current_name.startsWith(new_path, Qt::CaseSensitive)) {
|
||||||
#else
|
#else
|
||||||
@@ -474,18 +487,17 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Replace path in all files
|
// Replace path in all files
|
||||||
for (int i = 0; i<m_torrentInfo->num_files(); ++i) {
|
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
|
||||||
const QString ¤t_name = m_filesPath.at(i);
|
const QString ¤t_name = m_torrentInfo.filePath(i);
|
||||||
if (current_name.startsWith(old_path)) {
|
if (current_name.startsWith(old_path)) {
|
||||||
QString new_name = current_name;
|
QString new_name = current_name;
|
||||||
new_name.replace(0, old_path.length(), new_path);
|
new_name.replace(0, old_path.length(), new_path);
|
||||||
new_name = fsutils::expandPath(new_name);
|
new_name = Utils::Fs::expandPath(new_name);
|
||||||
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
|
qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name));
|
||||||
// Rename in files_path
|
m_torrentInfo.renameFile(i, new_name);
|
||||||
m_filesPath.replace(i, new_name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_hasRenamedFile = true;
|
|
||||||
// Rename folder in torrent files model too
|
// Rename folder in torrent files model too
|
||||||
m_contentModel->setData(index, new_name_last);
|
m_contentModel->setData(index, new_name_last);
|
||||||
}
|
}
|
||||||
@@ -495,7 +507,7 @@ void AddNewTorrentDialog::renameSelectedFile()
|
|||||||
void AddNewTorrentDialog::setdialogPosition()
|
void AddNewTorrentDialog::setdialogPosition()
|
||||||
{
|
{
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
QPoint center(misc::screenCenter(this));
|
QPoint center(Utils::Misc::screenCenter(this));
|
||||||
// Adjust y
|
// Adjust y
|
||||||
int y = Preferences::instance()->getAddNewTorrentDialogPos();
|
int y = Preferences::instance()->getAddNewTorrentDialogPos();
|
||||||
if (y >= 0) {
|
if (y >= 0) {
|
||||||
@@ -516,7 +528,7 @@ void AddNewTorrentDialog::loadSavePathHistory()
|
|||||||
QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory();
|
QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory();
|
||||||
foreach (const QString &sp, raw_path_history)
|
foreach (const QString &sp, raw_path_history)
|
||||||
if (QDir(sp) != default_save_path)
|
if (QDir(sp) != default_save_path)
|
||||||
ui->save_path_combo->addItem(fsutils::toNativePath(sp), sp);
|
ui->save_path_combo->addItem(Utils::Fs::toNativePath(sp), sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
|
void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
|
||||||
@@ -524,8 +536,8 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
|
|||||||
QMenu myFilesLlistMenu;
|
QMenu myFilesLlistMenu;
|
||||||
const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0);
|
const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0);
|
||||||
QAction *actRename = 0;
|
QAction *actRename = 0;
|
||||||
if (selectedRows.size() == 1 && m_torrentInfo->num_files() > 1) {
|
if ((selectedRows.size() == 1) && (m_torrentInfo.filesCount() > 1)) {
|
||||||
actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
|
actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename..."));
|
||||||
myFilesLlistMenu.addSeparator();
|
myFilesLlistMenu.addSeparator();
|
||||||
}
|
}
|
||||||
QMenu subMenu;
|
QMenu subMenu;
|
||||||
@@ -564,85 +576,78 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&)
|
|||||||
|
|
||||||
void AddNewTorrentDialog::accept()
|
void AddNewTorrentDialog::accept()
|
||||||
{
|
{
|
||||||
if (m_isMagnet)
|
if (!m_hasMetadata)
|
||||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo &)));
|
||||||
|
|
||||||
|
Preferences *const pref = Preferences::instance();
|
||||||
|
BitTorrent::AddTorrentParams params;
|
||||||
|
|
||||||
Preferences* const pref = Preferences::instance();
|
|
||||||
// Save Temporary data about torrent
|
|
||||||
QString save_path = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString();
|
|
||||||
TorrentTempData::setSavePath(m_hash, save_path);
|
|
||||||
if (ui->skip_check_cb->isChecked())
|
if (ui->skip_check_cb->isChecked())
|
||||||
// TODO: Check if destination actually exists
|
// TODO: Check if destination actually exists
|
||||||
TorrentTempData::setSeedingMode(m_hash, true);
|
params.skipChecking = true;
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
const QString label = ui->label_combo->currentText();
|
params.label = ui->label_combo->currentText();
|
||||||
if (!label.isEmpty())
|
|
||||||
TorrentTempData::setLabel(m_hash, label);
|
|
||||||
|
|
||||||
// Save file priorities
|
// Save file priorities
|
||||||
if (m_contentModel)
|
if (m_contentModel)
|
||||||
TorrentTempData::setFilesPriority(m_hash, m_contentModel->model()->getFilesPriorities());
|
params.filePriorities = m_contentModel->model()->getFilePriorities();
|
||||||
|
|
||||||
// Rename files if necessary
|
params.addPaused = !ui->start_torrent_cb->isChecked();
|
||||||
if (m_hasRenamedFile)
|
|
||||||
TorrentTempData::setFilesPath(m_hash, m_filesPath);
|
|
||||||
|
|
||||||
TorrentTempData::setAddPaused(m_hash, !ui->start_torrent_cb->isChecked());
|
|
||||||
|
|
||||||
// Add torrent
|
|
||||||
if (m_isMagnet)
|
|
||||||
QBtSession::instance()->unhideMagnet(m_hash);
|
|
||||||
else
|
|
||||||
QBtSession::instance()->addTorrent(m_filePath, false, m_url);
|
|
||||||
|
|
||||||
saveSavePathHistory();
|
saveSavePathHistory();
|
||||||
// Save settings
|
|
||||||
pref->useAdditionDialog(!ui->never_show_cb->isChecked());
|
pref->useAdditionDialog(!ui->never_show_cb->isChecked());
|
||||||
|
|
||||||
|
QString savePath = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString();
|
||||||
if (ui->default_save_path_cb->isChecked()) {
|
if (ui->default_save_path_cb->isChecked()) {
|
||||||
pref->setSavePath(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString());
|
pref->setSavePath(savePath);
|
||||||
QBtSession::instance()->setDefaultSavePath(pref->getSavePath());
|
pref->apply();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// if we don't use default save path...
|
||||||
|
if (QDir(savePath) != QDir(pref->getSavePath()))
|
||||||
|
params.savePath = savePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add torrent
|
||||||
|
if (!m_hasMetadata)
|
||||||
|
BitTorrent::Session::instance()->addTorrent(m_hash, params);
|
||||||
|
else
|
||||||
|
BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params);
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::reject()
|
void AddNewTorrentDialog::reject()
|
||||||
{
|
{
|
||||||
if (m_isMagnet) {
|
if (!m_hasMetadata) {
|
||||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo)));
|
||||||
setMetadataProgressIndicator(false);
|
setMetadataProgressIndicator(false);
|
||||||
QBtSession::instance()->deleteTorrent(m_hash, true);
|
BitTorrent::Session::instance()->cancelLoadMetadata(m_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::reject();
|
QDialog::reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::updateMetadata(const QTorrentHandle &h)
|
void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &info)
|
||||||
{
|
{
|
||||||
try {
|
if (info.hash() != m_hash) return;
|
||||||
if (h.hash() != m_hash)
|
|
||||||
return;
|
|
||||||
|
|
||||||
disconnect(this, SLOT(updateMetadata(const QTorrentHandle &)));
|
disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo)));
|
||||||
Q_ASSERT(h.has_metadata());
|
if (!info.isValid()) {
|
||||||
|
MessageBoxRaised::critical(0, tr("I/O Error"), ("Invalid metadata."));
|
||||||
#if LIBTORRENT_VERSION_NUM < 10000
|
setMetadataProgressIndicator(false, tr("Invalid metadata"));
|
||||||
m_torrentInfo = new torrent_info(h.get_torrent_info());
|
|
||||||
#else
|
|
||||||
m_torrentInfo = new torrent_info(*h.torrent_file());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Good to go
|
|
||||||
m_hasMetadata = true;
|
|
||||||
setMetadataProgressIndicator(true, tr("Parsing metadata..."));
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
setupTreeview();
|
|
||||||
setMetadataProgressIndicator(false, tr("Metadata retrieval complete"));
|
|
||||||
} catch (invalid_handle&) {
|
|
||||||
MessageBoxRaised::critical(0, tr("I/O Error"), ("Unknown error."));
|
|
||||||
setMetadataProgressIndicator(false, tr("Unknown error"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Good to go
|
||||||
|
m_torrentInfo = info;
|
||||||
|
m_hasMetadata = true;
|
||||||
|
setMetadataProgressIndicator(true, tr("Parsing metadata..."));
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
setupTreeview();
|
||||||
|
setMetadataProgressIndicator(false, tr("Metadata retrieval complete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText)
|
void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText)
|
||||||
@@ -661,21 +666,15 @@ void AddNewTorrentDialog::setupTreeview()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Set dialog title
|
// Set dialog title
|
||||||
setWindowTitle(misc::toQStringU(m_torrentInfo->name()));
|
setWindowTitle(m_torrentInfo.name());
|
||||||
|
|
||||||
// Set torrent information
|
// Set torrent information
|
||||||
QString comment = misc::toQString(m_torrentInfo->comment());
|
QString comment = m_torrentInfo.comment();
|
||||||
ui->comment_lbl->setText(comment.replace('\n', ' '));
|
ui->comment_lbl->setText(comment.replace('\n', ' '));
|
||||||
ui->date_lbl->setText(m_torrentInfo->creation_date() ? misc::toQString(*m_torrentInfo->creation_date()) : tr("Not available"));
|
ui->date_lbl->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleLongDate) : tr("Not available"));
|
||||||
|
|
||||||
file_storage const& fs = m_torrentInfo->files();
|
|
||||||
|
|
||||||
// Populate m_filesList
|
|
||||||
for (int i = 0; i < fs.num_files(); ++i)
|
|
||||||
m_filesPath << misc::toQStringU(fs.file_path(i));
|
|
||||||
|
|
||||||
// Prepare content tree
|
// Prepare content tree
|
||||||
if (fs.num_files() > 1) {
|
if (m_torrentInfo.filesCount() > 1) {
|
||||||
m_contentModel = new TorrentContentFilterModel(this);
|
m_contentModel = new TorrentContentFilterModel(this);
|
||||||
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
|
connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel()));
|
||||||
ui->content_tree->setModel(m_contentModel);
|
ui->content_tree->setModel(m_contentModel);
|
||||||
@@ -686,7 +685,7 @@ void AddNewTorrentDialog::setupTreeview()
|
|||||||
connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &)));
|
connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &)));
|
||||||
|
|
||||||
// List files in torrent
|
// List files in torrent
|
||||||
m_contentModel->model()->setupModelData(*m_torrentInfo);
|
m_contentModel->model()->setupModelData(m_torrentInfo);
|
||||||
if (!m_headerState.isEmpty())
|
if (!m_headerState.isEmpty())
|
||||||
ui->content_tree->header()->restoreState(m_headerState);
|
ui->content_tree->header()->restoreState(m_headerState);
|
||||||
|
|
||||||
@@ -695,9 +694,9 @@ void AddNewTorrentDialog::setupTreeview()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Update save paths (append file name to them)
|
// Update save paths (append file name to them)
|
||||||
QString single_file_relpath = misc::toQStringU(fs.file_path(0));
|
QString single_file_relpath = m_torrentInfo.filePath(0);
|
||||||
for (int i = 0; i<ui->save_path_combo->count() - 1; ++i)
|
for (int i = 0; i < ui->save_path_combo->count(); ++i)
|
||||||
ui->save_path_combo->setItemText(i, fsutils::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath)));
|
ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,3 +705,27 @@ void AddNewTorrentDialog::setupTreeview()
|
|||||||
// Set dialog position
|
// Set dialog position
|
||||||
setdialogPosition();
|
setdialogPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason)
|
||||||
|
{
|
||||||
|
MessageBoxRaised::critical(0, tr("Download Error"), QString("Cannot download %1: %2").arg(url).arg(reason));
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddNewTorrentDialog::handleRedirectedToMagnet(const QString &url, const QString &magnetUri)
|
||||||
|
{
|
||||||
|
Q_UNUSED(url)
|
||||||
|
if (loadMagnet(magnetUri))
|
||||||
|
open();
|
||||||
|
else
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QString &filePath)
|
||||||
|
{
|
||||||
|
Q_UNUSED(url)
|
||||||
|
if (loadTorrent(filePath))
|
||||||
|
open();
|
||||||
|
else
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include "core/bittorrent/infohash.h"
|
||||||
#include "qtorrenthandle.h"
|
#include "core/bittorrent/torrentinfo.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
@@ -54,8 +54,7 @@ class AddNewTorrentDialog: public QDialog
|
|||||||
public:
|
public:
|
||||||
~AddNewTorrentDialog();
|
~AddNewTorrentDialog();
|
||||||
|
|
||||||
static void showTorrent(const QString& torrent_path, const QString& from_url, QWidget *parent = 0);
|
static void show(QString source, QWidget *parent = 0);
|
||||||
static void showMagnet(const QString& torrent_link, QWidget *parent = 0);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event);
|
||||||
@@ -68,8 +67,11 @@ private slots:
|
|||||||
void relayout();
|
void relayout();
|
||||||
void renameSelectedFile();
|
void renameSelectedFile();
|
||||||
void setdialogPosition();
|
void setdialogPosition();
|
||||||
void updateMetadata(const QTorrentHandle& h);
|
void updateMetadata(const BitTorrent::TorrentInfo &info);
|
||||||
void browseButton_clicked();
|
void browseButton_clicked();
|
||||||
|
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||||
|
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||||
|
void handleDownloadFinished(const QString &url, const QString &filePath);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
virtual void accept();
|
virtual void accept();
|
||||||
@@ -77,7 +79,7 @@ protected slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
explicit AddNewTorrentDialog(QWidget *parent = 0);
|
explicit AddNewTorrentDialog(QWidget *parent = 0);
|
||||||
bool loadTorrent(const QString& torrent_path, const QString& from_url);
|
bool loadTorrent(const QString& torrent_path);
|
||||||
bool loadMagnet(const QString& magnet_uri);
|
bool loadMagnet(const QString& magnet_uri);
|
||||||
void loadSavePathHistory();
|
void loadSavePathHistory();
|
||||||
void saveSavePathHistory() const;
|
void saveSavePathHistory() const;
|
||||||
@@ -92,14 +94,10 @@ private:
|
|||||||
Ui::AddNewTorrentDialog *ui;
|
Ui::AddNewTorrentDialog *ui;
|
||||||
TorrentContentFilterModel *m_contentModel;
|
TorrentContentFilterModel *m_contentModel;
|
||||||
PropListDelegate *m_contentDelegate;
|
PropListDelegate *m_contentDelegate;
|
||||||
bool m_isMagnet;
|
|
||||||
bool m_hasMetadata;
|
bool m_hasMetadata;
|
||||||
QString m_filePath;
|
QString m_filePath;
|
||||||
QString m_url;
|
BitTorrent::InfoHash m_hash;
|
||||||
QString m_hash;
|
BitTorrent::TorrentInfo m_torrentInfo;
|
||||||
boost::intrusive_ptr<libtorrent::torrent_info> m_torrentInfo;
|
|
||||||
QStringList m_filesPath;
|
|
||||||
bool m_hasRenamedFile;
|
|
||||||
QShortcut *editHotkey;
|
QShortcut *editHotkey;
|
||||||
QByteArray m_headerState;
|
QByteArray m_headerState;
|
||||||
int m_oldIndex;
|
int m_oldIndex;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user