Merge pull request #4413 from glassez/dlmgr

Improve Download Manager. Closes #4305
This commit is contained in:
sledgehammer999 2016-01-05 10:10:08 -06:00
commit 52d08f7112
13 changed files with 319 additions and 248 deletions

View file

@ -462,10 +462,10 @@ void Application::cleanup()
#ifndef DISABLE_COUNTRIES_RESOLUTION #ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::freeInstance(); Net::GeoIPManager::freeInstance();
#endif #endif
Net::DownloadManager::freeInstance();
Preferences::freeInstance(); Preferences::freeInstance();
Logger::freeInstance(); Logger::freeInstance();
IconProvider::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);

View file

@ -45,6 +45,7 @@
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "base/qinisettings.h" #include "base/qinisettings.h"
#include "base/preferences.h"
bool userAcceptsUpgrade() bool userAcceptsUpgrade()
{ {
@ -114,6 +115,9 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent,
bool upgrade(bool ask = true) bool upgrade(bool ask = true)
{ {
// Move RSS cookies to common storage
Preferences::instance()->moveRSSCookies();
QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume"); QIniSettings *oldResumeSettings = new QIniSettings("qBittorrent", "qBittorrent-resume");
QString oldResumeFilename = oldResumeSettings->fileName(); QString oldResumeFilename = oldResumeSettings->fileName();
QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash(); QVariantHash oldResumeData = oldResumeSettings->value("torrents").toHash();

View file

@ -28,7 +28,6 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include <QNetworkAccessManager>
#include <QDebug> #include <QDebug>
#include <QRegExp> #include <QRegExp>
#include <QStringList> #include <QStringList>
@ -37,6 +36,8 @@
#endif #endif
#include "base/logger.h" #include "base/logger.h"
#include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "dnsupdater.h" #include "dnsupdater.h"
using namespace Net; using namespace Net;
@ -76,65 +77,62 @@ DNSUpdater::~DNSUpdater()
void DNSUpdater::checkPublicIP() void DNSUpdater::checkPublicIP()
{ {
Q_ASSERT(m_state == OK); Q_ASSERT(m_state == OK);
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipRequestFinished(QNetworkReply *))); DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
"http://checkip.dyndns.org", false, 0, false,
QString("qBittorrent/%1").arg(VERSION));
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipRequestFinished(QString, QByteArray)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipRequestFailed(QString, QString)));
m_lastIPCheckTime = QDateTime::currentDateTime(); m_lastIPCheckTime = QDateTime::currentDateTime();
QNetworkRequest request;
request.setUrl(QUrl("http://checkip.dyndns.org"));
request.setRawHeader("User-Agent", "qBittorrent/" VERSION);
manager->get(request);
} }
void DNSUpdater::ipRequestFinished(QNetworkReply *reply) void DNSUpdater::ipRequestFinished(const QString &url, const QByteArray &data)
{ {
qDebug() << Q_FUNC_INFO; Q_UNUSED(url);
if (reply->error()) {
// Error // Parse response
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
} if (ipregex.indexIn(data) >= 0) {
else { QString ipStr = ipregex.cap(1);
// Parse response qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>"); QHostAddress newIp(ipStr);
QString ret = reply->readAll(); if (!newIp.isNull()) {
if (ipregex.indexIn(ret) >= 0) { if (m_lastIP != newIp) {
QString ip_str = ipregex.cap(1); qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str; qDebug() << m_lastIP.toString() << "->" << newIp.toString();
QHostAddress new_ip(ip_str); m_lastIP = newIp;
if (!new_ip.isNull()) { updateDNSService();
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 { else {
qWarning() << Q_FUNC_INFO << "Regular expression failed to capture the IP address"; qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
} }
} }
// Clean up else {
reply->deleteLater(); qWarning() << Q_FUNC_INFO << "Regular expression failed to capture the IP address";
sender()->deleteLater(); }
}
void DNSUpdater::ipRequestFailed(const QString &url, const QString &error)
{
Q_UNUSED(url);
qWarning() << "IP request failed:" << error;
} }
void DNSUpdater::updateDNSService() void DNSUpdater::updateDNSService()
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
// Prepare request
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipUpdateFinished(QNetworkReply *)));
m_lastIPCheckTime = QDateTime::currentDateTime(); m_lastIPCheckTime = QDateTime::currentDateTime();
QNetworkRequest request; DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
request.setUrl(getUpdateUrl()); getUpdateUrl(), false, 0, false,
request.setRawHeader("User-Agent", "qBittorrent/" VERSION); QString("qBittorrent/%1").arg(VERSION));
manager->get(request); connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipUpdateFinished(QString, QByteArray)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipUpdateFailed(QString, QString)));
} }
QUrl DNSUpdater::getUpdateUrl() const QString DNSUpdater::getUpdateUrl() const
{ {
QUrl url; QUrl url;
#ifdef QT_NO_OPENSSL #ifdef QT_NO_OPENSSL
@ -172,22 +170,20 @@ QUrl DNSUpdater::getUpdateUrl() const
Q_ASSERT(url.isValid()); Q_ASSERT(url.isValid());
qDebug() << Q_FUNC_INFO << url.toString(); qDebug() << Q_FUNC_INFO << url.toString();
return url; return url.toString();
} }
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply) void DNSUpdater::ipUpdateFinished(const QString &url, const QByteArray &data)
{ {
if (reply->error()) { Q_UNUSED(url);
// Error // Parse reply
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); processIPUpdateReply(data);
} }
else {
// Parse reply void DNSUpdater::ipUpdateFailed(const QString &url, const QString &error)
processIPUpdateReply(reply->readAll()); {
} Q_UNUSED(url);
// Clean up qWarning() << "IP update failed:" << error;
reply->deleteLater();
sender()->deleteLater();
} }
void DNSUpdater::processIPUpdateReply(const QString &reply) void DNSUpdater::processIPUpdateReply(const QString &reply)
@ -196,16 +192,19 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
qDebug() << Q_FUNC_INFO << reply; qDebug() << Q_FUNC_INFO << reply;
QString code = reply.split(" ").first(); QString code = reply.split(" ").first();
qDebug() << Q_FUNC_INFO << "Code:" << code; qDebug() << Q_FUNC_INFO << "Code:" << code;
if (code == "good" || code == "nochg") {
if ((code == "good") || (code == "nochg")) {
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO); logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
return; return;
} }
if ((code == "911") || (code == "dnserr")) { 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); logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
m_lastIP.clear(); m_lastIP.clear();
// It will retry in 30 minutes because the timer was not stopped // It will retry in 30 minutes because the timer was not stopped
return; return;
} }
// Everything bellow is an error, stop updating until the user updates something // Everything bellow is an error, stop updating until the user updates something
m_ipCheckTimer.stop(); m_ipCheckTimer.stop();
m_lastIP.clear(); m_lastIP.clear();
@ -214,23 +213,27 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
m_state = INVALID_CREDS; m_state = INVALID_CREDS;
return; return;
} }
if (code == "badauth") { if (code == "badauth") {
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL); logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
m_state = INVALID_CREDS; m_state = INVALID_CREDS;
return; return;
} }
if (code == "badagent") { if (code == "badagent") {
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."), logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
Log::CRITICAL); Log::CRITICAL);
m_state = FATAL; m_state = FATAL;
return; return;
} }
if (code == "!donator") { 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"), 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); Log::CRITICAL);
m_state = FATAL; m_state = FATAL;
return; return;
} }
if (code == "abuse") { if (code == "abuse") {
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL); logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
m_state = FATAL; m_state = FATAL;

View file

@ -33,15 +33,15 @@
#include <QObject> #include <QObject>
#include <QHostAddress> #include <QHostAddress>
#include <QNetworkReply>
#include <QDateTime> #include <QDateTime>
#include <QTimer> #include <QTimer>
#include "base/preferences.h" #include "base/preferences.h"
namespace Net 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
@ -56,15 +56,25 @@ namespace Net
private slots: private slots:
void checkPublicIP(); void checkPublicIP();
void ipRequestFinished(QNetworkReply *reply); void ipRequestFinished(const QString &url, const QByteArray &data);
void ipRequestFailed(const QString &url, const QString &error);
void updateDNSService(); void updateDNSService();
void ipUpdateFinished(QNetworkReply *reply); void ipUpdateFinished(const QString &url, const QByteArray &data);
void ipUpdateFailed(const QString &url, const QString &error);
private: private:
QUrl getUpdateUrl() const; enum State
{
OK,
INVALID_CREDS,
FATAL
};
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
QString getUpdateUrl() const;
void processIPUpdateReply(const QString &reply); void processIPUpdateReply(const QString &reply);
private:
QHostAddress m_lastIP; QHostAddress m_lastIP;
QDateTime m_lastIPCheckTime; QDateTime m_lastIPCheckTime;
QTimer m_ipCheckTimer; QTimer m_ipCheckTimer;
@ -74,16 +84,6 @@ namespace Net
QString m_domain; QString m_domain;
QString m_username; QString m_username;
QString m_password; QString m_password;
private:
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
enum State
{
OK,
INVALID_CREDS,
FATAL
};
}; };
} }

View file

@ -27,11 +27,13 @@
* exception statement from your version. * exception statement from your version.
*/ */
#include <QDateTime>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QNetworkCookieJar> #include <QNetworkCookieJar>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkCookie> #include <QNetworkCookie>
#include <QNetworkCookieJar>
#include <QSslError> #include <QSslError>
#include <QUrl> #include <QUrl>
#include <QDebug> #include <QDebug>
@ -40,6 +42,74 @@
#include "downloadhandler.h" #include "downloadhandler.h"
#include "downloadmanager.h" #include "downloadmanager.h"
// Spoof Firefox 38 user agent to avoid web server banning
const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0";
namespace
{
class NetworkCookieJar: public QNetworkCookieJar
{
public:
explicit NetworkCookieJar(QObject *parent = 0)
: QNetworkCookieJar(parent)
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = Preferences::instance()->getNetworkCookies();
foreach (const QNetworkCookie &cookie, Preferences::instance()->getNetworkCookies()) {
if (cookie.isSessionCookie() || (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
setAllCookies(cookies);
}
~NetworkCookieJar()
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = allCookies();
foreach (const QNetworkCookie &cookie, allCookies()) {
if (cookie.isSessionCookie() || (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
Preferences::instance()->setNetworkCookies(cookies);
}
#ifndef QBT_USES_QT5
virtual bool deleteCookie(const QNetworkCookie &cookie)
{
auto myCookies = allCookies();
myCookies.removeAll(cookie);
setAllCookies(myCookies);
}
#endif
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const override
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = QNetworkCookieJar::cookiesForUrl(url);
foreach (const QNetworkCookie &cookie, QNetworkCookieJar::cookiesForUrl(url)) {
if (!cookie.isSessionCookie() && (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
return cookies;
}
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url) override
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = cookieList;
foreach (const QNetworkCookie &cookie, cookieList) {
if (!cookie.isSessionCookie() && (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
}
};
}
using namespace Net; using namespace Net;
DownloadManager *DownloadManager::m_instance = 0; DownloadManager *DownloadManager::m_instance = 0;
@ -50,10 +120,7 @@ DownloadManager::DownloadManager(QObject *parent)
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>))); connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
#endif #endif
} m_networkManager.setCookieJar(new NetworkCookieJar(this));
DownloadManager::~DownloadManager()
{
} }
void DownloadManager::initInstance() void DownloadManager::initInstance()
@ -75,7 +142,7 @@ DownloadManager *DownloadManager::instance()
return m_instance; return m_instance;
} }
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet) DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet, const QString &userAgent)
{ {
// Update proxy settings // Update proxy settings
applyProxySettings(); applyProxySettings();
@ -85,29 +152,36 @@ DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFil
const QUrl qurl = QUrl::fromEncoded(url.toUtf8()); const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
QNetworkRequest request(qurl); QNetworkRequest request(qurl);
// Spoof Firefox 38 user agent to avoid web server banning if (userAgent.isEmpty())
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0"); request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
else
request.setRawHeader("User-Agent", userAgent.toUtf8());
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents // Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request.setRawHeader("Referer", request.url().toEncoded().data()); request.setRawHeader("Referer", request.url().toEncoded().data());
qDebug("Downloading %s...", request.url().toEncoded().data()); qDebug("Downloading %s...", request.url().toEncoded().data());
qDebug() << "Cookies:" << m_networkManager.cookieJar()->cookiesForUrl(request.url());
// accept gzip // accept gzip
request.setRawHeader("Accept-Encoding", "gzip"); request.setRawHeader("Accept-Encoding", "gzip");
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet); return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
} }
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QString &url) const QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QUrl &url) const
{ {
return m_networkManager.cookieJar()->cookiesForUrl(url); return m_networkManager.cookieJar()->cookiesForUrl(url);
} }
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &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); return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
} }
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
}
void DownloadManager::applyProxySettings() void DownloadManager::applyProxySettings()
{ {
QNetworkProxy proxy; QNetworkProxy proxy;

View file

@ -33,12 +33,10 @@
#include <QObject> #include <QObject>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
QT_BEGIN_NAMESPACE
class QNetworkReply; class QNetworkReply;
class QNetworkCookie; class QNetworkCookie;
class QSslError; class QSslError;
class QUrl; class QUrl;
QT_END_NAMESPACE
namespace Net namespace Net
{ {
@ -53,9 +51,10 @@ namespace Net
static void freeInstance(); static void freeInstance();
static DownloadManager *instance(); static DownloadManager *instance();
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false); DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
QList<QNetworkCookie> cookiesForUrl(const QString &url) const; QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url); bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
bool deleteCookie(const QNetworkCookie &cookie);
private slots: private slots:
#ifndef QT_NO_OPENSSL #ifndef QT_NO_OPENSSL
@ -63,8 +62,7 @@ namespace Net
#endif #endif
private: private:
DownloadManager(QObject *parent = 0); explicit DownloadManager(QObject *parent = 0);
~DownloadManager();
void applyProxySettings(); void applyProxySettings();

View file

@ -996,12 +996,12 @@ void Preferences::setFilteringEnabled(bool enabled)
bool Preferences::isFilteringTrackerEnabled() const bool Preferences::isFilteringTrackerEnabled() const
{ {
return value("Preferences/IPFilter/FilterTracker", false).toBool(); return value("Preferences/IPFilter/FilterTracker", false).toBool();
} }
void Preferences::setFilteringTrackerEnabled(bool enabled) void Preferences::setFilteringTrackerEnabled(bool enabled)
{ {
setValue("Preferences/IPFilter/FilterTracker", enabled); setValue("Preferences/IPFilter/FilterTracker", enabled);
} }
QString Preferences::getFilter() const QString Preferences::getFilter() const
@ -2494,45 +2494,57 @@ void Preferences::setToolbarTextPosition(const int position)
setValue("Toolbar/textPosition", position); setValue("Toolbar/textPosition", position);
} }
QList<QByteArray> Preferences::getHostNameCookies(const QString &host_name) const void Preferences::moveRSSCookies()
{ {
QMap<QString, QVariant> hosts_table = value("Rss/hosts_cookies").toMap(); QList<QNetworkCookie> cookies = getNetworkCookies();
if (!hosts_table.contains(host_name)) return QList<QByteArray>(); QVariantMap hostsTable = value("Rss/hosts_cookies").toMap();
QByteArray raw_cookies = hosts_table.value(host_name).toByteArray(); foreach (const QString &key, hostsTable.keys()) {
return raw_cookies.split(':'); QVariant value = hostsTable[key];
} QList<QByteArray> rawCookies = value.toByteArray().split(':');
foreach (const QByteArray &rawCookie, rawCookies) {
QList<QNetworkCookie> Preferences::getHostNameQNetworkCookies(const QString& host_name) const foreach (QNetworkCookie cookie, QNetworkCookie::parseCookies(rawCookie)) {
{ cookie.setDomain(key);
QList<QNetworkCookie> cookies; cookie.setPath("/");
const QList<QByteArray> raw_cookies = getHostNameCookies(host_name); cookie.setExpirationDate(QDateTime::currentDateTime().addYears(10));
foreach (const QByteArray& raw_cookie, raw_cookies) { cookies << cookie;
QList<QByteArray> cookie_parts = raw_cookie.split('='); }
if (cookie_parts.size() == 2) {
qDebug("Loading cookie: %s = %s", cookie_parts.first().constData(), cookie_parts.last().constData());
cookies << QNetworkCookie(cookie_parts.first(), cookie_parts.last());
} }
} }
setNetworkCookies(cookies);
QWriteLocker locker(&lock);
dirty = true;
timer.start();
m_data.remove("Rss/hosts_cookies");
}
QList<QNetworkCookie> Preferences::getNetworkCookies() const
{
QList<QNetworkCookie> cookies;
QStringList rawCookies = value("Network/Cookies").toStringList();
foreach (const QString &rawCookie, rawCookies)
cookies << QNetworkCookie::parseCookies(rawCookie.toUtf8());
return cookies; return cookies;
} }
void Preferences::setHostNameCookies(const QString &host_name, const QList<QByteArray> &cookies) void Preferences::setNetworkCookies(const QList<QNetworkCookie> &cookies)
{ {
QMap<QString, QVariant> hosts_table = value("Rss/hosts_cookies").toMap(); QStringList rawCookies;
QByteArray raw_cookies = ""; foreach (const QNetworkCookie &cookie, cookies)
foreach (const QByteArray& cookie, cookies) rawCookies << cookie.toRawForm();
raw_cookies += cookie + ":";
if (raw_cookies.endsWith(":")) setValue("Network/Cookies", rawCookies);
raw_cookies.chop(1);
hosts_table.insert(host_name, raw_cookies);
setValue("Rss/hosts_cookies", hosts_table);
} }
int Preferences::getSpeedWidgetPeriod() const { int Preferences::getSpeedWidgetPeriod() const
{
return value("SpeedWidget/period", 1).toInt(); return value("SpeedWidget/period", 1).toInt();
} }
void Preferences::setSpeedWidgetPeriod(const int period) { void Preferences::setSpeedWidgetPeriod(const int period)
{
setValue("SpeedWidget/period", period); setValue("SpeedWidget/period", period);
} }

View file

@ -534,9 +534,12 @@ public:
void setRssFeedsUrls(const QStringList &rssFeeds); void setRssFeedsUrls(const QStringList &rssFeeds);
QStringList getRssFeedsAliases() const; QStringList getRssFeedsAliases() const;
void setRssFeedsAliases(const QStringList &rssAliases); void setRssFeedsAliases(const QStringList &rssAliases);
QList<QByteArray> getHostNameCookies(const QString &host_name) const;
QList<QNetworkCookie> getHostNameQNetworkCookies(const QString& host_name) const; // Network
void setHostNameCookies(const QString &host_name, const QList<QByteArray> &cookies); QList<QNetworkCookie> getNetworkCookies() const;
void setNetworkCookies(const QList<QNetworkCookie> &cookies);
// Temporary method for upgrade purposes
void moveRSSCookies();
// SpeedWidget // SpeedWidget
int getSpeedWidgetPeriod() const; int getSpeedWidgetPeriod() const;

View file

@ -28,23 +28,20 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QNetworkProxy>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDebug> #include <QDebug>
#include <QRegExp> #include <QRegExp>
#include <QStringList> #include <QStringList>
#include "programupdater.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/preferences.h" #include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h"
#include "programupdater.h"
namespace namespace
{ {
const QUrl RSS_URL("http://www.fosshub.com/software/feedqBittorent"); const QString RSS_URL("http://www.fosshub.com/software/feedqBittorent");
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
const QString OS_TYPE("Mac OS X"); const QString OS_TYPE("Mac OS X");
@ -59,99 +56,73 @@ ProgramUpdater::ProgramUpdater(QObject *parent, bool invokedByUser)
: QObject(parent) : QObject(parent)
, m_invokedByUser(invokedByUser) , m_invokedByUser(invokedByUser)
{ {
m_networkManager = new QNetworkAccessManager(this);
Preferences* const pref = Preferences::instance();
// Proxy support
if (pref->isProxyEnabled()) {
QNetworkProxy proxy;
switch(pref->getProxyType()) {
case Proxy::SOCKS4:
case Proxy::SOCKS5:
case Proxy::SOCKS5_PW:
proxy.setType(QNetworkProxy::Socks5Proxy);
default:
proxy.setType(QNetworkProxy::HttpProxy);
break;
}
proxy.setHostName(pref->getProxyIp());
proxy.setPort(pref->getProxyPort());
// Proxy authentication
if (pref->isProxyAuthEnabled()) {
proxy.setUser(pref->getProxyUsername());
proxy.setPassword(pref->getProxyPassword());
}
m_networkManager->setProxy(proxy);
}
}
ProgramUpdater::~ProgramUpdater()
{
delete m_networkManager;
} }
void ProgramUpdater::checkForUpdates() void ProgramUpdater::checkForUpdates()
{ {
// SIGNAL/SLOT Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), RSS_URL, false, 0, false,
this, SLOT(rssDownloadFinished(QNetworkReply*))); // Don't change this User-Agent. In case our updater goes haywire,
// Send the request // the filehost can identify it and contact us.
QNetworkRequest request(RSS_URL); QString("qBittorrent/%1 ProgramUpdater (www.qbittorrent.org)").arg(VERSION));
// Don't change this User-Agent. In case our updater goes haywire, the filehost can indetify it and contact us. connect(handler, SIGNAL(downloadFinished(QString,QByteArray)), SLOT(rssDownloadFinished(QString,QByteArray)));
request.setRawHeader("User-Agent", QString("qBittorrent/%1 ProgramUpdater (www.qbittorrent.org)").arg(VERSION).toLocal8Bit()); connect(handler, SIGNAL(downloadFailed(QString,QString)), SLOT(rssDownloadFailed(QString,QString)));
m_networkManager->get(request);
} }
void ProgramUpdater::rssDownloadFinished(QNetworkReply *reply) void ProgramUpdater::rssDownloadFinished(const QString &url, const QByteArray &data)
{ {
// Disconnect SIGNAL/SLOT Q_UNUSED(url);
disconnect(m_networkManager, 0, this, 0);
qDebug("Finished downloading the new qBittorrent updates RSS"); qDebug("Finished downloading the new qBittorrent updates RSS");
QString version; QString version;
if (!reply->error()) { QXmlStreamReader xml(data);
qDebug("No download error, good."); bool inItem = false;
QXmlStreamReader xml(reply); QString updateLink;
bool inItem = false; QString type;
QString updateLink;
QString type;
while (!xml.atEnd()) { while (!xml.atEnd()) {
xml.readNext(); xml.readNext();
if (xml.isStartElement()) { if (xml.isStartElement()) {
if (xml.name() == "item") if (xml.name() == "item")
inItem = true; inItem = true;
else if (inItem && xml.name() == "link") else if (inItem && xml.name() == "link")
updateLink = getStringValue(xml); updateLink = getStringValue(xml);
else if (inItem && xml.name() == "type") else if (inItem && xml.name() == "type")
type = getStringValue(xml); type = getStringValue(xml);
else if (inItem && xml.name() == "version") else if (inItem && xml.name() == "version")
version = getStringValue(xml); version = getStringValue(xml);
} }
else if (xml.isEndElement()) { else if (xml.isEndElement()) {
if (inItem && xml.name() == "item") { if (inItem && xml.name() == "item") {
if (type.compare(OS_TYPE, Qt::CaseInsensitive) == 0) { if (type.compare(OS_TYPE, Qt::CaseInsensitive) == 0) {
qDebug("The last update available is %s", qPrintable(version)); qDebug("The last update available is %s", qPrintable(version));
if (!version.isEmpty()) { if (!version.isEmpty()) {
qDebug("Detected version is %s", qPrintable(version)); qDebug("Detected version is %s", qPrintable(version));
if (isVersionMoreRecent(version)) if (isVersionMoreRecent(version))
m_updateUrl = updateLink; m_updateUrl = updateLink;
}
break;
} }
break;
inItem = false;
updateLink.clear();
type.clear();
version.clear();
} }
inItem = false;
updateLink.clear();
type.clear();
version.clear();
} }
} }
} }
emit updateCheckFinished(!m_updateUrl.isEmpty(), version, m_invokedByUser); emit updateCheckFinished(!m_updateUrl.isEmpty(), version, m_invokedByUser);
// Clean up }
reply->deleteLater();
void ProgramUpdater::rssDownloadFailed(const QString &url, const QString &error)
{
Q_UNUSED(url);
qDebug() << "Downloading the new qBittorrent updates RSS failed:" << error;
emit updateCheckFinished(false, QString(), m_invokedByUser);
} }
void ProgramUpdater::updateProgram() void ProgramUpdater::updateProgram()

View file

@ -34,30 +34,27 @@
#include <QObject> #include <QObject>
#include <QUrl> #include <QUrl>
class QNetworkReply;
class QNetworkAccessManager;
class ProgramUpdater: public QObject class ProgramUpdater: public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ProgramUpdater(QObject *parent = 0, bool invokedByUser = false); explicit ProgramUpdater(QObject *parent = 0, bool invokedByUser = false);
~ProgramUpdater();
void checkForUpdates(); void checkForUpdates();
void updateProgram(); void updateProgram();
protected:
bool isVersionMoreRecent(const QString &remoteVersion) const;
protected slots:
void rssDownloadFinished(QNetworkReply* reply);
signals: signals:
void updateCheckFinished(bool updateAvailable, QString version, bool invokedByUser); void updateCheckFinished(bool updateAvailable, QString version, bool invokedByUser);
private slots:
void rssDownloadFinished(const QString &url, const QByteArray &data);
void rssDownloadFailed(const QString &url, const QString &error);
private: private:
bool isVersionMoreRecent(const QString &remoteVersion) const;
QString m_updateUrl; QString m_updateUrl;
QNetworkAccessManager *m_networkManager;
bool m_invokedByUser; bool m_invokedByUser;
}; };

View file

@ -31,12 +31,14 @@
#include "cookiesdlg.h" #include "cookiesdlg.h"
#include "ui_cookiesdlg.h" #include "ui_cookiesdlg.h"
#include "guiiconprovider.h" #include "guiiconprovider.h"
#include "base/net/downloadmanager.h"
#include <QNetworkCookie> #include <QNetworkCookie>
#include <QDateTime>
enum CookiesCols { COOKIE_KEY, COOKIE_VALUE}; enum CookiesCols { COOKIE_KEY, COOKIE_VALUE};
CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) : CookiesDlg::CookiesDlg(const QUrl &url, QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::CookiesDlg) ui(new Ui::CookiesDlg)
{ {
@ -46,13 +48,13 @@ CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) :
ui->del_btn->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); ui->del_btn->setIcon(GuiIconProvider::instance()->getIcon("list-remove"));
ui->infos_lbl->setText(tr("Common keys for cookies are: '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass")); ui->infos_lbl->setText(tr("Common keys for cookies are: '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass"));
foreach (const QByteArray &raw_cookie, raw_cookies) {
QList<QByteArray> cookie_parts = raw_cookie.split('='); QList<QNetworkCookie> cookies = Net::DownloadManager::instance()->cookiesForUrl(url);
if (cookie_parts.size() != 2) continue; foreach (const QNetworkCookie &cookie, cookies) {
const int i = ui->cookiesTable->rowCount(); const int i = ui->cookiesTable->rowCount();
ui->cookiesTable->setRowCount(i+1); ui->cookiesTable->setRowCount(i+1);
ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(cookie_parts.first().data())); ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(QString(cookie.name())));
ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(cookie_parts.last().data())); ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(QString(cookie.value())));
} }
} }
@ -75,8 +77,9 @@ void CookiesDlg::on_del_btn_clicked() {
} }
} }
QList<QByteArray> CookiesDlg::getCookies() const { QList<QNetworkCookie> CookiesDlg::getCookies() const {
QList<QByteArray> ret; QList<QNetworkCookie> ret;
auto now = QDateTime::currentDateTime();
for (int i=0; i<ui->cookiesTable->rowCount(); ++i) { for (int i=0; i<ui->cookiesTable->rowCount(); ++i) {
QString key; QString key;
if (ui->cookiesTable->item(i, COOKIE_KEY)) if (ui->cookiesTable->item(i, COOKIE_KEY))
@ -85,20 +88,23 @@ QList<QByteArray> CookiesDlg::getCookies() const {
if (ui->cookiesTable->item(i, COOKIE_VALUE)) if (ui->cookiesTable->item(i, COOKIE_VALUE))
value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed(); value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed();
if (!key.isEmpty() && !value.isEmpty()) { if (!key.isEmpty() && !value.isEmpty()) {
const QString raw_cookie = key+"="+value; QNetworkCookie cookie(key.toUtf8(), value.toUtf8());
qDebug("Cookie: %s", qPrintable(raw_cookie)); // TODO: Delete this hack when advanced Cookie dialog will be implemented.
ret << raw_cookie.toLocal8Bit(); cookie.setExpirationDate(now.addYears(10));
qDebug("Cookie: %s", cookie.toRawForm().data());
ret << cookie;
} }
} }
return ret; return ret;
} }
QList<QByteArray> CookiesDlg::askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok) { bool CookiesDlg::askForCookies(QWidget *parent, const QUrl &url, QList<QNetworkCookie> &out)
CookiesDlg dlg(parent, raw_cookies); {
CookiesDlg dlg(url, parent);
if (dlg.exec()) { if (dlg.exec()) {
*ok = true; out = dlg.getCookies();
return dlg.getCookies(); return true;
} }
*ok = false;
return QList<QByteArray>(); return false;
} }

View file

@ -32,22 +32,24 @@
#define COOKIESDLG_H #define COOKIESDLG_H
#include <QDialog> #include <QDialog>
#include <QList>
class QNetworkCookie;
class QUrl;
QT_BEGIN_NAMESPACE
namespace Ui { namespace Ui {
class CookiesDlg; class CookiesDlg;
} }
QT_END_NAMESPACE
class CookiesDlg : public QDialog class CookiesDlg : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CookiesDlg(QWidget *parent = 0, const QList<QByteArray> &raw_cookies = QList<QByteArray>()); explicit CookiesDlg(const QUrl &url, QWidget *parent = 0);
~CookiesDlg(); ~CookiesDlg();
QList<QByteArray> getCookies() const; QList<QNetworkCookie> getCookies() const;
static QList<QByteArray> askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok); static bool askForCookies(QWidget *parent, const QUrl &url, QList<QNetworkCookie> &out);
protected slots: protected slots:
void on_add_btn_clicked(); void on_add_btn_clicked();

View file

@ -142,18 +142,19 @@ void RSSImp::displayItemsListMenu(const QPoint&)
void RSSImp::on_actionManage_cookies_triggered() void RSSImp::on_actionManage_cookies_triggered()
{ {
Q_ASSERT(!m_feedList->selectedItems().empty()); Q_ASSERT(!m_feedList->selectedItems().empty());
// Get feed hostname
QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first()); // TODO: Create advanced application wide Cookie dialog and use it everywhere.
QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host(); QUrl feedUrl = QUrl::fromEncoded(m_feedList->getItemID(m_feedList->selectedItems().first()).toUtf8());
qDebug("RSS Feed hostname is: %s", qPrintable(feed_hostname)); QList<QNetworkCookie> cookies;
Q_ASSERT(!feed_hostname.isEmpty()); if (CookiesDlg::askForCookies(this, feedUrl, cookies)) {
bool ok = false; auto downloadManager = Net::DownloadManager::instance();
Preferences* const pref = Preferences::instance(); QList<QNetworkCookie> oldCookies = downloadManager->cookiesForUrl(feedUrl);
QList<QByteArray> raw_cookies = CookiesDlg::askForCookies(this, pref->getHostNameCookies(feed_hostname), &ok); foreach (const QNetworkCookie &oldCookie, oldCookies) {
if (ok) { if (!cookies.contains(oldCookie))
qDebug() << "Settings cookies for host name: " << feed_hostname; downloadManager->deleteCookie(oldCookie);
pref->setHostNameCookies(feed_hostname, raw_cookies); }
Net::DownloadManager::instance()->setCookiesFromUrl(pref->getHostNameQNetworkCookies(feed_hostname), feed_hostname);
downloadManager->setCookiesFromUrl(cookies, feedUrl);
} }
} }