Rename Core to Base (Closes #3733).

This commit is contained in:
Vladimir Golovnev (Glassez)
2015-09-25 11:10:05 +03:00
committed by sledgehammer999
parent a280467270
commit 4ed4ebcdb7
156 changed files with 303 additions and 302 deletions

86
src/base/base.pri Normal file
View File

@@ -0,0 +1,86 @@
HEADERS += \
$$PWD/types.h \
$$PWD/tristatebool.h \
$$PWD/filesystemwatcher.h \
$$PWD/qinisettings.h \
$$PWD/logger.h \
$$PWD/preferences.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/connection.h \
$$PWD/http/requestparser.h \
$$PWD/http/responsegenerator.h \
$$PWD/http/server.h \
$$PWD/http/types.h \
$$PWD/http/responsebuilder.h \
$$PWD/net/dnsupdater.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/downloadhandler.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.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/speedmonitor.h \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/misc.h \
$$PWD/utils/string.h \
$$PWD/unicodestrings.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h
SOURCES += \
$$PWD/tristatebool.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/logger.cpp \
$$PWD/preferences.cpp \
$$PWD/iconprovider.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/requestparser.cpp \
$$PWD/http/responsegenerator.cpp \
$$PWD/http/server.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/net/dnsupdater.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.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/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/string.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp

View 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;
}

View 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

View 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);
}

View File

@@ -0,0 +1,61 @@
/*
* 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/sha1_hash.hpp>
#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

View 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 <libtorrent/bencode.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/magnet_uri.hpp>
#include "base/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));
foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds)
m_urlSeeds.append(QUrl(urlSeed.c_str()));
}
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;
}

View 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

View File

@@ -0,0 +1,255 @@
/*
* 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 "base/net/geoipmanager.h"
#include "base/utils/string.h"
#include "base/unicodestrings.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);
}
#ifndef DISABLE_COUNTRIES_RESOLUTION
QString PeerInfo::country() const
{
return Net::GeoIPManager::instance()->lookup(address().ip);
}
#endif
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
{
return (m_nativeInfo.flags & libt::peer_info::utp_socket);
}
bool PeerInfo::useSSLSocket() const
{
return (m_nativeInfo.flags & libt::peer_info::ssl_socket);
}
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());
for (int i = 0; i < m_nativeInfo.pieces.size(); ++i)
result.setBit(i, m_nativeInfo.pieces.get_bit(i));
return result;
}
QString PeerInfo::connectionType() const
{
if (m_nativeInfo.flags & libt::peer_info::utp_socket)
return QString::fromUtf8(C_UTP);
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;
}

View File

@@ -0,0 +1,101 @@
/*
* 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;
#ifndef DISABLE_COUNTRIES_RESOLUTION
QString country() const;
#endif
private:
libtorrent::peer_info m_nativeInfo;
};
}
#endif // BITTORRENT_PEERINFO_H

View 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 "base/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 accommodate for external system clock changes
// eg from the user or from a timesync utility
QTimer::start(1500);
}

View File

@@ -0,0 +1,50 @@
/*
* 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
*/
#ifndef BANDWIDTHSCHEDULER_H
#define BANDWIDTHSCHEDULER_H
#include <QTimer>
class BandwidthScheduler : public QTimer
{
Q_OBJECT
public:
BandwidthScheduler(QObject *parent = 0);
public slots:
void start();
signals:
void switchToAlternativeMode(bool alternative);
};
#endif // BANDWIDTHSCHEDULER_H

View 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 "base/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 filter 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 split by commas
QList<QByteArray> partsList = line.split(',');
const uint nbElem = partsList.size();
// IP Range should be split 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 filter 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 split 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 filter 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 constructor 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 constructor 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");
}

View File

@@ -0,0 +1,74 @@
/*
* 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 FILTERPARSERTHREAD_H
#define FILTERPARSERTHREAD_H
#include <QThread>
class QDataStream;
class QStringList;
namespace libtorrent
{
class session;
struct ip_filter;
}
class FilterParserThread : public QThread
{
Q_OBJECT
public:
FilterParserThread(libtorrent::session *s, QObject *parent = 0);
~FilterParserThread();
int parseDATFilterFile(QString filePath, libtorrent::ip_filter &filter);
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter);
int getlineInStream(QDataStream &stream, std::string &name, char delim);
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter);
void processFilterFile(QString _filePath);
static void processFilterList(libtorrent::session *s, const QStringList &IPs);
signals:
void IPFilterParsed(int ruleCount);
void IPFilterError();
protected:
QString cleanupIPAddress(QString _ip);
void run();
private:
libtorrent::session *m_session;
bool m_abort;
QString m_filePath;
};
#endif // BITTORRENT_FILTERPARSERTHREAD_H

View 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();
}

View File

@@ -0,0 +1,84 @@
/*
* 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.
*/
#ifndef SPEEDMONITOR_H
#define SPEEDMONITOR_H
template<typename T> class QList;
template<typename T>
struct Sample
{
Sample()
: download()
, upload()
{
}
Sample(T dl, T ul)
: download(dl)
, upload(ul)
{
}
Sample<T> &operator+=(const Sample<T> &other)
{
download += other.download;
upload += other.upload;
return *this;
}
Sample<T> &operator-=(const Sample<T> &other)
{
download -= other.download;
upload -= other.upload;
return *this;
}
T download;
T upload;
};
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

View File

@@ -0,0 +1,115 @@
#include <QDateTime>
#include <libtorrent/session.hpp>
#include "base/qinisettings.h"
#include "base/preferences.h"
#include "base/bittorrent/sessionstatus.h"
#include "base/bittorrent/session.h"
#include "statistics.h"
static const qint64 SAVE_INTERVAL = 15 * 60 * 1000;
namespace libt = libtorrent;
using namespace BitTorrent;
Statistics::Statistics(Session *session)
: QObject(session)
, m_session(session)
, m_sessionUL(0)
, m_sessionDL(0)
, m_lastWrite(0)
, m_dirty(false)
{
load();
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
m_timer.start(60 * 1000);
}
Statistics::~Statistics()
{
if (m_dirty)
m_lastWrite = 0;
save();
}
quint64 Statistics::getAlltimeDL() const
{
return m_alltimeDL + m_sessionDL;
}
quint64 Statistics::getAlltimeUL() const
{
return m_alltimeUL + m_sessionUL;
}
void Statistics::gather()
{
SessionStatus ss = m_session->status();
if (ss.totalDownload() > m_sessionDL) {
m_sessionDL = ss.totalDownload();
m_dirty = true;
}
if (ss.totalUpload() > m_sessionUL) {
m_sessionUL = ss.totalUpload();
m_dirty = true;
}
save();
}
void Statistics::save() const
{
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL))
return;
QIniSettings s("qBittorrent", "qBittorrent-data");
QVariantHash v;
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL);
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL);
s.setValue("Stats/AllStats", v);
m_dirty = false;
m_lastWrite = now;
}
void Statistics::load()
{
// 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
// from the old file. This code should be removed after some time has passed.
// e.g. When we reach v3.3.0
// Don't forget to remove:
// 1. Preferences::getStats()
// 2. Preferences::removeStats()
// 3. #include "base/preferences.h"
Preferences* const pref = Preferences::instance();
QIniSettings s("qBittorrent", "qBittorrent-data");
QVariantHash v = pref->getStats();
// Let's test if the qbittorrent.ini holds the key
if (!v.isEmpty()) {
m_dirty = true;
// If the user has used qbt > 3.1.5 and then reinstalled/used
// qbt < 3.1.6, there will be stats in qbittorrent-data.ini too
// so we need to merge those 2.
if (s.contains("Stats/AllStats")) {
QVariantHash tmp = s.value("Stats/AllStats").toHash();
v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong();
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
}
}
else {
v = s.value("Stats/AllStats").toHash();
}
m_alltimeDL = v["AlltimeDL"].toULongLong();
m_alltimeUL = v["AlltimeUL"].toULongLong();
if (m_dirty) {
save();
pref->removeStats();
}
}

View File

@@ -0,0 +1,41 @@
#ifndef STATISTICS_H
#define STATISTICS_H
#include <QObject>
#include <QTimer>
namespace BitTorrent { class Session; }
class Statistics : QObject
{
Q_OBJECT
Q_DISABLE_COPY(Statistics)
public:
Statistics(BitTorrent::Session *session);
~Statistics();
quint64 getAlltimeDL() const;
quint64 getAlltimeUL() const;
private slots:
void gather();
private:
void save() const;
void load();
private:
BitTorrent::Session *m_session;
// Will overflow at 15.9 EiB
quint64 m_alltimeUL;
quint64 m_alltimeDL;
qint64 m_sessionUL;
qint64 m_sessionDL;
mutable qint64 m_lastWrite;
mutable bool m_dirty;
QTimer m_timer;
};
#endif // STATISTICS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,375 @@
/*
* 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 <QNetworkConfigurationManager>
#include "base/tristatebool.h"
#include "base/types.h"
#include "torrentinfo.h"
namespace libtorrent
{
class session;
class entry;
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_delete_failed_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;
}
class QTimer;
class QStringList;
class QString;
class QUrl;
template<typename T> class QList;
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 addForced;
TriStateBool addPaused;
QVector<int> filePriorities; // used if TorrentInfo is set
bool ignoreShareRatio;
bool skipChecking;
AddTorrentParams();
};
struct TorrentStatusReport
{
uint nbDownloading = 0;
uint nbSeeding = 0;
uint nbCompleted = 0;
uint nbActive = 0;
uint nbInactive = 0;
uint nbPaused = 0;
uint nbResumed = 0;
uint nbErrored = 0;
};
class Session : public QObject
{
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;
bool isTempPathEnabled() const;
bool isAppendExtensionEnabled() const;
bool useAppendLabelToSavePath() const;
QString defaultSavePath() const;
QString tempPath() const;
TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const;
TorrentStatusReport torrentStatusReport() 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 &params = AddTorrentParams());
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params = 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);
// TorrentHandle interface
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel);
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);
signals:
void torrentsUpdated();
void addTorrentFailed(const QString &error);
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
void torrentAboutToBeRemoved(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 torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel);
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);
void switchToAlternativeMode(bool alternative);
// Session reconfiguration triggers
void networkOnlineStateChanged(const bool online);
void networkConfigurationChange(const QNetworkConfiguration&);
private:
explicit Session(QObject *parent = 0);
~Session();
bool hasPerTorrentRatioLimit() const;
void initResumeFolder();
// Session configuration
void setSessionSettings();
void setProxySettings(libtorrent::proxy_settings proxySettings);
void adjustLimits();
void adjustLimits(libtorrent::session_settings &sessionSettings);
const QStringList getListeningIPs();
void setListeningPort();
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 changeSpeedLimitMode_impl(bool alternative);
void setAppendLabelToSavePath(bool append);
void setAppendExtension(bool append);
void startUpTorrents();
bool addTorrent_impl(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 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 handleTorrentDeleteFailedAlert(libtorrent::torrent_delete_failed_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);
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);
AddTorrentData addDataFromParams(const AddTorrentParams &params);
// 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;
bool m_appendLabelToSavePath;
bool m_appendExtension;
uint m_refreshInterval;
MaxRatioAction m_highRatioAction;
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
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;
TorrentStatusReport m_torrentStatusReport;
QMutex m_alertsMutex;
QWaitCondition m_alertsWaitCondition;
QVector<libtorrent::alert *> m_alerts;
QNetworkConfigurationManager m_networkManager;
static Session *m_instance;
};
}
#endif // BITTORRENT_SESSION_H

View File

@@ -0,0 +1,136 @@
/*
* 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;
}
int SessionStatus::downloadRate() const
{
return m_nativeStatus.download_rate;
}
int SessionStatus::uploadRate() const
{
return m_nativeStatus.upload_rate;
}
int SessionStatus::ipOverheadDownloadRate() const
{
return m_nativeStatus.ip_overhead_download_rate;
}
int SessionStatus::ipOverheadUploadRate() const
{
return m_nativeStatus.ip_overhead_upload_rate;
}
int SessionStatus::dhtDownloadRate() const
{
return m_nativeStatus.dht_download_rate;
}
int SessionStatus::dhtUploadRate() const
{
return m_nativeStatus.dht_upload_rate;
}
int SessionStatus::trackerDownloadRate() const
{
return m_nativeStatus.tracker_download_rate;
}
int SessionStatus::trackerUploadRate() const
{
return m_nativeStatus.tracker_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;
}

View File

@@ -0,0 +1,79 @@
/*
* 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;
// Additional download/upload rates
int uploadRate() const;
int downloadRate() const;
int ipOverheadUploadRate() const;
int ipOverheadDownloadRate() const;
int dhtUploadRate() const;
int dhtDownloadRate() const;
int trackerUploadRate() const;
int trackerDownloadRate() 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

View File

@@ -0,0 +1,165 @@
/*
* 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/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 "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/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()));
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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
*/
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
#define BITTORRENT_TORRENTCREATORTHREAD_H
#include <QThread>
#include <QStringList>
namespace BitTorrent
{
class TorrentCreatorThread : public QThread
{
Q_OBJECT
public:
TorrentCreatorThread(QObject *parent = 0);
~TorrentCreatorThread();
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
void abortCreation();
protected:
void run();
signals:
void creationFailure(const QString &msg);
void creationSuccess(const QString &path, const QString &branchPath);
void updateProgress(int progress);
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,418 @@
/*
* 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 "base/tristatebool.h"
#include "private/speedmonitor.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 file_rename_failed_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 torrent_status;
}
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;
bool skipChecking;
TriStateBool addForced;
TriStateBool addPaused;
// for new torrents
QVector<int> filePriorities;
// for resumed torrents
qreal ratioLimit;
};
struct TrackerInfo
{
QString lastMessage;
quint32 numPeers;
TrackerInfo();
};
class TorrentState
{
public:
enum
{
Unknown = -1,
ForcedDownloading,
Downloading,
DownloadingMetadata,
Allocating,
StalledDownloading,
ForcedUploading,
Uploading,
StalledUploading,
QueuedDownloading,
QueuedUploading,
CheckingUploading,
CheckingDownloading,
QueuedForChecking,
CheckingResumeData,
PausedDownloading,
PausedUploading,
MissingFiles,
Error
};
TorrentState(int value);
operator int() const;
QString toString() const;
private:
int m_value;
};
class TorrentHandle : public QObject
{
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;
// 1. savePath() - the path where all the files and subfolders of torrent are stored (as always).
// 2. rootPath() - absolute path of torrent file tree (save path + first item from 1st torrent file path).
// 3. contentPath() - absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents).
//
// These methods have 'actual' parameter (defaults to false) which allow to get actual or final path variant.
//
// Examples.
// Suppose we have three torrent with following structures and save path `/home/user/torrents`:
//
// Torrent A (multifile)
//
// torrentA/
// subdir1/
// subdir2/
// file1
// file2
// file3
// file4
//
//
// Torrent B (singlefile)
//
// torrentB/
// subdir1/
// file1
//
//
// Torrent C (singlefile)
//
// file1
//
//
// Results:
// | | rootPath | contentPath |
// |---|------------------------------|--------------------------------------------|
// | A | /home/user/torrents/torrentA | /home/user/torrents/torrentA |
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
// | C | /home/user/torrents/file1 | /home/user/torrents/file1 |
QString savePath(bool actual = false) const;
QString rootPath(bool actual = false) const;
QString contentPath(bool actual = false) const;
int filesCount() const;
int piecesCount() const;
int piecesHave() const;
qreal progress() const;
QString label() const;
QDateTime addedTime() const;
qreal ratioLimit() 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 isErrored() 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 finishedTime() const;
int seedingTime() const;
qulonglong eta() const;
QVector<qreal> filesProgress() const;
int seedsCount() const;
int peersCount() const;
int leechsCount() const;
int totalSeedsCount() const;
int totalPeersCount() const;
int totalLeechersCount() const;
int completeCount() const;
int incompleteCount() const;
QDateTime lastSeenComplete() const;
QDateTime completedTime() const;
int timeSinceUpload() const;
int timeSinceDownload() const;
int timeSinceActivity() 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;
qlonglong totalPayloadUpload() const;
qlonglong 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 setFirstLastPiecePriority(bool b);
void toggleFirstLastPiecePriority();
void pause();
void resume(bool forced = false);
void move(QString path);
void forceReannounce(int index = -1);
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;
// Session interface
libtorrent::torrent_handle nativeHandle() const;
void handleAlert(libtorrent::alert *a);
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
private:
typedef boost::function<void ()> EventTrigger;
void initialize();
void updateStatus();
void updateStatus(const libtorrent::torrent_status &nativeStatus);
void updateState();
void updateTorrentInfo();
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 handleFileRenameFailedAlert(libtorrent::file_rename_failed_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 adjustActualSavePath();
void adjustActualSavePath_impl();
void moveStorage(const QString &newPath);
void appendExtensionsToIncompleteFiles();
void removeExtensionsFromIncompleteFiles();
bool addTracker(const TrackerEntry &tracker);
bool addUrlSeed(const QUrl &urlSeed);
bool removeUrlSeed(const QUrl &urlSeed);
Session *const m_session;
libtorrent::torrent_handle m_nativeHandle;
libtorrent::torrent_status m_nativeStatus;
TorrentState m_state;
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
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;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount;
// Persistent data
QString m_name;
QString m_savePath;
QString m_label;
bool m_hasSeedStatus;
qreal m_ratioLimit;
bool m_tempPathDisabled;
bool m_hasMissingFiles;
bool m_pauseAfterRecheck;
bool m_needSaveResumeData;
QHash<QString, TrackerInfo> m_trackerInfos;
};
}
#endif // BITTORRENT_TORRENTHANDLE_H

View File

@@ -0,0 +1,223 @@
/*
* 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 "base/utils/misc.h"
#include "base/utils/fs.h"
#include "base/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());
}
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;
}

View File

@@ -0,0 +1,87 @@
/*
* 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;
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

View File

@@ -0,0 +1,257 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* 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 <vector>
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include "base/preferences.h"
#include "base/http/server.h"
#include "base/utils/string.h"
#include "tracker.h"
// static limits
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 uid() != other.uid();
}
bool Peer::operator==(const Peer &other) const
{
return uid() == other.uid();
}
QString Peer::uid() const
{
return ip + ":" + QString::number(port);
}
libtorrent::entry Peer::toEntry(bool noPeerId) const
{
libtorrent::entry::dictionary_type peerMap;
if (!noPeerId)
peerMap["id"] = libtorrent::entry(Utils::String::toStdString(peerId));
peerMap["ip"] = libtorrent::entry(Utils::String::toStdString(ip));
peerMap["port"] = libtorrent::entry(port);
return libtorrent::entry(peerMap);
}
// Tracker
Tracker::Tracker(QObject *parent)
: Http::ResponseBuilder(parent)
, m_server(new Http::Server(this, this))
{
}
Tracker::~Tracker()
{
if (m_server->isListening())
qDebug("Shutting down the embedded tracker...");
// TODO: Store the torrent list
}
bool Tracker::start()
{
const int listenPort = Preferences::instance()->getTrackerPort();
if (m_server->isListening()) {
if (m_server->serverPort() == listenPort) {
// Already listening on the right port, just return
return true;
}
// Wrong port, closing the server
m_server->close();
}
qDebug("Starting the embedded tracker...");
// Listen on the predefined port
return m_server->listen(QHostAddress::Any, listenPort);
}
Http::Response Tracker::processRequest(const Http::Request &request, const Http::Environment &env)
{
clear(); // clear response
//qDebug("Tracker received the following request:\n%s", qPrintable(parser.toString()));
// Is request a GET request?
if (request.method != "GET") {
qDebug("Tracker: Unsupported HTTP request: %s", qPrintable(request.method));
status(100, "Invalid request type");
}
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
qDebug("Tracker: Unrecognized path: %s", qPrintable(request.path));
status(100, "Invalid request type");
}
else {
// OK, this is a GET request
m_request = request;
m_env = env;
respondToAnnounceRequest();
}
return response();
}
void Tracker::respondToAnnounceRequest()
{
const QStringMap &gets = m_request.gets;
TrackerAnnounceRequest annonceReq;
// IP
annonceReq.peer.ip = m_env.clientAddress.toString();
// 1. Get info_hash
if (!gets.contains("info_hash")) {
qDebug("Tracker: Missing info_hash");
status(101, "Missing info_hash");
return;
}
annonceReq.infoHash = gets.value("info_hash");
// info_hash cannot be longer than 20 bytes
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
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");
return;
}*/
// 2. Get peer ID
if (!gets.contains("peer_id")) {
qDebug("Tracker: Missing peer_id");
status(102, "Missing peer_id");
return;
}
annonceReq.peer.peerId = gets.value("peer_id");
// peer_id cannot be longer than 20 bytes
/*if (annonce_req.peer.peer_id.length() > 20) {
qDebug("Tracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
status(151, "Invalid peerid");
return;
}*/
// 3. Get port
if (!gets.contains("port")) {
qDebug("Tracker: Missing port");
status(103, "Missing port");
return;
}
bool ok = false;
annonceReq.peer.port = gets.value("port").toInt(&ok);
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
status(103, "Missing port");
return;
}
// 4. Get event
annonceReq.event = "";
if (gets.contains("event")) {
annonceReq.event = gets.value("event");
qDebug("Tracker: event is %s", qPrintable(annonceReq.event));
}
// 5. Get numwant
annonceReq.numwant = 50;
if (gets.contains("numwant")) {
int tmp = gets.value("numwant").toInt();
if (tmp > 0) {
qDebug("Tracker: numwant = %d", tmp);
annonceReq.numwant = tmp;
}
}
// 6. no_peer_id (extension)
annonceReq.noPeerId = false;
if (gets.contains("no_peer_id"))
annonceReq.noPeerId = true;
// 7. TODO: support "compact" extension
// Done parsing, now let's reply
if (m_torrents.contains(annonceReq.infoHash)) {
if (annonceReq.event == "stopped") {
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid());
return;
}
}
else {
// Unknown torrent
if (m_torrents.size() == MAX_TORRENTS) {
// Reached max size, remove a random torrent
m_torrents.erase(m_torrents.begin());
}
}
// Register the user
PeerList peers = m_torrents.value(annonceReq.infoHash);
if (peers.size() == MAX_PEERS_PER_TORRENT) {
// Too many peers, remove a random one
peers.erase(peers.begin());
}
peers[annonceReq.peer.uid()] = annonceReq.peer;
m_torrents[annonceReq.infoHash] = peers;
// Reply
replyWithPeerList(annonceReq);
}
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
{
// Prepare the entry for bencoding
libtorrent::entry::dictionary_type replyDict;
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
QList<Peer> peers = m_torrents.value(annonceReq.infoHash).values();
libtorrent::entry::list_type peerList;
foreach (const Peer &p, peers) {
//if (p != annonce_req.peer)
peerList.push_back(p.toEntry(annonceReq.noPeerId));
}
replyDict["peers"] = libtorrent::entry(peerList);
libtorrent::entry replyEntry(replyDict);
// bencode
std::vector<char> buf;
libtorrent::bencode(std::back_inserter(buf), replyEntry);
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
// HTTP reply
print(reply, Http::CONTENT_TYPE_TXT);
}

View 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 "base/http/types.h"
#include "base/http/responsebuilder.h"
#include "base/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

View 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 <QString>
#include "base/utils/misc.h"
#include "base/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
{
// libtorrent::announce_entry::is_working() returns
// true when the tracker hasn't been tried yet.
if (m_nativeEntry.verified && m_nativeEntry.is_working())
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;
}

View 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

View 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 "base/preferences.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/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

View File

@@ -0,0 +1,52 @@
#ifndef FILESYSTEMWATCHER_H
#define FILESYSTEMWATCHER_H
#include <QFileSystemWatcher>
#include <QDir>
#include <QTimer>
#include <QPointer>
#include <QStringList>
#include <QHash>
/*
* Subclassing QFileSystemWatcher in order to support Network File
* System watching (NFS, CIFS) on Linux and Mac OS.
*/
class FileSystemWatcher : public QFileSystemWatcher
{
Q_OBJECT
public:
explicit FileSystemWatcher(QObject *parent = 0);
~FileSystemWatcher();
QStringList directories() const;
void addPath(const QString &path);
void removePath(const QString &path);
signals:
void torrentsAdded(const QStringList &pathList);
protected slots:
void scanLocalFolder(QString path);
void scanNetworkFolders();
void processPartialTorrents();
private:
void startPartialTorrentTimer();
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
static bool isNetworkFileSystem(QString path);
#endif
#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

View File

@@ -0,0 +1,98 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 <QTcpSocket>
#include <QDebug>
#include <QRegExp>
#include "types.h"
#include "requestparser.h"
#include "responsegenerator.h"
#include "irequesthandler.h"
#include "connection.h"
using namespace Http;
Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent)
: QObject(parent)
, m_socket(socket)
, m_requestHandler(requestHandler)
{
m_socket->setParent(this);
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
}
Connection::~Connection()
{
}
void Connection::read()
{
m_receivedData.append(m_socket->readAll());
Request request;
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request);
switch (err) {
case RequestParser::IncompleteRequest:
// Partial request waiting for the rest
break;
case RequestParser::BadRequest:
sendResponse(Response(400, "Bad Request"));
break;
case RequestParser::NoError:
Environment env;
env.clientAddress = m_socket->peerAddress();
Response response = m_requestHandler->processRequest(request, env);
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
sendResponse(response);
break;
}
}
void Connection::sendResponse(const Response &response)
{
m_socket->write(ResponseGenerator::generate(response));
m_socket->disconnectFromHost();
}
bool Connection::acceptsGzipEncoding(const QString &encoding)
{
QRegExp rx("(gzip)(;q=([^,]+))?");
if (rx.indexIn(encoding) >= 0) {
if (rx.cap(2).size() > 0)
// check if quality factor > 0
return (rx.cap(3).toDouble() > 0);
// if quality factor is not specified, then it's 1
return true;
}
return false;
}

View File

@@ -0,0 +1,69 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 HTTP_CONNECTION_H
#define HTTP_CONNECTION_H
#include <QObject>
#include "types.h"
QT_BEGIN_NAMESPACE
class QTcpSocket;
QT_END_NAMESPACE
namespace Http
{
class IRequestHandler;
class Connection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Connection)
public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
~Connection();
private slots:
void read();
private:
static bool acceptsGzipEncoding(const QString &encoding);
void sendResponse(const Response &response);
QTcpSocket *m_socket;
IRequestHandler *m_requestHandler;
QByteArray m_receivedData;
};
}
#endif // HTTP_CONNECTION_H

View File

@@ -0,0 +1,45 @@
/*
* 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 HTTP_IREQUESTHANDLER_H
#define HTTP_IREQUESTHANDLER_H
#include "types.h"
namespace Http
{
class IRequestHandler
{
public:
virtual ~IRequestHandler() {}
virtual Response processRequest(const Request &request, const Environment &env) = 0;
};
}
#endif // HTTP_IREQUESTHANDLER_H

View File

@@ -0,0 +1,351 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 <QStringList>
#include <QUrl>
#ifdef QBT_USES_QT5
#include <QUrlQuery>
#endif
#include <QDir>
#include <QTemporaryFile>
#include <QDebug>
#include "requestparser.h"
const QByteArray EOL("\r\n");
const QByteArray EOH("\r\n\r\n");
inline QString unquoted(const QString& str)
{
if ((str[0] == '\"') && (str[str.length() - 1] == '\"'))
return str.mid(1, str.length() - 2);
return str;
}
using namespace Http;
RequestParser::ErrorCode RequestParser::parse(const QByteArray& data, Request& request, uint maxContentLength)
{
return RequestParser(maxContentLength).parseHttpRequest(data, request);
}
RequestParser::RequestParser(uint maxContentLength)
: m_maxContentLength(maxContentLength)
{
}
RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data, Request& request)
{
m_request = Request();
// Parse HTTP request header
const int header_end = data.indexOf(EOH);
if (header_end < 0) {
qDebug() << Q_FUNC_INFO << "incomplete request";
return IncompleteRequest;
}
if (!parseHttpHeader(data.left(header_end))) {
qWarning() << Q_FUNC_INFO << "header parsing error";
return BadRequest;
}
// Parse HTTP request message
if (m_request.headers.contains("content-length")) {
int content_length = m_request.headers["content-length"].toInt();
if (content_length > static_cast<int>(m_maxContentLength)) {
qWarning() << Q_FUNC_INFO << "bad request: message too long";
return BadRequest;
}
QByteArray content = data.mid(header_end + EOH.length(), content_length);
if (content.length() < content_length) {
qDebug() << Q_FUNC_INFO << "incomplete request";
return IncompleteRequest;
}
if (!parseContent(content)) {
qWarning() << Q_FUNC_INFO << "message parsing error";
return BadRequest;
}
}
// qDebug() << Q_FUNC_INFO;
// qDebug() << "HTTP Request header:";
// qDebug() << data.left(header_end) << "\n";
request = m_request;
return NoError;
}
bool RequestParser::parseStartingLine(const QString &line)
{
const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$");
if (rx.indexIn(line.trimmed()) >= 0) {
m_request.method = rx.cap(1);
QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1());
m_request.path = url.path(); // Path
// Parse GET parameters
#ifndef QBT_USES_QT5
QListIterator<QPair<QString, QString> > i(url.queryItems());
#else
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems());
#endif
while (i.hasNext()) {
QPair<QString, QString> pair = i.next();
m_request.gets[pair.first] = pair.second;
}
return true;
}
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
return false;
}
bool RequestParser::parseHeaderLine(const QString &line, QPair<QString, QString>& out)
{
int i = line.indexOf(QLatin1Char(':'));
if (i == -1) {
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
return false;
}
out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed());
return true;
}
bool RequestParser::parseHttpHeader(const QByteArray &data)
{
QString str = QString::fromUtf8(data);
QStringList lines = str.trimmed().split(EOL);
QStringList headerLines;
foreach (const QString& line, lines) {
if (line[0].isSpace()) { // header line continuation
if (!headerLines.isEmpty()) { // really continuation
headerLines.last() += QLatin1Char(' ');
headerLines.last() += line.trimmed();
}
}
else {
headerLines.append(line);
}
}
if (headerLines.isEmpty())
return false; // Empty header
QStringList::Iterator it = headerLines.begin();
if (!parseStartingLine(*it))
return false;
++it;
for (; it != headerLines.end(); ++it) {
QPair<QString, QString> header;
if (!parseHeaderLine(*it, header))
return false;
m_request.headers[header.first] = header.second;
}
return true;
}
QList<QByteArray> RequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary)
{
QList<QByteArray> ret;
QByteArray sep = boundary + EOL;
const int sepLength = sep.size();
int start = 0, end = 0;
if ((end = data.indexOf(sep, start)) >= 0) {
start = end + sepLength; // skip first boundary
while ((end = data.indexOf(sep, start)) >= 0) {
ret << data.mid(start, end - start);
start = end + sepLength;
}
// last or single part
sep = boundary + "--" + EOL;
if ((end = data.indexOf(sep, start)) >= 0)
ret << data.mid(start, end - start);
}
return ret;
}
bool RequestParser::parseContent(const QByteArray& data)
{
// Parse message content
qDebug() << Q_FUNC_INFO << "Content-Length: " << m_request.headers["content-length"];
qDebug() << Q_FUNC_INFO << "data.size(): " << data.size();
// Parse url-encoded POST data
if (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) {
QUrl url;
#ifndef QBT_USES_QT5
url.setEncodedQuery(data);
QListIterator<QPair<QString, QString> > i(url.queryItems());
#else
url.setQuery(data);
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
#endif
while (i.hasNext()) {
QPair<QString, QString> pair = i.next();
m_request.posts[pair.first.toLower()] = pair.second;
}
return true;
}
// Parse multipart/form data (torrent file)
/**
data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5")
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Filename\"
PB020344.torrent
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"torrentfile"; filename=\"PB020344.torrent\"
Content-Type: application/x-bittorrent
BINARY DATA IS HERE
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Upload\"
Submit Query
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--
**/
QString content_type = m_request.headers["content-type"];
if (content_type.startsWith("multipart/form-data")) {
const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
QByteArray boundary;
if (boundaryRegexQuoted.indexIn(content_type) < 0) {
if (boundaryRegexNotQuoted.indexIn(content_type) < 0) {
qWarning() << "Could not find boundary in multipart/form-data header!";
return false;
}
else {
boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1();
}
}
else {
boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1();
}
qDebug() << "Boundary is " << boundary;
QList<QByteArray> parts = splitMultipartData(data, boundary);
qDebug() << parts.size() << "parts in data";
foreach (const QByteArray& part, parts) {
if (!parseFormData(part))
return false;
}
return true;
}
qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type);
return false;
}
bool RequestParser::parseFormData(const QByteArray& data)
{
// Parse form data header
const int header_end = data.indexOf(EOH);
if (header_end < 0) {
qDebug() << "Invalid form data: \n" << data;
return false;
}
QString header_str = QString::fromUtf8(data.left(header_end));
QStringList lines = header_str.trimmed().split(EOL);
QStringMap headers;
foreach (const QString& line, lines) {
QPair<QString, QString> header;
if (!parseHeaderLine(line, header))
return false;
headers[header.first] = header.second;
}
QStringMap disposition;
if (!headers.contains("content-disposition")
|| !parseHeaderValue(headers["content-disposition"], disposition)
|| !disposition.contains("name")) {
qDebug() << "Invalid form data header: \n" << header_str;
return false;
}
if (disposition.contains("filename")) {
UploadedFile ufile;
ufile.filename = disposition["filename"];
ufile.type = disposition["content-type"];
ufile.data = data.mid(header_end + EOH.length());
m_request.files[disposition["name"]] = ufile;
}
else {
m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length()));
}
return true;
}
bool RequestParser::parseHeaderValue(const QString& value, QStringMap& out)
{
int pos = value.indexOf(QLatin1Char(';'));
if (pos == -1) {
out[""] = value.trimmed();
return true;
}
out[""] = value.left(pos).trimmed();
QRegExp rx(";\\s*([^=;\"]+)\\s*=\\s*(\"[^\"]*\"|[^\";\\s]+)\\s*");
while (rx.indexIn(value, pos) == pos) {
out[rx.cap(1).trimmed()] = unquoted(rx.cap(2));
pos += rx.cap(0).length();
}
if (pos != value.length())
return false;
return true;
}

View File

@@ -0,0 +1,72 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 HTTP_REQUESTPARSER_H
#define HTTP_REQUESTPARSER_H
#include "types.h"
namespace Http
{
class RequestParser
{
public:
enum ErrorCode
{
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;
};
}
#endif // HTTP_REQUESTPARSER_H

View File

@@ -0,0 +1,74 @@
/*
* 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 "responsebuilder.h"
using namespace Http;
ResponseBuilder::ResponseBuilder(QObject *parent)
: QObject(parent)
{
}
void ResponseBuilder::status(uint code, const QString &text)
{
m_response.status = ResponseStatus(code, text);
}
void ResponseBuilder::header(const QString &name, const QString &value)
{
m_response.headers[name] = value;
}
void ResponseBuilder::print(const QString &text, const QString &type)
{
print_impl(text.toUtf8(), type);
}
void ResponseBuilder::print(const QByteArray &data, const QString &type)
{
print_impl(data, type);
}
void ResponseBuilder::clear()
{
m_response = Response();
}
Response ResponseBuilder::response() const
{
return m_response;
}
void ResponseBuilder::print_impl(const QByteArray &data, const QString &type)
{
if (!m_response.headers.contains(HEADER_CONTENT_TYPE))
m_response.headers[HEADER_CONTENT_TYPE] = type;
m_response.content += data;
}

View File

@@ -0,0 +1,58 @@
/*
* 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 HTTP_RESPONSEBUILDER_H
#define HTTP_RESPONSEBUILDER_H
#include <QObject>
#include "types.h"
namespace Http
{
class ResponseBuilder : public QObject
{
public:
explicit ResponseBuilder(QObject *parent = 0);
protected:
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);
Response m_response;
};
}
#endif // HTTP_RESPONSEBUILDER_H

View File

@@ -0,0 +1,67 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 "base/utils/gzip.h"
#include "responsegenerator.h"
using namespace Http;
QByteArray ResponseGenerator::generate(Response response)
{
if (response.headers[HEADER_CONTENT_ENCODING] == "gzip") {
// A gzip seems to have 23 bytes overhead.
// Also "Content-Encoding: gzip\r\n" is 26 bytes long
// So we only benefit from gzip if the message is bigger than 23+26 = 49
// If the message is smaller than 49 bytes we actually send MORE data if we gzip
QByteArray dest_buf;
if ((response.content.size() > 49) && (Utils::Gzip::compress(response.content, dest_buf)))
response.content = dest_buf;
else
response.headers.remove(HEADER_CONTENT_ENCODING);
}
if (response.content.length() > 0)
response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n"));
QString header;
foreach (const QString& key, response.headers.keys())
header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]);
ret = ret.arg(response.status.code).arg(response.status.text).arg(header);
// qDebug() << Q_FUNC_INFO;
// qDebug() << "HTTP Response header:";
// qDebug() << ret;
return ret.toUtf8() + response.content;
}

View File

@@ -0,0 +1,47 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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 HTTP_RESPONSEGENERATOR_H
#define HTTP_RESPONSEGENERATOR_H
#include "types.h"
namespace Http
{
class ResponseGenerator
{
public:
static QByteArray generate(Response response);
};
}
#endif // HTTP_RESPONSEGENERATOR_H

98
src/base/http/server.cpp Normal file
View File

@@ -0,0 +1,98 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Ishan Arora <ishan@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 QT_NO_OPENSSL
#include <QSslSocket>
#else
#include <QTcpSocket>
#endif
#include "connection.h"
#include "server.h"
using namespace Http;
Server::Server(IRequestHandler *requestHandler, QObject *parent)
: QTcpServer(parent)
, m_requestHandler(requestHandler)
#ifndef QT_NO_OPENSSL
, m_https(false)
#endif
{
}
Server::~Server()
{
}
#ifndef QT_NO_OPENSSL
void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key)
{
m_certificate = certificate;
m_key = key;
m_https = true;
}
void Server::disableHttps()
{
m_https = false;
m_certificate.clear();
m_key.clear();
}
#endif
#ifdef QBT_USES_QT5
void Server::incomingConnection(qintptr socketDescriptor)
#else
void Server::incomingConnection(int socketDescriptor)
#endif
{
QTcpSocket *serverSocket;
#ifndef QT_NO_OPENSSL
if (m_https)
serverSocket = new QSslSocket(this);
else
#endif
serverSocket = new QTcpSocket(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
#ifndef QT_NO_OPENSSL
if (m_https) {
static_cast<QSslSocket*>(serverSocket)->setProtocol(QSsl::AnyProtocol);
static_cast<QSslSocket*>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket*>(serverSocket)->setLocalCertificate(m_certificate);
static_cast<QSslSocket*>(serverSocket)->startServerEncryption();
}
#endif
new Connection(serverSocket, m_requestHandler, this);
}
else {
serverSocket->deleteLater();
}
}

78
src/base/http/server.h Normal file
View File

@@ -0,0 +1,78 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#include <QTcpServer>
#ifndef QT_NO_OPENSSL
#include <QSslCertificate>
#include <QSslKey>
#endif
namespace Http
{
class IRequestHandler;
class Connection;
class Server : public QTcpServer
{
Q_OBJECT
Q_DISABLE_COPY(Server)
public:
Server(IRequestHandler *requestHandler, QObject *parent = 0);
~Server();
#ifndef QT_NO_OPENSSL
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
void disableHttps();
#endif
private:
#ifdef QBT_USES_QT5
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
};
}
#endif // HTTP_SERVER_H

94
src/base/http/types.h Normal file
View File

@@ -0,0 +1,94 @@
/*
* 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 HTTP_TYPES_H
#define HTTP_TYPES_H
#include <QString>
#include <QMap>
#include <QHostAddress>
typedef QMap<QString, QString> QStringMap;
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 CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
const QString CONTENT_TYPE_GIF = "image/gif";
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
const QString CONTENT_TYPE_JS = "application/javascript; charset=UTF-8";
const QString CONTENT_TYPE_JSON = "application/json";
const QString CONTENT_TYPE_PNG = "image/png";
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
struct Environment
{
QHostAddress clientAddress;
};
struct UploadedFile
{
QString filename; // original filename
QString type; // MIME type
QByteArray data; // File data
};
struct Request
{
QString method;
QString path;
QStringMap headers;
QStringMap gets;
QStringMap posts;
QMap<QString, UploadedFile> files;
};
struct ResponseStatus
{
uint code;
QString text;
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
};
struct Response
{
ResponseStatus status;
QStringMap headers;
QByteArray content;
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
};
}
#endif // HTTP_TYPES_H

64
src/base/iconprovider.cpp Normal file
View 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;

55
src/base/iconprovider.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef ICONPROVIDER_H
#define ICONPROVIDER_H
#include <QObject>
class QString;
class IconProvider : public QObject
{
Q_DISABLE_COPY(IconProvider)
public:
static void initInstance();
static void freeInstance();
static IconProvider *instance();
virtual QString getIconPath(const QString &iconId);
protected:
explicit IconProvider(QObject *parent = 0);
~IconProvider();
static IconProvider *m_instance;
};
#endif // ICONPROVIDER_H

116
src/base/logger.cpp Normal file
View File

@@ -0,0 +1,116 @@
#include "logger.h"
#include <QDateTime>
namespace Log
{
Msg::Msg() {}
Msg::Msg(int id, MsgType type, const QString &message)
: id(id)
, timestamp(QDateTime::currentMSecsSinceEpoch())
, type(type)
, message(message)
{
}
Peer::Peer() {}
Peer::Peer(int id, const QString &ip, bool blocked, const QString &reason)
: id(id)
, timestamp(QDateTime::currentMSecsSinceEpoch())
, ip(ip)
, blocked(blocked)
, reason(reason)
{
}
}
Logger* Logger::m_instance = 0;
Logger::Logger()
: lock(QReadWriteLock::Recursive)
, msgCounter(0)
, peerCounter(0)
{
}
Logger::~Logger() {}
Logger *Logger::instance()
{
return m_instance;
}
void Logger::initInstance()
{
if (!m_instance)
m_instance = new Logger;
}
void Logger::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = 0;
}
}
void Logger::addMessage(const QString &message, const Log::MsgType &type)
{
QWriteLocker locker(&lock);
Log::Msg temp(msgCounter++, type, message);
m_messages.push_back(temp);
if (m_messages.size() >= MAX_LOG_MESSAGES)
m_messages.pop_front();
emit newLogMessage(temp);
}
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
{
QWriteLocker locker(&lock);
Log::Peer temp(peerCounter++, ip, blocked, reason);
m_peers.push_back(temp);
if (m_peers.size() >= MAX_LOG_MESSAGES)
m_peers.pop_front();
emit newLogPeer(temp);
}
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
{
QReadLocker locker(&lock);
int diff = msgCounter - lastKnownId - 1;
int size = m_messages.size();
if ((lastKnownId == -1) || (diff >= size))
return m_messages;
if (diff <= 0)
return QVector<Log::Msg>();
return m_messages.mid(size - diff);
}
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
{
QReadLocker locker(&lock);
int diff = peerCounter - lastKnownId - 1;
int size = m_peers.size();
if ((lastKnownId == -1) || (diff >= size))
return m_peers;
if (diff <= 0)
return QVector<Log::Peer>();
return m_peers.mid(size - diff);
}

74
src/base/logger.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QString>
#include <QVector>
#include <QReadWriteLock>
#include <QObject>
const int MAX_LOG_MESSAGES = 1000;
namespace Log
{
enum MsgType
{
NORMAL,
INFO,
WARNING,
CRITICAL //ERROR is defined by libtorrent and results in compiler error
};
struct Msg
{
Msg();
Msg(int id, MsgType type, const QString &message);
int id;
qint64 timestamp;
MsgType type;
QString message;
};
struct Peer
{
Peer(int id, const QString &ip, bool blocked, const QString &reason);
Peer();
int id;
qint64 timestamp;
QString ip;
bool blocked;
QString reason;
};
}
class Logger : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Logger)
public:
static void initInstance();
static void freeInstance();
static Logger *instance();
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
void addPeer(const QString &ip, bool blocked, const QString &reason = QString());
QVector<Log::Msg> getMessages(int lastKnownId = -1) const;
QVector<Log::Peer> getPeers(int lastKnownId = -1) const;
signals:
void newLogMessage(const Log::Msg &message);
void newLogPeer(const Log::Peer &peer);
private:
Logger();
~Logger();
static Logger* m_instance;
QVector<Log::Msg> m_messages;
QVector<Log::Peer> m_peers;
mutable QReadWriteLock lock;
int msgCounter;
int peerCounter;
};
#endif // LOGGER_H

305
src/base/net/dnsupdater.cpp Normal file
View 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>
#ifdef QBT_USES_QT5
#include <QUrlQuery>
#endif
#include "base/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);
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 to 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);
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");
#ifndef QBT_USES_QT5
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 {
// Parse 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();
}

90
src/base/net/dnsupdater.h Normal file
View File

@@ -0,0 +1,90 @@
/*
* 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
*/
#ifndef DNSUPDATER_H
#define DNSUPDATER_H
#include <QObject>
#include <QHostAddress>
#include <QNetworkReply>
#include <QDateTime>
#include <QTimer>
#include "base/preferences.h"
namespace Net
{
// Based on http://www.dyndns.com/developers/specs/
class DNSUpdater : public QObject
{
Q_OBJECT
public:
explicit DNSUpdater(QObject *parent = 0);
~DNSUpdater();
static QUrl getRegistrationUrl(int service);
public slots:
void updateCredentials();
private slots:
void checkPublicIP();
void ipRequestFinished(QNetworkReply *reply);
void updateDNSService();
void ipUpdateFinished(QNetworkReply *reply);
private:
QUrl getUpdateUrl() const;
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

View File

@@ -0,0 +1,248 @@
/*
* 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 "base/utils/fs.h"
#include "base/utils/gzip.h"
#include "base/utils/misc.h"
#include "downloadmanager.h"
#include "downloadhandler.h"
static QString errorCodeToString(QNetworkReply::NetworkError status);
using namespace Net;
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
: QObject(manager)
, m_reply(reply)
, m_manager(manager)
, m_saveToFile(saveToFile)
, m_sizeLimit(limit)
, m_handleRedirectToMagnet(handleRedirectToMagnet)
, 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
QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// uncompress gzip reply
Utils::Gzip::uncompress(replyData, replyData);
}
if (m_saveToFile) {
QString filePath;
if (saveToFile(replyData, filePath))
emit downloadFinished(m_url, filePath);
else
emit downloadFailed(m_url, tr("I/O Error"));
}
else {
emit downloadFinished(m_url, replyData);
}
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(const QByteArray &replyData, 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)) {
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();
if (m_handleRedirectToMagnet)
emit redirectedToMagnet(m_url, newUrlString);
else
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
this->deleteLater();
}
else {
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet);
m_reply->deleteLater();
m_reply = tmp->m_reply;
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 honor 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");
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
~DownloadHandler();
QString url() const;
signals:
void downloadFinished(const QString &url, const QByteArray &data);
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(const QByteArray &replyData, QString &filePath);
void handleRedirection(QUrl newUrl);
QNetworkReply *m_reply;
DownloadManager *m_manager;
bool m_saveToFile;
qint64 m_sizeLimit;
bool m_handleRedirectToMagnet;
QString m_url;
};
}
#endif // NET_DOWNLOADHANDLER_H

View File

@@ -0,0 +1,151 @@
/*
* 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 "base/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, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
{
// 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 38 user agent to avoid web server banning
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0");
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request.setRawHeader("Referer", request.url().toEncoded().data());
qDebug("Downloading %s...", request.url().toEncoded().data());
// accept gzip
request.setRawHeader("Accept-Encoding", "gzip");
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
}
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() && !pref->isProxyOnlyForTorrents()) {
// 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

View 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_DOWNLOADMANAGER_H
#define NET_DOWNLOADMANAGER_H
#include <QObject>
#include <QNetworkAccessManager>
QT_BEGIN_NAMESPACE
class QNetworkReply;
class QNetworkCookie;
class QSslError;
class QUrl;
QT_END_NAMESPACE
namespace Net
{
class DownloadHandler;
class DownloadManager : public QObject
{
Q_OBJECT
public:
static void initInstance();
static void freeInstance();
static DownloadManager *instance();
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
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;
};
}
#endif // NET_DOWNLOADMANAGER_H

View File

@@ -0,0 +1,461 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 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 <QDebug>
#include <QFile>
#include <QDir>
#include <QHostAddress>
#include <QDateTime>
#include "base/logger.h"
#include "base/preferences.h"
#include "base/utils/fs.h"
#include "base/utils/gzip.h"
#include "downloadmanager.h"
#include "downloadhandler.h"
#include "private/geoipdatabase.h"
#include "geoipmanager.h"
static const char DATABASE_URL[] = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
static const char GEOIP_FOLDER[] = "GeoIP";
static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb";
static const int CACHE_SIZE = 1000;
static const int UPDATE_INTERVAL = 30; // Days between database updates
using namespace Net;
// GeoIPManager
GeoIPManager *GeoIPManager::m_instance = 0;
GeoIPManager::GeoIPManager()
: m_enabled(false)
, m_geoIPDatabase(0)
{
configure();
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
}
GeoIPManager::~GeoIPManager()
{
if (m_geoIPDatabase)
delete m_geoIPDatabase;
}
void GeoIPManager::initInstance()
{
if (!m_instance)
m_instance = new GeoIPManager;
}
void GeoIPManager::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = 0;
}
}
GeoIPManager *GeoIPManager::instance()
{
return m_instance;
}
void GeoIPManager::loadDatabase()
{
if (m_geoIPDatabase) {
delete m_geoIPDatabase;
m_geoIPDatabase = 0;
}
QString filepath = Utils::Fs::expandPathAbs(
QString("%1%2/%3").arg(Utils::Fs::QDesktopServicesDataLocation())
.arg(GEOIP_FOLDER).arg(GEOIP_FILENAME));
QString error;
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
if (m_geoIPDatabase)
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
Log::INFO);
else
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
manageDatabaseUpdate();
}
void GeoIPManager::manageDatabaseUpdate()
{
if (!m_geoIPDatabase || (m_geoIPDatabase->buildEpoch().daysTo(QDateTime::currentDateTimeUtc()) >= UPDATE_INTERVAL))
downloadDatabaseFile();
}
void GeoIPManager::downloadDatabaseFile()
{
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(DATABASE_URL);
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(downloadFinished(QString, QByteArray)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(downloadFailed(QString, QString)));
}
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
{
if (m_enabled && m_geoIPDatabase)
return m_geoIPDatabase->lookup(hostAddr);
return QString();
}
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
QString GeoIPManager::CountryName(const QString &countryISOCode)
{
static QHash<QString, QString> countries;
static bool initialized = false;
if (!initialized) {
countries[QString()] = tr("N/A");
countries["AP"] = tr("Asia/Pacific Region");
countries["EU"] = tr("Europe");
countries["AD"] = tr("Andorra");
countries["AE"] = tr("United Arab Emirates");
countries["AF"] = tr("Afghanistan");
countries["AG"] = tr("Antigua and Barbuda");
countries["AI"] = tr("Anguilla");
countries["AL"] = tr("Albania");
countries["AM"] = tr("Armenia");
countries["AN"] = tr("Netherlands Antilles");
countries["AO"] = tr("Angola");
countries["AQ"] = tr("Antarctica");
countries["AR"] = tr("Argentina");
countries["AS"] = tr("American Samoa");
countries["AT"] = tr("Austria");
countries["AU"] = tr("Australia");
countries["AW"] = tr("Aruba");
countries["AZ"] = tr("Azerbaijan");
countries["BA"] = tr("Bosnia and Herzegovina");
countries["BB"] = tr("Barbados");
countries["BD"] = tr("Bangladesh");
countries["BE"] = tr("Belgium");
countries["BF"] = tr("Burkina Faso");
countries["BG"] = tr("Bulgaria");
countries["BH"] = tr("Bahrain");
countries["BI"] = tr("Burundi");
countries["BJ"] = tr("Benin");
countries["BM"] = tr("Bermuda");
countries["BN"] = tr("Brunei Darussalam");
countries["BO"] = tr("Bolivia");
countries["BR"] = tr("Brazil");
countries["BS"] = tr("Bahamas");
countries["BT"] = tr("Bhutan");
countries["BV"] = tr("Bouvet Island");
countries["BW"] = tr("Botswana");
countries["BY"] = tr("Belarus");
countries["BZ"] = tr("Belize");
countries["CA"] = tr("Canada");
countries["CC"] = tr("Cocos (Keeling) Islands");
countries["CD"] = tr("Congo, The Democratic Republic of the");
countries["CF"] = tr("Central African Republic");
countries["CG"] = tr("Congo");
countries["CH"] = tr("Switzerland");
countries["CI"] = tr("Cote D'Ivoire");
countries["CK"] = tr("Cook Islands");
countries["CL"] = tr("Chile");
countries["CM"] = tr("Cameroon");
countries["CN"] = tr("China");
countries["CO"] = tr("Colombia");
countries["CR"] = tr("Costa Rica");
countries["CU"] = tr("Cuba");
countries["CV"] = tr("Cape Verde");
countries["CX"] = tr("Christmas Island");
countries["CY"] = tr("Cyprus");
countries["CZ"] = tr("Czech Republic");
countries["DE"] = tr("Germany");
countries["DJ"] = tr("Djibouti");
countries["DK"] = tr("Denmark");
countries["DM"] = tr("Dominica");
countries["DO"] = tr("Dominican Republic");
countries["DZ"] = tr("Algeria");
countries["EC"] = tr("Ecuador");
countries["EE"] = tr("Estonia");
countries["EG"] = tr("Egypt");
countries["EH"] = tr("Western Sahara");
countries["ER"] = tr("Eritrea");
countries["ES"] = tr("Spain");
countries["ET"] = tr("Ethiopia");
countries["FI"] = tr("Finland");
countries["FJ"] = tr("Fiji");
countries["FK"] = tr("Falkland Islands (Malvinas)");
countries["FM"] = tr("Micronesia, Federated States of");
countries["FO"] = tr("Faroe Islands");
countries["FR"] = tr("France");
countries["FX"] = tr("France, Metropolitan");
countries["GA"] = tr("Gabon");
countries["GB"] = tr("United Kingdom");
countries["GD"] = tr("Grenada");
countries["GE"] = tr("Georgia");
countries["GF"] = tr("French Guiana");
countries["GH"] = tr("Ghana");
countries["GI"] = tr("Gibraltar");
countries["GL"] = tr("Greenland");
countries["GM"] = tr("Gambia");
countries["GN"] = tr("Guinea");
countries["GP"] = tr("Guadeloupe");
countries["GQ"] = tr("Equatorial Guinea");
countries["GR"] = tr("Greece");
countries["GS"] = tr("South Georgia and the South Sandwich Islands");
countries["GT"] = tr("Guatemala");
countries["GU"] = tr("Guam");
countries["GW"] = tr("Guinea-Bissau");
countries["GY"] = tr("Guyana");
countries["HK"] = tr("Hong Kong");
countries["HM"] = tr("Heard Island and McDonald Islands");
countries["HN"] = tr("Honduras");
countries["HR"] = tr("Croatia");
countries["HT"] = tr("Haiti");
countries["HU"] = tr("Hungary");
countries["ID"] = tr("Indonesia");
countries["IE"] = tr("Ireland");
countries["IL"] = tr("Israel");
countries["IN"] = tr("India");
countries["IO"] = tr("British Indian Ocean Territory");
countries["IQ"] = tr("Iraq");
countries["IR"] = tr("Iran, Islamic Republic of");
countries["IS"] = tr("Iceland");
countries["IT"] = tr("Italy");
countries["JM"] = tr("Jamaica");
countries["JO"] = tr("Jordan");
countries["JP"] = tr("Japan");
countries["KE"] = tr("Kenya");
countries["KG"] = tr("Kyrgyzstan");
countries["KH"] = tr("Cambodia");
countries["KI"] = tr("Kiribati");
countries["KM"] = tr("Comoros");
countries["KN"] = tr("Saint Kitts and Nevis");
countries["KP"] = tr("Korea, Democratic People's Republic of");
countries["KR"] = tr("Korea, Republic of");
countries["KW"] = tr("Kuwait");
countries["KY"] = tr("Cayman Islands");
countries["KZ"] = tr("Kazakhstan");
countries["LA"] = tr("Lao People's Democratic Republic");
countries["LB"] = tr("Lebanon");
countries["LC"] = tr("Saint Lucia");
countries["LI"] = tr("Liechtenstein");
countries["LK"] = tr("Sri Lanka");
countries["LR"] = tr("Liberia");
countries["LS"] = tr("Lesotho");
countries["LT"] = tr("Lithuania");
countries["LU"] = tr("Luxembourg");
countries["LV"] = tr("Latvia");
countries["LY"] = tr("Libyan Arab Jamahiriya");
countries["MA"] = tr("Morocco");
countries["MC"] = tr("Monaco");
countries["MD"] = tr("Moldova, Republic of");
countries["MG"] = tr("Madagascar");
countries["MH"] = tr("Marshall Islands");
countries["MK"] = tr("Macedonia");
countries["ML"] = tr("Mali");
countries["MM"] = tr("Myanmar");
countries["MN"] = tr("Mongolia");
countries["MO"] = tr("Macau");
countries["MP"] = tr("Northern Mariana Islands");
countries["MQ"] = tr("Martinique");
countries["MR"] = tr("Mauritania");
countries["MS"] = tr("Montserrat");
countries["MT"] = tr("Malta");
countries["MU"] = tr("Mauritius");
countries["MV"] = tr("Maldives");
countries["MW"] = tr("Malawi");
countries["MX"] = tr("Mexico");
countries["MY"] = tr("Malaysia");
countries["MZ"] = tr("Mozambique");
countries["NA"] = tr("Namibia");
countries["NC"] = tr("New Caledonia");
countries["NE"] = tr("Niger");
countries["NF"] = tr("Norfolk Island");
countries["NG"] = tr("Nigeria");
countries["NI"] = tr("Nicaragua");
countries["NL"] = tr("Netherlands");
countries["NO"] = tr("Norway");
countries["NP"] = tr("Nepal");
countries["NR"] = tr("Nauru");
countries["NU"] = tr("Niue");
countries["NZ"] = tr("New Zealand");
countries["OM"] = tr("Oman");
countries["PA"] = tr("Panama");
countries["PE"] = tr("Peru");
countries["PF"] = tr("French Polynesia");
countries["PG"] = tr("Papua New Guinea");
countries["PH"] = tr("Philippines");
countries["PK"] = tr("Pakistan");
countries["PL"] = tr("Poland");
countries["PM"] = tr("Saint Pierre and Miquelon");
countries["PN"] = tr("Pitcairn Islands");
countries["PR"] = tr("Puerto Rico");
countries["PS"] = tr("Palestinian Territory");
countries["PT"] = tr("Portugal");
countries["PW"] = tr("Palau");
countries["PY"] = tr("Paraguay");
countries["QA"] = tr("Qatar");
countries["RE"] = tr("Reunion");
countries["RO"] = tr("Romania");
countries["RU"] = tr("Russian Federation");
countries["RW"] = tr("Rwanda");
countries["SA"] = tr("Saudi Arabia");
countries["SB"] = tr("Solomon Islands");
countries["SC"] = tr("Seychelles");
countries["SD"] = tr("Sudan");
countries["SE"] = tr("Sweden");
countries["SG"] = tr("Singapore");
countries["SH"] = tr("Saint Helena");
countries["SI"] = tr("Slovenia");
countries["SJ"] = tr("Svalbard and Jan Mayen");
countries["SK"] = tr("Slovakia");
countries["SL"] = tr("Sierra Leone");
countries["SM"] = tr("San Marino");
countries["SN"] = tr("Senegal");
countries["SO"] = tr("Somalia");
countries["SR"] = tr("Suriname");
countries["ST"] = tr("Sao Tome and Principe");
countries["SV"] = tr("El Salvador");
countries["SY"] = tr("Syrian Arab Republic");
countries["SZ"] = tr("Swaziland");
countries["TC"] = tr("Turks and Caicos Islands");
countries["TD"] = tr("Chad");
countries["TF"] = tr("French Southern Territories");
countries["TG"] = tr("Togo");
countries["TH"] = tr("Thailand");
countries["TJ"] = tr("Tajikistan");
countries["TK"] = tr("Tokelau");
countries["TM"] = tr("Turkmenistan");
countries["TN"] = tr("Tunisia");
countries["TO"] = tr("Tonga");
countries["TL"] = tr("Timor-Leste");
countries["TR"] = tr("Turkey");
countries["TT"] = tr("Trinidad and Tobago");
countries["TV"] = tr("Tuvalu");
countries["TW"] = tr("Taiwan");
countries["TZ"] = tr("Tanzania, United Republic of");
countries["UA"] = tr("Ukraine");
countries["UG"] = tr("Uganda");
countries["UM"] = tr("United States Minor Outlying Islands");
countries["US"] = tr("United States");
countries["UY"] = tr("Uruguay");
countries["UZ"] = tr("Uzbekistan");
countries["VA"] = tr("Holy See (Vatican City State)");
countries["VC"] = tr("Saint Vincent and the Grenadines");
countries["VE"] = tr("Venezuela");
countries["VG"] = tr("Virgin Islands, British");
countries["VI"] = tr("Virgin Islands, U.S.");
countries["VN"] = tr("Vietnam");
countries["VU"] = tr("Vanuatu");
countries["WF"] = tr("Wallis and Futuna");
countries["WS"] = tr("Samoa");
countries["YE"] = tr("Yemen");
countries["YT"] = tr("Mayotte");
countries["RS"] = tr("Serbia");
countries["ZA"] = tr("South Africa");
countries["ZM"] = tr("Zambia");
countries["ME"] = tr("Montenegro");
countries["ZW"] = tr("Zimbabwe");
countries["A1"] = tr("Anonymous Proxy");
countries["A2"] = tr("Satellite Provider");
countries["O1"] = tr("Other");
countries["AX"] = tr("Aland Islands");
countries["GG"] = tr("Guernsey");
countries["IM"] = tr("Isle of Man");
countries["JE"] = tr("Jersey");
countries["BL"] = tr("Saint Barthelemy");
countries["MF"] = tr("Saint Martin");
initialized = true;
}
return countries.value(countryISOCode, tr("N/A"));
}
void GeoIPManager::configure()
{
const bool enabled = Preferences::instance()->resolvePeerCountries();
if (m_enabled != enabled) {
m_enabled = enabled;
if (m_enabled && !m_geoIPDatabase) {
loadDatabase();
}
else if (!m_enabled && m_geoIPDatabase) {
delete m_geoIPDatabase;
m_geoIPDatabase = 0;
}
}
}
void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
{
Q_UNUSED(url);
if (!Utils::Gzip::uncompress(data, data)) {
Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING);
return;
}
QString error;
GeoIPDatabase *geoIPDatabase = GeoIPDatabase::load(data, error);
if (geoIPDatabase) {
if (!m_geoIPDatabase || (geoIPDatabase->buildEpoch() > m_geoIPDatabase->buildEpoch())) {
if (m_geoIPDatabase)
delete m_geoIPDatabase;
m_geoIPDatabase = geoIPDatabase;
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
Log::INFO);
QString targetPath = Utils::Fs::expandPathAbs(
Utils::Fs::QDesktopServicesDataLocation() + GEOIP_FOLDER);
if (!QDir(targetPath).exists())
QDir().mkpath(targetPath);
QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME));
if (!targetFile.open(QFile::WriteOnly) || (targetFile.write(data) == -1)) {
Logger::instance()->addMessage(
tr("Couldn't save downloaded GeoIP database file."), Log::WARNING);
}
else {
Logger::instance()->addMessage(tr("Successfully updated GeoIP database."), Log::INFO);
}
}
else {
delete geoIPDatabase;
}
}
else {
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
}
}
void GeoIPManager::downloadFailed(const QString &url, const QString &reason)
{
Q_UNUSED(url);
Logger::instance()->addMessage(tr("Couldn't download GeoIP database file. Reason: %1").arg(reason), Log::WARNING);
}

View File

@@ -0,0 +1,76 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2010 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_GEOIPMANAGER_H
#define NET_GEOIPMANAGER_H
#include <QObject>
#include <QCache>
class QHostAddress;
class QString;
class GeoIPDatabase;
namespace Net
{
class GeoIPManager : public QObject
{
Q_OBJECT
public:
static void initInstance();
static void freeInstance();
static GeoIPManager *instance();
QString lookup(const QHostAddress &hostAddr) const;
static QString CountryName(const QString &countryISOCode);
private slots:
void configure();
void downloadFinished(const QString &url, QByteArray data);
void downloadFailed(const QString &url, const QString &reason);
private:
GeoIPManager();
~GeoIPManager();
void loadDatabase();
void manageDatabaseUpdate();
void downloadDatabaseFile();
bool m_enabled;
GeoIPDatabase *m_geoIPDatabase;
static GeoIPManager *m_instance;
};
}
#endif // NET_GEOIPMANAGER_H

View File

@@ -0,0 +1,122 @@
/*
* 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>
#include "base/logger.h"
#include "base/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)
{
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)) {
m_mappedPorts.insert(port, 0);
if (m_active)
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
}
}
void PortForwarder::deletePort(qint16 port)
{
if (m_mappedPorts.contains(port)) {
if (m_active)
m_provider->delete_port_mapping(m_mappedPorts[port]);
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");
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);
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();
m_active = false;
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO);
}
PortForwarder *PortForwarder::m_instance = 0;

View 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 NET_PORTFORWARDER_H
#define NET_PORTFORWARDER_H
#include <QObject>
#include <QHash>
namespace libtorrent
{
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;
QHash<qint16, int> m_mappedPorts;
static PortForwarder *m_instance;
};
}
#endif // NET_PORTFORWARDER_H

View File

@@ -0,0 +1,520 @@
/*
* 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 <QVariant>
#include <QHash>
#include <QHostAddress>
#include <QDateTime>
#include <QFile>
#include "base/types.h"
#include "geoipdatabase.h"
namespace
{
const quint32 __ENDIAN_TEST__ = 0x00000001;
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__ENDIAN_TEST__)[0] == 0x01);
const int MAX_FILE_SIZE = 10485760; // 10MB
const char DB_TYPE[] = "GeoLite2-Country";
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
const char DATA_SECTION_SEPARATOR[16] = { 0 };
enum class DataType
{
Unknown = 0,
Pointer = 1,
String = 2,
Double = 3,
Bytes = 4,
Integer16 = 5,
Integer32 = 6,
Map = 7,
SignedInteger32 = 8,
Integer64 = 9,
Integer128 = 10,
Array = 11,
DataCacheContainer = 12,
EndMarker = 13,
Boolean = 14,
Float = 15
};
#ifndef QBT_USES_QT5
Q_IPV6ADDR createMappedAddress(quint32 ip4);
#endif
}
struct DataFieldDescriptor
{
DataType fieldType;
union
{
quint32 fieldSize;
quint32 offset; // Pointer
};
};
GeoIPDatabase::GeoIPDatabase(quint32 size)
: m_ipVersion(0)
, m_recordSize(0)
, m_nodeCount(0)
, m_nodeSize(0)
, m_indexSize(0)
, m_recordBytes(0)
, m_size(size)
, m_data(new uchar[size])
{
}
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
{
GeoIPDatabase *db = 0;
QFile file(filename);
if (file.size() > MAX_FILE_SIZE) {
error = tr("Unsupported database file size.");
return 0;
}
if (!file.open(QFile::ReadOnly)) {
error = file.errorString();
return 0;
}
db = new GeoIPDatabase(file.size());
if (file.read((char *)db->m_data, db->m_size) != db->m_size) {
error = file.errorString();
delete db;
return 0;
}
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
delete db;
return 0;
}
return db;
}
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
{
GeoIPDatabase *db = 0;
if (data.size() > MAX_FILE_SIZE) {
error = tr("Unsupported database file size.");
return 0;
}
db = new GeoIPDatabase(data.size());
memcpy((char *)db->m_data, data.constData(), db->m_size);
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
delete db;
return 0;
}
return db;
}
GeoIPDatabase::~GeoIPDatabase()
{
delete [] m_data;
}
QString GeoIPDatabase::type() const
{
return DB_TYPE;
}
quint16 GeoIPDatabase::ipVersion() const
{
return m_ipVersion;
}
QDateTime GeoIPDatabase::buildEpoch() const
{
return m_buildEpoch;
}
QString GeoIPDatabase::lookup(const QHostAddress &hostAddr) const
{
#ifndef QBT_USES_QT5
Q_IPV6ADDR addr = hostAddr.protocol() == QAbstractSocket::IPv4Protocol
? createMappedAddress(hostAddr.toIPv4Address())
: hostAddr.toIPv6Address();
#else
Q_IPV6ADDR addr = hostAddr.toIPv6Address();
#endif
const uchar *ptr = m_data;
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 8; ++j) {
bool right = static_cast<bool>((addr[i] >> (7 - j)) & 1);
// Interpret the left/right record as number
if (right)
ptr += m_recordBytes;
quint32 id = 0;
uchar *idPtr = reinterpret_cast<uchar *>(&id);
memcpy(&idPtr[4 - m_recordBytes], ptr, m_recordBytes);
fromBigEndian(idPtr, 4);
if (id == m_nodeCount) {
return QString();
}
else if (id > m_nodeCount) {
QString country = m_countries.value(id);
if (country.isEmpty()) {
const quint32 offset = id - m_nodeCount - sizeof(DATA_SECTION_SEPARATOR);
quint32 tmp = offset + m_indexSize + sizeof(DATA_SECTION_SEPARATOR);
QVariant val = readDataField(tmp);
if (val.userType() == QMetaType::QVariantHash) {
country = val.toHash()["country"].toHash()["iso_code"].toString();
m_countries[id] = country;
}
}
return country;
}
else {
ptr = m_data + (id * m_nodeSize);
}
}
}
return QString();
}
#define CHECK_METADATA_REQ(key, type) \
if (!metadata.contains(#key)) { \
error = errMsgNotFound.arg(#key); \
return false; \
} \
else if (metadata.value(#key).userType() != QMetaType::type) { \
error = errMsgInvalid.arg(#key); \
return false; \
}
#define CHECK_METADATA_OPT(key, type) \
if (metadata.contains(#key)) { \
if (metadata.value(#key).userType() != QMetaType::type) { \
error = errMsgInvalid.arg(#key); \
return false; \
} \
}
bool GeoIPDatabase::parseMetadata(const QVariantHash &metadata, QString &error)
{
const QString errMsgNotFound = tr("Metadata error: '%1' entry not found.");
const QString errMsgInvalid = tr("Metadata error: '%1' entry has invalid type.");
qDebug() << "Parsing MaxMindDB metadata...";
CHECK_METADATA_REQ(binary_format_major_version, UShort);
CHECK_METADATA_REQ(binary_format_minor_version, UShort);
uint versionMajor = metadata.value("binary_format_major_version").toUInt();
uint versionMinor = metadata.value("binary_format_minor_version").toUInt();
if (versionMajor != 2) {
error = tr("Unsupported database version: %1.%2").arg(versionMajor).arg(versionMinor);
return false;
}
CHECK_METADATA_REQ(ip_version, UShort);
m_ipVersion = metadata.value("ip_version").value<quint16>();
if (m_ipVersion != 6) {
error = tr("Unsupported IP version: %1").arg(m_ipVersion);
return false;
}
CHECK_METADATA_REQ(record_size, UShort);
m_recordSize = metadata.value("record_size").value<quint16>();
if (m_recordSize != 24) {
error = tr("Unsupported record size: %1").arg(m_recordSize);
return false;
}
m_nodeSize = m_recordSize / 4;
m_recordBytes = m_nodeSize / 2;
CHECK_METADATA_REQ(node_count, UInt);
m_nodeCount = metadata.value("node_count").value<quint32>();
m_indexSize = m_nodeCount * m_nodeSize;
CHECK_METADATA_REQ(database_type, QString);
QString dbType = metadata.value("database_type").toString();
if (dbType != DB_TYPE) {
error = tr("Invalid database type: %1").arg(dbType);
return false;
}
CHECK_METADATA_REQ(build_epoch, ULongLong);
m_buildEpoch = QDateTime::fromTime_t(metadata.value("build_epoch").toULongLong());
CHECK_METADATA_OPT(languages, QVariantList);
CHECK_METADATA_OPT(description, QVariantHash);
return true;
}
bool GeoIPDatabase::loadDB(QString &error) const
{
qDebug() << "Parsing MaxMindDB index tree...";
const int nodeSize = m_recordSize / 4; // in bytes
const int indexSize = m_nodeCount * nodeSize;
if ((m_size < (indexSize + sizeof(DATA_SECTION_SEPARATOR)))
|| (memcmp(m_data + indexSize, DATA_SECTION_SEPARATOR, sizeof(DATA_SECTION_SEPARATOR)) != 0)) {
error = tr("Database corrupted: no data section found.");
return false;
}
return true;
}
QVariantHash GeoIPDatabase::readMetadata() const
{
const char *ptr = reinterpret_cast<const char *>(m_data);
quint32 size = m_size;
if (m_size > MAX_METADATA_SIZE) {
ptr += m_size - MAX_METADATA_SIZE;
size = MAX_METADATA_SIZE;
}
const QByteArray data = QByteArray::fromRawData(ptr, size);
int index = data.lastIndexOf(METADATA_BEGIN_MARK);
if (index >= 0) {
if (m_size > MAX_METADATA_SIZE)
index += (m_size - MAX_METADATA_SIZE); // from begin of all data
quint32 offset = static_cast<quint32>(index + strlen(METADATA_BEGIN_MARK));
QVariant metadata = readDataField(offset);
if (metadata.userType() == QMetaType::QVariantHash)
return metadata.toHash();
}
return QVariantHash();
}
QVariant GeoIPDatabase::readDataField(quint32 &offset) const
{
DataFieldDescriptor descr;
if (!readDataFieldDescriptor(offset, descr))
return QVariant();
quint32 locOffset = offset;
bool usePointer = false;
if (descr.fieldType == DataType::Pointer) {
usePointer = true;
// convert offset from data section to global
locOffset = descr.offset + (m_nodeCount * m_recordSize / 4) + sizeof(DATA_SECTION_SEPARATOR);
if (!readDataFieldDescriptor(locOffset, descr))
return QVariant();
}
QVariant fieldValue;
switch (descr.fieldType) {
case DataType::Pointer:
qDebug() << "* Illegal Pointer using";
break;
case DataType::String:
fieldValue = QString::fromUtf8(reinterpret_cast<const char *>(m_data + locOffset), descr.fieldSize);
locOffset += descr.fieldSize;
break;
case DataType::Double:
if (descr.fieldSize == 8)
fieldValue = readPlainValue<double>(locOffset, descr.fieldSize);
else
qDebug() << "* Invalid field size for type: Double";
break;
case DataType::Bytes:
fieldValue = QByteArray(reinterpret_cast<const char *>(m_data + locOffset), descr.fieldSize);
locOffset += descr.fieldSize;
break;
case DataType::Integer16:
fieldValue = readPlainValue<quint16>(locOffset, descr.fieldSize);
break;
case DataType::Integer32:
fieldValue = readPlainValue<quint32>(locOffset, descr.fieldSize);
break;
case DataType::Map:
fieldValue = readMapValue(locOffset, descr.fieldSize);
break;
case DataType::SignedInteger32:
fieldValue = readPlainValue<qint32>(locOffset, descr.fieldSize);
break;
case DataType::Integer64:
fieldValue = readPlainValue<quint64>(locOffset, descr.fieldSize);
break;
case DataType::Integer128:
qDebug() << "* Unsupported data type: Integer128";
break;
case DataType::Array:
fieldValue = readArrayValue(locOffset, descr.fieldSize);
break;
case DataType::DataCacheContainer:
qDebug() << "* Unsupported data type: DataCacheContainer";
break;
case DataType::EndMarker:
qDebug() << "* Unsupported data type: EndMarker";
break;
case DataType::Boolean:
fieldValue = QVariant::fromValue(static_cast<bool>(descr.fieldSize));
break;
case DataType::Float:
if (descr.fieldSize == 4)
fieldValue = readPlainValue<float>(locOffset, descr.fieldSize);
else
qDebug() << "* Invalid field size for type: Float";
break;
default:
qDebug() << "* Unsupported data type: Unknown";
}
if (!usePointer)
offset = locOffset;
return fieldValue;
}
bool GeoIPDatabase::readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out) const
{
const uchar *dataPtr = m_data + offset;
int availSize = m_size - offset;
if (availSize < 1) return false;
out.fieldType = static_cast<DataType>((dataPtr[0] & 0xE0) >> 5);
if (out.fieldType == DataType::Pointer) {
int size = ((dataPtr[0] & 0x18) >> 3);
if (availSize < (size + 2)) return false;
if (size == 0)
out.offset = ((dataPtr[0] & 0x07) << 8) + dataPtr[1];
else if (size == 1)
out.offset = ((dataPtr[0] & 0x07) << 16) + (dataPtr[1] << 8) + dataPtr[2] + 2048;
else if (size == 2)
out.offset = ((dataPtr[0] & 0x07) << 24) + (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 526336;
else if (size == 3)
out.offset = (dataPtr[1] << 24) + (dataPtr[2] << 16) + (dataPtr[3] << 8) + dataPtr[4];
offset += size + 2;
return true;
}
out.fieldSize = dataPtr[0] & 0x1F;
if (out.fieldSize <= 28) {
if (out.fieldType == DataType::Unknown) {
out.fieldType = static_cast<DataType>(dataPtr[1] + 7);
if ((out.fieldType <= DataType::Map) || (out.fieldType > DataType::Float) || (availSize < 3))
return false;
offset += 2;
}
else {
offset += 1;
}
}
else if (out.fieldSize == 29) {
if (availSize < 2) return false;
out.fieldSize = dataPtr[1] + 29;
offset += 2;
}
else if (out.fieldSize == 30) {
if (availSize < 3) return false;
out.fieldSize = (dataPtr[1] << 8) + dataPtr[2] + 285;
offset += 3;
}
else if (out.fieldSize == 31) {
if (availSize < 4) return false;
out.fieldSize = (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 65821;
offset += 4;
}
return true;
}
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
{
if (__IS_LITTLE_ENDIAN__)
std::reverse(buf, buf + len);
}
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
{
QVariantHash map;
for (quint32 i = 0; i < count; ++i) {
QVariant field = readDataField(offset);
if (field.userType() != QMetaType::QString)
return QVariant();
QString key = field.toString();
field = readDataField(offset);
if (field.userType() == QVariant::Invalid)
return QVariant();
map[key] = field;
}
return map;
}
QVariant GeoIPDatabase::readArrayValue(quint32 &offset, quint32 count) const
{
QVariantList array;
for (quint32 i = 0; i < count; ++i) {
QVariant field = readDataField(offset);
if (field.userType() == QVariant::Invalid)
return QVariant();
array.append(field);
}
return array;
}
namespace
{
#ifndef QBT_USES_QT5
Q_IPV6ADDR createMappedAddress(quint32 ip4)
{
Q_IPV6ADDR ip6;
memset(&ip6, 0, sizeof(ip6));
int i;
for (i = 15; ip4 != 0; i--) {
ip6[i] = ip4 & 0xFF;
ip4 >>= 8;
}
ip6[11] = 0xFF;
ip6[10] = 0xFF;
return ip6;
}
#endif
}

View File

@@ -0,0 +1,102 @@
/*
* 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 GEOIPDATABASE_H
#define GEOIPDATABASE_H
#include <QtGlobal>
#include <QCoreApplication>
class QHostAddress;
class QString;
class QByteArray;
class QDateTime;
struct DataFieldDescriptor;
class GeoIPDatabase
{
Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase)
public:
static GeoIPDatabase *load(const QString &filename, QString &error);
static GeoIPDatabase *load(const QByteArray &data, QString &error);
~GeoIPDatabase();
QString type() const;
quint16 ipVersion() const;
QDateTime buildEpoch() const;
QString lookup(const QHostAddress &hostAddr) const;
private:
GeoIPDatabase(quint32 size);
bool parseMetadata(const QVariantHash &metadata, QString &error);
bool loadDB(QString &error) const;
QVariantHash readMetadata() const;
QVariant readDataField(quint32 &offset) const;
bool readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out) const;
void fromBigEndian(uchar *buf, quint32 len) const;
QVariant readMapValue(quint32 &offset, quint32 count) const;
QVariant readArrayValue(quint32 &offset, quint32 count) const;
template<typename T>
QVariant readPlainValue(quint32 &offset, quint8 len) const
{
T value = 0;
const uchar *const data = m_data + offset;
const quint32 availSize = m_size - offset;
if ((len > 0) && (len <= sizeof(T) && (availSize >= len))) {
// copy input data to last 'len' bytes of 'value'
uchar *dst = reinterpret_cast<uchar *>(&value) + (sizeof(T) - len);
memcpy(dst, data, len);
fromBigEndian(reinterpret_cast<uchar *>(&value), sizeof(T));
offset += len;
}
return QVariant::fromValue(value);
}
// Metadata
quint16 m_ipVersion;
quint16 m_recordSize;
quint32 m_nodeCount;
int m_nodeSize;
int m_indexSize;
int m_recordBytes;
QDateTime m_buildEpoch;
// Search data
mutable QHash<quint32, QString> m_countries;
quint32 m_size;
const uchar *m_data;
};
#endif // GEOIPDATABASE_H

View File

@@ -0,0 +1,90 @@
/*
* 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
*/
#include <QDebug>
#include <QHostInfo>
#include <QString>
#include <boost/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include "reverseresolution.h"
const int CACHE_SIZE = 500;
using namespace Net;
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
{
return (!hostname.isEmpty() && hostname != ip);
}
ReverseResolution::ReverseResolution(QObject *parent)
: QObject(parent)
{
m_cache.setMaxCost(CACHE_SIZE);
}
ReverseResolution::~ReverseResolution()
{
qDebug("Deleting host name resolver...");
}
void ReverseResolution::resolve(const QString &ip)
{
if (m_cache.contains(ip)) {
const QString &hostname = *m_cache.object(ip);
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
if (isUsefulHostName(hostname, ip))
emit ipResolved(ip, hostname);
}
else {
// Actually resolve the ip
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
}
}
void ReverseResolution::hostResolved(const QHostInfo &host)
{
const QString &ip = m_lookups.take(host.lookupId());
Q_ASSERT(!ip.isNull());
if (host.error() != QHostInfo::NoError) {
qDebug() << "DNS Reverse resolution error: " << host.errorString();
return;
}
const QString &hostname = host.hostName();
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
m_cache.insert(ip, new QString(hostname));
if (isUsefulHostName(hostname, ip))
emit ipResolved(ip, hostname);
}

View 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/base/net/smtp.cpp Normal file
View 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 "base/preferences.h"
#include "base/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 response 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);
}

125
src/base/net/smtp.h Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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)
*/
#ifndef SMTP_H
#define SMTP_H
#include <QString>
#include <QObject>
#include <QByteArray>
#include <QHash>
QT_BEGIN_NAMESPACE
class QTextStream;
#ifndef QT_NO_OPENSSL
class QSslSocket;
#else
class QTcpSocket;
#endif
class QTextCodec;
QT_END_NAMESPACE
namespace Net
{
class Smtp : public QObject
{
Q_OBJECT
public:
Smtp(QObject *parent = 0);
~Smtp();
void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body);
private slots:
void readyRead();
private:
enum States
{
Rcpt,
EhloSent,
HeloSent,
EhloDone,
EhloGreetReceived,
AuthRequestSent,
AuthSent,
AuthUsernameSent,
Authenticated,
StartTLSSent,
Data,
Init,
Body,
Quit,
Close
};
enum AuthType
{
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
QSslSocket *m_socket;
#else
QTcpSocket *m_socket;
#endif
QString m_from;
QString m_rcpt;
QString m_response;
int m_state;
QHash<QString, QString> m_extensions;
QByteArray m_buffer;
bool m_useSsl;
AuthType m_authType;
QString m_username;
QString m_password;
};
}
#endif

2585
src/base/preferences.cpp Normal file

File diff suppressed because it is too large Load Diff

559
src/base/preferences.h Normal file
View File

@@ -0,0 +1,559 @@
/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
* Copyright (C) 2014 sledgehammer999
*
* 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
* Contact : hammered999@gmail.com
*/
#ifndef PREFERENCES_H
#define PREFERENCES_H
#include <QTime>
#include <QDateTime>
#include <QList>
#include <QTimer>
#include <QReadWriteLock>
#include <QNetworkCookie>
#include <QVariant>
#include "base/types.h"
enum scheduler_days
{
EVERY_DAY,
WEEK_DAYS,
WEEK_ENDS,
MON,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
namespace Proxy
{
enum ProxyType
{
HTTP = 1,
SOCKS5 = 2,
HTTP_PW = 3,
SOCKS5_PW = 4,
SOCKS4 = 5
};
}
namespace TrayIcon
{
enum Style
{
NORMAL = 0,
MONO_DARK,
MONO_LIGHT
};
}
namespace DNS
{
enum Service
{
DYNDNS,
NOIP,
NONE = -1
};
}
class Preferences: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Preferences)
private:
Preferences();
~Preferences();
static Preferences* m_instance;
QHash<QString, QVariant> m_data;
int m_randomPort;
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);
private slots:
bool save();
signals:
void changed();
void externalLabelAdded(QString&);
public:
static void initInstance();
static void freeInstance();
static Preferences* instance();
// General options
QString getLocale() const;
void setLocale(const QString &locale);
bool useProgramNotification() const;
void useProgramNotification(bool use);
bool deleteTorrentFilesAsDefault() const;
void setDeleteTorrentFilesAsDefault(bool del);
bool confirmOnExit() const;
void setConfirmOnExit(bool confirm);
bool speedInTitleBar() const;
void showSpeedInTitleBar(bool show);
bool useAlternatingRowColors() const;
void setAlternatingRowColors(bool b);
bool useRandomPort() const;
void setRandomPort(bool b);
bool systrayIntegration() const;
void setSystrayIntegration(bool enabled);
bool isToolbarDisplayed() const;
void setToolbarDisplayed(bool displayed);
bool minimizeToTray() const;
void setMinimizeToTray(bool b);
bool closeToTray() const;
void setCloseToTray(bool b);
bool startMinimized() const;
void setStartMinimized(bool b);
bool isSplashScreenDisabled() const;
void setSplashScreenDisabled(bool b);
bool preventFromSuspend() const;
void setPreventFromSuspend(bool b);
#ifdef Q_OS_WIN
bool WinStartup() const;
void setWinStartup(bool b);
#endif
// Downloads
QString getSavePath() const;
void setSavePath(const QString &save_path);
bool isTempPathEnabled() const;
void setTempPathEnabled(bool enabled);
QString getTempPath() const;
void setTempPath(const QString &path);
QString getDefaultLabel() const;
void setDefaultLabel(const QString &defaultLabel);
bool useIncompleteFilesExtension() const;
void useIncompleteFilesExtension(bool enabled);
bool appendTorrentLabel() const;
void setAppendTorrentLabel(bool b);
QString lastLocationPath() const;
void setLastLocationPath(const QString &path);
bool preAllocateAllFiles() const;
void preAllocateAllFiles(bool enabled);
bool useAdditionDialog() const;
void useAdditionDialog(bool b);
bool additionDialogFront() const;
void additionDialogFront(bool b);
bool addTorrentsInPause() const;
void addTorrentsInPause(bool b);
QStringList getScanDirs() const;
void setScanDirs(const QStringList &dirs);
QList<bool> getDownloadInScanDirs() const;
void setDownloadInScanDirs(const QList<bool> &list);
QString getScanDirsLastPath() const;
void setScanDirsDownloadPaths(const QStringList &downloadpaths);
QStringList getScanDirsDownloadPaths() const;
void setScanDirsLastPath(const QString &path);
bool isTorrentExportEnabled() const;
QString getTorrentExportDir() const;
void setTorrentExportDir(QString path);
bool isFinishedTorrentExportEnabled() const;
QString getFinishedTorrentExportDir() const;
void setFinishedTorrentExportDir(QString path);
bool isMailNotificationEnabled() const;
void setMailNotificationEnabled(bool enabled);
QString getMailNotificationEmail() const;
void setMailNotificationEmail(const QString &mail);
QString getMailNotificationSMTP() const;
void setMailNotificationSMTP(const QString &smtp_server);
bool getMailNotificationSMTPSSL() const;
void setMailNotificationSMTPSSL(bool use);
bool getMailNotificationSMTPAuth() const;
void setMailNotificationSMTPAuth(bool use);
QString getMailNotificationSMTPUsername() const;
void setMailNotificationSMTPUsername(const QString &username);
QString getMailNotificationSMTPPassword() const;
void setMailNotificationSMTPPassword(const QString &password);
int getActionOnDblClOnTorrentDl() const;
void setActionOnDblClOnTorrentDl(int act);
int getActionOnDblClOnTorrentFn() const;
void setActionOnDblClOnTorrentFn(int act);
// Connection options
int getSessionPort() const;
void setSessionPort(int port);
bool isUPnPEnabled() const;
void setUPnPEnabled(bool enabled);
int getGlobalDownloadLimit() const;
void setGlobalDownloadLimit(int limit);
int getGlobalUploadLimit() const;
void setGlobalUploadLimit(int limit);
int getAltGlobalDownloadLimit() const;
void setAltGlobalDownloadLimit(int limit);
int getAltGlobalUploadLimit() const;
void setAltGlobalUploadLimit(int limit);
bool isAltBandwidthEnabled() const;
void setAltBandwidthEnabled(bool enabled);
bool isSchedulerEnabled() const;
void setSchedulerEnabled(bool enabled);
QTime getSchedulerStartTime() const;
void setSchedulerStartTime(const QTime &time);
QTime getSchedulerEndTime() const;
void setSchedulerEndTime(const QTime &time);
scheduler_days getSchedulerDays() const;
void setSchedulerDays(scheduler_days days);
// Proxy options
bool isProxyEnabled() const;
bool isProxyAuthEnabled() const;
void setProxyAuthEnabled(bool enabled);
QString getProxyIp() const;
void setProxyIp(const QString &ip);
unsigned short getProxyPort() const;
void setProxyPort(unsigned short port);
QString getProxyUsername() const;
void setProxyUsername(const QString &username);
QString getProxyPassword() const;
void setProxyPassword(const QString &password);
int getProxyType() const;
void setProxyType(int type);
bool proxyPeerConnections() const;
void setProxyPeerConnections(bool enabled);
bool getForceProxy() const;
void setForceProxy(bool enabled);
void setProxyOnlyForTorrents(bool enabled);
bool isProxyOnlyForTorrents() const;
// Bittorrent options
int getMaxConnecs() const;
void setMaxConnecs(int val);
int getMaxConnecsPerTorrent() const;
void setMaxConnecsPerTorrent(int val);
int getMaxUploads() const;
void setMaxUploads(int val);
int getMaxUploadsPerTorrent() const;
void setMaxUploadsPerTorrent(int val);
bool isuTPEnabled() const;
void setuTPEnabled(bool enabled);
bool isuTPRateLimited() const;
void setuTPRateLimited(bool enabled);
bool isDHTEnabled() const;
void setDHTEnabled(bool enabled);
bool isPeXEnabled() const;
void setPeXEnabled(bool enabled);
bool isLSDEnabled() const;
void setLSDEnabled(bool enabled);
int getEncryptionSetting() const;
void setEncryptionSetting(int val);
bool isAddTrackersEnabled() const;
void setAddTrackersEnabled(bool enabled);
QString getTrackersList() const;
void setTrackersList(const QString &val);
qreal getGlobalMaxRatio() const;
void setGlobalMaxRatio(qreal ratio);
MaxRatioAction getMaxRatioAction() const;
void setMaxRatioAction(MaxRatioAction act);
// IP Filter
bool isFilteringEnabled() const;
void setFilteringEnabled(bool enabled);
bool isFilteringTrackerEnabled() const;
void setFilteringTrackerEnabled(bool enabled);
QString getFilter() const;
void setFilter(const QString &path);
QStringList bannedIPs() const;
void banIP(const QString &ip);
// Search
bool isSearchEnabled() const;
void setSearchEnabled(bool enabled);
// Execution Log
bool isExecutionLogEnabled() const;
void setExecutionLogEnabled(bool b);
// Queueing system
bool isQueueingSystemEnabled() const;
void setQueueingSystemEnabled(bool enabled);
int getMaxActiveDownloads() const;
void setMaxActiveDownloads(int val);
int getMaxActiveUploads() const;
void setMaxActiveUploads(int val);
int getMaxActiveTorrents() const;
void setMaxActiveTorrents(int val);
bool ignoreSlowTorrentsForQueueing() const;
void setIgnoreSlowTorrentsForQueueing(bool ignore);
bool isWebUiEnabled() const;
void setWebUiEnabled(bool enabled);
bool isWebUiLocalAuthEnabled() const;
void setWebUiLocalAuthEnabled(bool enabled);
quint16 getWebUiPort() const;
void setWebUiPort(quint16 port);
bool useUPnPForWebUIPort() const;
void setUPnPForWebUIPort(bool enabled);
QString getWebUiUsername() const;
void setWebUiUsername(const QString &username);
QString getWebUiPassword() const;
void setWebUiPassword(const QString &new_password);
bool isWebUiHttpsEnabled() const;
void setWebUiHttpsEnabled(bool enabled);
QByteArray getWebUiHttpsCertificate() const;
void setWebUiHttpsCertificate(const QByteArray &data);
QByteArray getWebUiHttpsKey() const;
void setWebUiHttpsKey(const QByteArray &data);
bool isDynDNSEnabled() const;
void setDynDNSEnabled(bool enabled);
DNS::Service getDynDNSService() const;
void setDynDNSService(int service);
QString getDynDomainName() const;
void setDynDomainName(const QString &name);
QString getDynDNSUsername() const;
void setDynDNSUsername(const QString &username);
QString getDynDNSPassword() const;
void setDynDNSPassword(const QString &password);
// Advanced settings
void setUILockPassword(const QString &clear_password);
void clearUILockPassword();
QString getUILockPasswordMD5() const;
bool isUILocked() const;
void setUILocked(bool locked);
bool isAutoRunEnabled() const;
void setAutoRunEnabled(bool enabled);
QString getAutoRunProgram() const;
void setAutoRunProgram(const QString &program);
bool shutdownWhenDownloadsComplete() const;
void setShutdownWhenDownloadsComplete(bool shutdown);
bool suspendWhenDownloadsComplete() const;
void setSuspendWhenDownloadsComplete(bool suspend);
bool hibernateWhenDownloadsComplete() const;
void setHibernateWhenDownloadsComplete(bool hibernate);
bool shutdownqBTWhenDownloadsComplete() const;
void setShutdownqBTWhenDownloadsComplete(bool shutdown);
uint diskCacheSize() const;
void setDiskCacheSize(uint size);
uint diskCacheTTL() const;
void setDiskCacheTTL(uint ttl);
bool osCache() const;
void setOsCache(bool enable);
uint saveResumeDataInterval() const;
void setSaveResumeDataInterval(uint m);
uint outgoingPortsMin() const;
void setOutgoingPortsMin(uint val);
uint outgoingPortsMax() const;
void setOutgoingPortsMax(uint val);
bool getIgnoreLimitsOnLAN() const;
void setIgnoreLimitsOnLAN(bool ignore);
bool includeOverheadInLimits() const;
void includeOverheadInLimits(bool include);
bool trackerExchangeEnabled() const;
void setTrackerExchangeEnabled(bool enable);
bool recheckTorrentsOnCompletion() const;
void recheckTorrentsOnCompletion(bool recheck);
unsigned int getRefreshInterval() const;
void setRefreshInterval(uint interval);
bool resolvePeerCountries() const;
void resolvePeerCountries(bool resolve);
bool resolvePeerHostNames() const;
void resolvePeerHostNames(bool resolve);
int getMaxHalfOpenConnections() const;
void setMaxHalfOpenConnections(int value);
QString getNetworkInterface() const;
void setNetworkInterface(const QString& iface);
QString getNetworkInterfaceName() const;
void setNetworkInterfaceName(const QString& iface);
bool getListenIPv6() const;
void setListenIPv6(bool enable);
QString getNetworkAddress() const;
void setNetworkAddress(const QString& addr);
bool isAnonymousModeEnabled() const;
void enableAnonymousMode(bool enabled);
bool isSuperSeedingEnabled() const;
void enableSuperSeeding(bool enabled);
bool announceToAllTrackers() const;
void setAnnounceToAllTrackers(bool enabled);
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
bool useSystemIconTheme() const;
void useSystemIconTheme(bool enabled);
#endif
QStringList getTorrentLabels() const;
void setTorrentLabels(const QStringList& labels);
void addTorrentLabelExternal(const QString &label);
void addTorrentLabel(const QString& label);
void removeTorrentLabel(const QString& label);
bool recursiveDownloadDisabled() const;
void disableRecursiveDownload(bool disable = true);
#ifdef Q_OS_WIN
static QString getPythonPath();
bool neverCheckFileAssoc() const;
void setNeverCheckFileAssoc(bool check = true);
static bool isTorrentFileAssocSet();
static bool isMagnetLinkAssocSet();
static void setTorrentFileAssoc(bool set);
static void setMagnetLinkAssoc(bool set);
#endif
bool isTrackerEnabled() const;
void setTrackerEnabled(bool enabled);
int getTrackerPort() const;
void setTrackerPort(int port);
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
bool isUpdateCheckEnabled() const;
void setUpdateCheckEnabled(bool enabled);
#endif
bool confirmTorrentDeletion() const;
void setConfirmTorrentDeletion(bool enabled);
bool confirmTorrentRecheck() const;
void setConfirmTorrentRecheck(bool enabled);
TrayIcon::Style trayIconStyle() const;
void setTrayIconStyle(TrayIcon::Style style);
// Stuff that don't appear in the Options GUI but are saved
// in the same file.
QByteArray getAddNewTorrentDialogState() const;
void setAddNewTorrentDialogState(const QByteArray &state);
int getAddNewTorrentDialogPos() const;
void setAddNewTorrentDialogPos(const int &pos);
int getAddNewTorrentDialogWidth() const;
void setAddNewTorrentDialogWidth(const int &width);
bool getAddNewTorrentDialogExpanded() const;
void setAddNewTorrentDialogExpanded(const bool expanded);
QStringList getAddNewTorrentDialogPathHistory() const;
void setAddNewTorrentDialogPathHistory(const QStringList &history);
QDateTime getDNSLastUpd() const;
void setDNSLastUpd(const QDateTime &date);
QString getDNSLastIP() const;
void setDNSLastIP(const QString &ip);
bool getAcceptedLegal() const;
void setAcceptedLegal(const bool accepted);
QByteArray getMainGeometry() const;
void setMainGeometry(const QByteArray &geometry);
QByteArray getMainVSplitterState() const;
void setMainVSplitterState(const QByteArray &state);
QString getMainLastDir() const;
void setMainLastDir(const QString &path);
#ifndef DISABLE_GUI
QSize getPrefSize(const QSize &defaultSize) const;
void setPrefSize(const QSize &size);
#endif
QPoint getPrefPos() const;
void setPrefPos(const QPoint &pos);
QStringList getPrefHSplitterSizes() const;
void setPrefHSplitterSizes(const QStringList &sizes);
QByteArray getPeerListState() const;
void setPeerListState(const QByteArray &state);
QString getPropSplitterSizes() const;
void setPropSplitterSizes(const QString &sizes);
QByteArray getPropFileListState() const;
void setPropFileListState(const QByteArray &state);
int getPropCurTab() const;
void setPropCurTab(const int &tab);
bool getPropVisible() const;
void setPropVisible(const bool visible);
QByteArray getPropTrackerListState() const;
void setPropTrackerListState(const QByteArray &state);
QByteArray getRssGeometry() const;
void setRssGeometry(const QByteArray &geometry);
QByteArray getRssHSplitterSizes() const;
void setRssHSplitterSizes(const QByteArray &sizes);
QStringList getRssOpenFolders() const;
void setRssOpenFolders(const QStringList &folders);
QByteArray getRssHSplitterState() const;
void setRssHSplitterState(const QByteArray &state);
QByteArray getRssVSplitterState() const;
void setRssVSplitterState(const QByteArray &state);
QString getSearchColsWidth() const;
void setSearchColsWidth(const QString &width);
QStringList getSearchEngDisabled() const;
void setSearchEngDisabled(const QStringList &engines);
QString getCreateTorLastAddPath() const;
void setCreateTorLastAddPath(const QString &path);
QString getCreateTorLastSavePath() const;
void setCreateTorLastSavePath(const QString &path);
QString getCreateTorTrackers() const;
void setCreateTorTrackers(const QString &path);
QByteArray getCreateTorGeometry() const;
void setCreateTorGeometry(const QByteArray &geometry);
bool getCreateTorIgnoreRatio() const;
void setCreateTorIgnoreRatio(const bool ignore);
QString getTorImportLastContentDir() const;
void setTorImportLastContentDir(const QString &path);
QByteArray getTorImportGeometry() const;
void setTorImportGeometry(const QByteArray &geometry);
bool getStatusFilterState() const;
bool getLabelFilterState() const;
bool getTrackerFilterState() const;
int getTransSelFilter() const;
void setTransSelFilter(const int &index);
QByteArray getTransHeaderState() const;
void setTransHeaderState(const QByteArray &state);
int getToolbarTextPosition() const;
void setToolbarTextPosition(const int position);
// Temp code.
// See TorrentStatistics::loadStats() for details.
QVariantHash getStats() const;
void removeStats();
//From old RssSettings class
bool isRSSEnabled() const;
void setRSSEnabled(const bool enabled);
uint getRSSRefreshInterval() const;
void setRSSRefreshInterval(const uint &interval);
int getRSSMaxArticlesPerFeed() const;
void setRSSMaxArticlesPerFeed(const int &nb);
bool isRssDownloadingEnabled() const;
void setRssDownloadingEnabled(const bool b);
QStringList getRssFeedsUrls() const;
void setRssFeedsUrls(const QStringList &rssFeeds);
QStringList getRssFeedsAliases() const;
void setRssFeedsAliases(const QStringList &rssAliases);
QList<QByteArray> getHostNameCookies(const QString &host_name) const;
QList<QNetworkCookie> getHostNameQNetworkCookies(const QString& host_name) const;
void setHostNameCookies(const QString &host_name, const QList<QByteArray> &cookies);
// SpeedWidget
int getSpeedWidgetPeriod() const;
void setSpeedWidgetPeriod(const int period);
bool getSpeedWidgetGraphEnable(int id) const;
void setSpeedWidgetGraphEnable(int id, const bool enable);
public slots:
void setStatusFilterState(bool checked);
void setLabelFilterState(bool checked);
void setTrackerFilterState(bool checked);
void apply();
};
#endif // PREFERENCES_H

73
src/base/qinisettings.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* 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 QINISETTINGS_H
#define QINISETTINGS_H
#include <QSettings>
class QIniSettings : public QSettings {
Q_OBJECT
Q_DISABLE_COPY (QIniSettings)
public:
QIniSettings(const QString &organization = "qBittorrent", const QString &application = "qBittorrent", QObject *parent = 0 ):
#ifdef Q_OS_WIN
QSettings(QSettings::IniFormat, QSettings::UserScope, organization, application, parent)
#else
QSettings(organization, application, parent)
#endif
{
}
QIniSettings(const QString &fileName, Format format, QObject *parent = 0 ) : QSettings(fileName, format, parent) {
}
#ifdef Q_OS_WIN
QVariant value(const QString & key, const QVariant &defaultValue = QVariant()) const {
QString key_tmp(key);
QVariant ret = QSettings::value(key_tmp);
if (ret.isNull())
return defaultValue;
return ret;
}
void setValue(const QString &key, const QVariant &val) {
QString key_tmp(key);
if (format() == QSettings::NativeFormat) // Using registry, don't touch replace here
key_tmp.replace("\\", "/");
QSettings::setValue(key_tmp, val);
}
#endif
};
#endif // QINISETTINGS_H

View File

@@ -0,0 +1,373 @@
/*
* 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;
const int DownloadPath = 2;
}
class ScanFoldersModel::PathData
{
public:
PathData(const QString &path)
: path(path)
, downloadAtPath(false)
, downloadPath(path)
{
}
PathData(const QString &path, bool downloadAtPath, const QString &downloadPath)
: path(path)
, downloadAtPath(downloadAtPath)
, downloadPath(downloadPath)
{
}
const QString path; //watching directory
bool downloadAtPath; //if TRUE save data to watching directory
QString downloadPath; //if 'downloadAtPath' FALSE use this path for save data
};
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 3;
}
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());
QVariant value;
switch (index.column()) {
case PathColumn:
if (role == Qt::DisplayRole)
value = Utils::Fs::toNativePath(pathData->path);
break;
case DownloadAtTorrentColumn:
if (role == Qt::CheckStateRole)
value = pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked;
break;
case DownloadPath:
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole)
value = Utils::Fs::toNativePath(pathData->downloadPath);
break;
}
return value;
}
QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount()))
return QVariant();
QVariant title;
switch (section) {
case PathColumn:
title = tr("Watched Folder");
break;
case DownloadAtTorrentColumn:
title = tr("Download here");
break;
case DownloadPath:
title = tr("Download path");
break;
}
return title;
}
Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const
{
if (!index.isValid() || (index.row() >= rowCount()))
return QAbstractTableModel::flags(index);
const PathData *pathData = m_pathList.at(index.row());
Qt::ItemFlags flags;
switch (index.column()) {
case PathColumn:
flags = QAbstractTableModel::flags(index);
break;
case DownloadAtTorrentColumn:
flags = QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable;
break;
case DownloadPath:
if (pathData->downloadAtPath == false)
flags = QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled;
else
flags = QAbstractTableModel::flags(index) ^ Qt::ItemIsEnabled; //dont edit DownloadPath if checked 'downloadAtPath'
break;
}
return flags;
}
bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadPath))
return false;
bool success = true;
switch (index.column()) {
case PathColumn:
success = false;
break;
case DownloadAtTorrentColumn:
if (role == Qt::CheckStateRole) {
Q_ASSERT(index.column() == DownloadAtTorrentColumn);
m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked);
emit dataChanged(index, index);
success = true;
}
break;
case DownloadPath:
Q_ASSERT(index.column() == DownloadPath);
m_pathList[index.row()]->downloadPath = value.toString();
emit dataChanged(index, index);
success = true;
break;
}
return success;
}
ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath, const QString &downloadPath)
{
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());
QString downloadToPath = downloadPath.isEmpty() ? path : downloadPath;
m_pathList << new PathData(canonicalPath, downloadAtPath, downloadToPath);
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;
}
QString ScanFoldersModel::downloadPathTorrentFolder(const QString &filePath) const
{
const int row = findPathData(QFileInfo(filePath).dir().path());
Q_ASSERT(row != -1);
return m_pathList.at(row)->downloadPath;
}
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;
QStringList downloadPaths;
foreach (const PathData *pathData, m_pathList) {
paths << pathData->path;
downloadInFolderInfo << pathData->downloadAtPath;
downloadPaths << pathData->downloadPath;
}
pref->setScanDirs(paths);
pref->setDownloadInScanDirs(downloadInFolderInfo);
pref->setScanDirsDownloadPaths(downloadPaths);
}
void ScanFoldersModel::configure()
{
Preferences *const pref = Preferences::instance();
int i = 0;
QStringList downloadPaths = pref->getScanDirsDownloadPaths();
QList<bool> downloadInDirList = pref->getDownloadInScanDirs();
foreach (const QString &dir, pref->getScanDirs()) {
bool downloadInDir = downloadInDirList.value(i, true);
QString downloadPath = downloadPaths.value(i); //empty string if out-of-bounds
addPath(dir, downloadInDir, downloadPath);
++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();
else
params.savePath = downloadPathTorrentFolder(file); //if empty it will use the default savePath
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));
}
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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, const QString &downloadPath);
void removePath(int row);
bool removePath(const QString &path);
PathStatus setDownloadAtPath(int row, bool downloadAtPath);
bool downloadInTorrentFolder(const QString &filePath) const;
QString downloadPathTorrentFolder(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

168
src/base/torrentfilter.cpp Normal file
View File

@@ -0,0 +1,168 @@
/*
* 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);
const TorrentFilter TorrentFilter::ErroredTorrent(TorrentFilter::Errored);
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;
else if (filter == "errored")
type = Errored;
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)
{
// QString::operator==() doesn't distinguish between empty and null strings.
if ((m_label != label)
|| (m_label.isNull() && !label.isNull())
|| (!m_label.isNull() && label.isNull())) {
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();
case Errored:
return torrent->isErrored();
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.isNull()) return true;
else if (m_label.isEmpty()) return torrent->label().isEmpty();
else return (torrent->label() == m_label);
}

95
src/base/torrentfilter.h Normal file
View File

@@ -0,0 +1,95 @@
/*
* 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,
Errored
};
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;
static const TorrentFilter ErroredTorrent;
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

60
src/base/tristatebool.cpp Normal file
View 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;
}

53
src/base/tristatebool.h Normal file
View 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 TRISTATEBOOL_H
#define TRISTATEBOOL_H
class TriStateBool
{
public:
enum ValueType
{
Undefined = -1,
False = 0,
True = 1
};
TriStateBool();
TriStateBool(bool b);
TriStateBool(ValueType value);
operator ValueType() const;
operator bool() const;
private:
ValueType m_value;
};
#endif // TRISTATEBOOL_H

74
src/base/types.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* 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>
#include <QDataStream>
const qlonglong MAX_ETA = 8640000;
enum class MaxRatioAction
{
Pause,
Remove
};
Q_DECLARE_METATYPE(MaxRatioAction)
enum class TorrentExportFolder
{
Regular,
Finished
};
enum class ShutdownAction
{
None,
Shutdown,
Suspend,
Hibernate
};
template<typename T>
inline QDataStream &operator<<(QDataStream &out, const T &val)
{
return (out << static_cast<int>(val));
}
template<typename T>
inline QDataStream &operator>>(QDataStream &in, T &val)
{
int tmp;
in >> tmp;
val = static_cast<T>(tmp);
return in;
}
#endif // TYPES_H

86
src/base/unicodestrings.h Normal file
View File

@@ -0,0 +1,86 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Mike Tzou
*
* 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.
*/
// This file must be encoded in "UTF-8 with BOM"
#ifdef _MSC_VER
#pragma execution_character_set("utf-8")
#endif
// Because of the poor handling of UTF-8 characters in MSVC (emits warning C4819),
// we put all problematic UTF-8 chars/strings in this file.
// See issue #3059 for more details (https://github.com/qbittorrent/qBittorrent/issues/3059).
const char C_INFINITY[] = "";
const char C_UP[] = "";
const char C_DOWN[] = "";
const char C_COPYRIGHT[] = "©";
const char C_UTP[] = "μTP";
const char C_LOCALE_ENGLISH[] = "English";
const char C_LOCALE_ENGLISH_AUSTRALIA[] = "English(Australia)";
const char C_LOCALE_ENGLISH_UNITEDKINGDOM[] = "English(United Kingdom)";
const char C_LOCALE_ESPERANTO[] = "Esperanto";
const char C_LOCALE_FRENCH[] = "Français";
const char C_LOCALE_GERMAN[] = "Deutsch";
const char C_LOCALE_HUNGARIAN[] = "Magyar";
const char C_LOCALE_INDONESIAN[] = "Bahasa Indonesia";
const char C_LOCALE_ITALIAN[] = "Italiano";
const char C_LOCALE_DUTCH[] = "Nederlands";
const char C_LOCALE_SPANISH[] = "Español";
const char C_LOCALE_CATALAN[] = "Català";
const char C_LOCALE_GALICIAN[] = "Galego";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_LITHUANIAN[] = "Lietuvių";
const char C_LOCALE_CZECH[] = "Čeština";
const char C_LOCALE_SLOVAK[] = "Slovenčina";
const char C_LOCALE_SLOVENIAN[] = "Slovenščina";
const char C_LOCALE_SERBIAN[] = "Српски";
const char C_LOCALE_CROATIAN[] = "Hrvatski";
const char C_LOCALE_ARMENIAN[] = "Հայերեն";
const char C_LOCALE_ROMANIAN[] = "Română";
const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_GREEK[] = "Ελληνικά";
const char C_LOCALE_SWEDISH[] = "Svenska";
const char C_LOCALE_FINNISH[] = "Suomi";
const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_BULGARIAN[] = "Български";
const char C_LOCALE_UKRAINIAN[] = "Українська";
const char C_LOCALE_RUSSIAN[] = "Русский";
const char C_LOCALE_JAPANESE[] = "日本語";
const char C_LOCALE_HEBREW[] = "עברית";
const char C_LOCALE_HINDI[] = "हिन्दी, हिंदी";
const char C_LOCALE_ARABIC[] = "عربي";
const char C_LOCALE_GEORGIAN[] = "ქართული";
const char C_LOCALE_BYELORUSSIAN[] = "Беларуская";
const char C_LOCALE_BASQUE[] = "Euskara";
const char C_LOCALE_VIETNAMESE[] = "tiếng Việt";
const char C_LOCALE_CHINESE_TRADITIONAL_TW[] = "正體中文 (臺灣)";
const char C_LOCALE_CHINESE_TRADITIONAL_HK[] = "繁體中文 (香港)";
const char C_LOCALE_CHINESE_SIMPLIFIED[] = "简体中文";
const char C_LOCALE_KOREAN[] = "한글";

531
src/base/utils/fs.cpp Normal file
View File

@@ -0,0 +1,531 @@
/*
* 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 <QDirIterator>
#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
#ifndef QBT_USES_QT5
#ifndef DISABLE_GUI
#include <QDesktopServices>
#endif
#else
#include <QStandardPaths>
#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);
}
/**
* This function will first remove system cache files, e.g. `Thumbs.db`,
* `.DS_Store`. Then will try to remove the whole tree if the tree consist
* only of folders
*/
bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& path)
{
if (!QDir(path).exists())
return false;
static const QStringList deleteFilesList = {
// Windows
"Thumbs.db",
"desktop.ini",
// Linux
".directory",
// Mac OS
".DS_Store"
};
// travel from the deepest folder and remove anything unwanted on the way out.
QStringList dirList(path + "/"); // get all sub directories paths
QDirIterator iter(path, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
while (iter.hasNext())
dirList << iter.next() + "/";
std::sort(dirList.begin(), dirList.end(), [](const QString &l, const QString &r) { return l.count("/") > r.count("/"); }); // sort descending by directory depth
for (const QString &p : dirList) {
// remove unwanted files
for (const QString &f : deleteFilesList) {
forceRemove(p + f);
}
// remove temp files on linux (file ends with '~'), e.g. `filename~`
QDir dir(p);
QStringList tmpFileList = dir.entryList(QDir::Files);
for (const QString &f : tmpFileList) {
if (f.endsWith("~"))
forceRemove(p + f);
}
// remove directory if empty
dir.rmdir(p);
}
return QDir(path).exists();
}
/**
* 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();
}
/**
* Removes directory and its content recursively.
*
*/
void Utils::Fs::removeDirRecursive(const QString& dirName)
{
#ifdef QBT_USES_QT5
QDir(dirName).removeRecursively();
#else
QDir dir(dirName);
if (!dir.exists()) return;
Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot |
QDir::System |
QDir::Hidden |
QDir::AllDirs |
QDir::Files, QDir::DirsFirst)) {
if (info.isDir()) removeDirRecursive(info.absoluteFilePath());
else forceRemove(info.absoluteFilePath());
}
dir.rmdir(dirName);
#endif
}
/**
* 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(const QString &filename)
{
static const QRegExp regex("[\\\\/:?\"*<>|]");
QString validName = filename.trimmed();
validName.replace(regex, " ");
qDebug() << "toValidFileSystemName:" << filename << "=>" << validName;
return validName;
}
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()
{
#ifdef QBT_USES_QT5
#if defined(Q_OS_WIN)
if (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
#endif
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
#else
#if defined(Q_OS_OS2)
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
#endif
#if defined(Q_OS_WIN)
// 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 (QSysInfo::windowsVersion() <= QSysInfo::WV_XP) // Windows XP
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
QCoreApplication::translate("fsutils", "Downloads"));
else
return QDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)).absoluteFilePath("Downloads");
#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
#if defined(Q_OS_MAC)
// TODO: How to support this on Mac OS?
#endif
// Fallback
return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
#endif
}
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;
}

73
src/base/utils/fs.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* 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
*/
#ifndef UTILS_FS_H
#define UTILS_FS_H
/**
* Utility functions related to file system.
*/
#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(const 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& path);
bool forceRemove(const QString& file_path);
void removeDirRecursive(const QString& dirName);
/* Ported from Qt4 to drop dependency on QtGui */
QString QDesktopServicesDataLocation();
QString QDesktopServicesCacheLocation();
QString QDesktopServicesDownloadLocation();
/* End of Qt4 code */
QString searchEngineLocation();
QString cacheLocation();
}
}
#endif // UTILS_FS_H

142
src/base/utils/gzip.cpp Normal file
View File

@@ -0,0 +1,142 @@
/*
* 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 <zlib.h>
#include "gzip.h"
bool Utils::Gzip::compress(QByteArray src, QByteArray &dest)
{
static const int BUFSIZE = 128 * 1024;
char tmpBuf[BUFSIZE];
int ret;
dest.clear();
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.next_in = reinterpret_cast<uchar *>(src.data());
strm.avail_in = src.length();
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
// windowBits = 15 + 16 to enable gzip
// From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits
// to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
return false;
while (strm.avail_in != 0) {
ret = deflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK)
return false;
if (strm.avail_out == 0) {
dest.append(tmpBuf, BUFSIZE);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
}
int deflateRes = Z_OK;
while (deflateRes == Z_OK) {
if (strm.avail_out == 0) {
dest.append(tmpBuf, BUFSIZE);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
deflateRes = deflate(&strm, Z_FINISH);
}
if (deflateRes != Z_STREAM_END)
return false;
dest.append(tmpBuf, BUFSIZE - strm.avail_out);
deflateEnd(&strm);
return true;
}
bool Utils::Gzip::uncompress(QByteArray src, QByteArray &dest)
{
dest.clear();
if (src.size() <= 4) {
qWarning("uncompress: Input data is truncated");
return false;
}
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 = static_cast<uint>(src.size());
strm.next_in = reinterpret_cast<uchar *>(src.data());
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 false;
// run inflate()
do {
strm.avail_out = CHUNK_SIZE;
strm.next_out = reinterpret_cast<uchar *>(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:
inflateEnd(&strm);
return false;
}
dest.append(out, CHUNK_SIZE - strm.avail_out);
}
while (!strm.avail_out);
// clean up and return
inflateEnd(&strm);
return true;
}

44
src/base/utils/gzip.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* 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_GZIP_H
#define UTILS_GZIP_H
class QByteArray;
namespace Utils
{
namespace Gzip
{
bool compress(QByteArray src, QByteArray &dest);
bool uncompress(QByteArray src, QByteArray &dest);
}
}
#endif // UTILS_GZIP_H

651
src/base/utils/misc.cpp Normal file
View File

@@ -0,0 +1,651 @@
/*
* 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
*/
#include <QUrl>
#include <QDir>
#include <QFileInfo>
#include <QDateTime>
#include <QByteArray>
#include <QDebug>
#include <QProcess>
#include <QSettings>
#include <QThread>
#ifdef DISABLE_GUI
#include <QCoreApplication>
#else
#include <QApplication>
#include <QDesktopWidget>
#include <QStyle>
#endif
#ifdef Q_OS_WIN
#include <windows.h>
#include <PowrProf.h>
const int UNLEN = 256;
#else
#include <unistd.h>
#include <sys/types.h>
#endif
#ifdef Q_OS_MAC
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>
#endif
#ifndef DISABLE_GUI
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
#include <QDBusInterface>
#include <QDBusMessage>
#endif
#endif // DISABLE_GUI
#ifndef DISABLE_GUI
#include <QDesktopServices>
#include <QProcess>
#endif
#include "base/utils/string.h"
#include "base/unicodestrings.h"
#include "base/logger.h"
#include "misc.h"
#include "fs.h"
static struct { const char *source; const char *comment; } units[] = {
QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
};
#ifndef DISABLE_GUI
void Utils::Misc::shutdownComputer(ShutdownAction action)
{
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB)
// Use dbus to power off / suspend the system
if (action != ShutdownAction::Shutdown) {
// Some recent systems use systemd's logind
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
if (login1Iface.isValid()) {
if (action == ShutdownAction::Suspend)
login1Iface.call("Suspend", false);
else
login1Iface.call("Hibernate", false);
return;
}
// Else, other recent systems use UPower
QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower",
"org.freedesktop.UPower", QDBusConnection::systemBus());
if (upowerIface.isValid()) {
if (action == ShutdownAction::Suspend)
upowerIface.call("Suspend");
else
upowerIface.call("Hibernate");
return;
}
// HAL (older systems)
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
"org.freedesktop.Hal.Device.SystemPowerManagement",
QDBusConnection::systemBus());
if (action == ShutdownAction::Suspend)
halIface.call("Suspend", 5);
else
halIface.call("Hibernate");
}
else {
// Some recent systems use systemd's logind
QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", QDBusConnection::systemBus());
if (login1Iface.isValid()) {
login1Iface.call("PowerOff", false);
return;
}
// Else, other recent systems use ConsoleKit
QDBusInterface consolekitIface("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager",
"org.freedesktop.ConsoleKit.Manager", QDBusConnection::systemBus());
if (consolekitIface.isValid()) {
consolekitIface.call("Stop");
return;
}
// HAL (older systems)
QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer",
"org.freedesktop.Hal.Device.SystemPowerManagement",
QDBusConnection::systemBus());
halIface.call("Shutdown");
}
#endif
#ifdef Q_OS_MAC
AEEventID EventToSend;
if (action != ShutdownAction::Shutdown)
EventToSend = kAESleep;
else
EventToSend = kAEShutDown;
AEAddressDesc targetDesc;
static const ProcessSerialNumber kPSNOfSystemProcess = { 0, kSystemProcess };
AppleEvent eventReply = {typeNull, NULL};
AppleEvent appleEventToSend = {typeNull, NULL};
OSStatus error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess,
sizeof(kPSNOfSystemProcess), &targetDesc);
if (error != noErr)
return;
error = AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc,
kAutoGenerateReturnID, kAnyTransactionID, &appleEventToSend);
AEDisposeDesc(&targetDesc);
if (error != noErr)
return;
error = AESend(&appleEventToSend, &eventReply, kAENoReply,
kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
AEDisposeDesc(&appleEventToSend);
if (error != noErr)
return;
AEDisposeDesc(&eventReply);
#endif
#ifdef Q_OS_WIN
HANDLE hToken; // handle to process token
TOKEN_PRIVILEGES tkp; // pointer to token structure
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return;
// Get the LUID for shutdown privilege.
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
&tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get shutdown privilege for this process.
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);
// Cannot test the return value of AdjustTokenPrivileges.
if (GetLastError() != ERROR_SUCCESS)
return;
if (action == ShutdownAction::Suspend)
SetSuspendState(false, false, false);
else if (action == ShutdownAction::Hibernate)
SetSuspendState(true, false, false);
else
InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false);
// Disable shutdown privilege.
tkp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);
#endif
}
#endif // DISABLE_GUI
#ifndef DISABLE_GUI
// Get screen center
QPoint Utils::Misc::screenCenter(QWidget *win)
{
int scrn = 0;
const QWidget *w = win->window();
if (w)
scrn = QApplication::desktop()->screenNumber(w);
else if (QApplication::desktop()->isVirtualDesktop())
scrn = QApplication::desktop()->screenNumber(QCursor::pos());
else
scrn = QApplication::desktop()->screenNumber(win);
QRect desk(QApplication::desktop()->availableGeometry(scrn));
return QPoint((desk.width() - win->frameGeometry().width()) / 2, (desk.height() - win->frameGeometry().height()) / 2);
}
#endif
/**
* Detects the python version.
*/
int Utils::Misc::pythonVersion()
{
static int version = -1;
if (version < 0) {
QString versionComplete = pythonVersionComplete().trimmed();
QStringList splitted = versionComplete.split('.');
if (splitted.size() > 1) {
int highVer = splitted.at(0).toInt();
if ((highVer == 2) || (highVer == 3))
version = highVer;
}
}
return version;
}
/**
* Detects the python executable by calling "python --version".
*/
QString Utils::Misc::pythonExecutable()
{
static QString executable;
if (executable.isEmpty()) {
QProcess pythonProc;
#if defined(Q_OS_UNIX)
/*
* On Unix-Like Systems python2 and python3 should always exist
* http://legacy.python.org/dev/peps/pep-0394/
*/
pythonProc.start("python3", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
executable = "python3";
return executable;
}
pythonProc.start("python2", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
executable = "python2";
return executable;
}
#endif
// Look for "python" in Windows and in UNIX if "python2" and "python3" are
// not detected.
pythonProc.start("python", QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0)
executable = "python";
else
Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python not detected"), Log::INFO);
}
return executable;
}
/**
* Returns the complete python version
* eg 2.7.9
* Make sure to have setup python first
*/
QString Utils::Misc::pythonVersionComplete() {
static QString version;
if (version.isEmpty()) {
if (pythonExecutable().isEmpty())
return version;
QProcess pythonProc;
pythonProc.start(pythonExecutable(), QStringList() << "--version", QIODevice::ReadOnly);
if (pythonProc.waitForFinished() && pythonProc.exitCode() == 0) {
QByteArray output = pythonProc.readAllStandardOutput();
if (output.isEmpty())
output = pythonProc.readAllStandardError();
// Software 'Anaconda' installs its own python interpreter
// and `python --version` returns a string like this:
// `Python 3.4.3 :: Anaconda 2.3.0 (64-bit)`
const QList<QByteArray> verSplit = output.split(' ');
if (verSplit.size() > 1) {
version = verSplit.at(1).trimmed();
Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python version: %1").arg(version), Log::INFO);
}
}
}
return version;
}
// 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
// to send numbers instead of strings with suffixes
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
{
if (val < 0)
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
int i = 0;
while(val >= 1024. && i < 4) {
val /= 1024.;
++i;
}
QString ret;
if (i == 0)
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
else
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
if (is_speed)
ret += QCoreApplication::translate("misc", "/s", "per second");
return ret;
}
bool Utils::Misc::isPreviewable(const QString& extension)
{
static QSet<QString> multimedia_extensions;
if (multimedia_extensions.empty()) {
multimedia_extensions.insert("3GP");
multimedia_extensions.insert("AAC");
multimedia_extensions.insert("AC3");
multimedia_extensions.insert("AIF");
multimedia_extensions.insert("AIFC");
multimedia_extensions.insert("AIFF");
multimedia_extensions.insert("ASF");
multimedia_extensions.insert("AU");
multimedia_extensions.insert("AVI");
multimedia_extensions.insert("FLAC");
multimedia_extensions.insert("FLV");
multimedia_extensions.insert("M3U");
multimedia_extensions.insert("M4A");
multimedia_extensions.insert("M4P");
multimedia_extensions.insert("M4V");
multimedia_extensions.insert("MID");
multimedia_extensions.insert("MKV");
multimedia_extensions.insert("MOV");
multimedia_extensions.insert("MP2");
multimedia_extensions.insert("MP3");
multimedia_extensions.insert("MP4");
multimedia_extensions.insert("MPC");
multimedia_extensions.insert("MPE");
multimedia_extensions.insert("MPEG");
multimedia_extensions.insert("MPG");
multimedia_extensions.insert("MPP");
multimedia_extensions.insert("OGG");
multimedia_extensions.insert("OGM");
multimedia_extensions.insert("OGV");
multimedia_extensions.insert("QT");
multimedia_extensions.insert("RA");
multimedia_extensions.insert("RAM");
multimedia_extensions.insert("RM");
multimedia_extensions.insert("RMV");
multimedia_extensions.insert("RMVB");
multimedia_extensions.insert("SWA");
multimedia_extensions.insert("SWF");
multimedia_extensions.insert("VOB");
multimedia_extensions.insert("WAV");
multimedia_extensions.insert("WMA");
multimedia_extensions.insert("WMV");
}
if (extension.isEmpty())
return false;
return multimedia_extensions.contains(extension.toUpper());
}
QString Utils::Misc::bcLinkToMagnet(QString bc_link)
{
QByteArray raw_bc = bc_link.toUtf8();
raw_bc = raw_bc.mid(8); // skip bc://bt/
raw_bc = QByteArray::fromBase64(raw_bc); // Decode base64
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
QStringList parts = QString(raw_bc).split("/");
if (parts.size() != 5) return QString::null;
QString filename = parts.at(1);
QString hash = parts.at(3);
QString magnet = "magnet:?xt=urn:btih:" + hash;
magnet += "&dn=" + filename;
return magnet;
}
// Take a number of seconds and return an user-friendly
// time duration like "1d 2h 10m".
QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
{
if (seconds < 0 || seconds >= MAX_ETA)
return QString::fromUtf8(C_INFINITY);
if (seconds == 0)
return "0";
if (seconds < 60)
return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
int minutes = seconds / 60;
if (minutes < 60)
return QCoreApplication::translate("misc", "%1m","e.g: 10minutes").arg(QString::number(minutes));
int hours = minutes / 60;
minutes = minutes - hours * 60;
if (hours < 24)
return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3hours 5minutes").arg(QString::number(hours)).arg(QString::number(minutes));
int days = hours / 24;
hours = hours - days * 24;
if (days < 100)
return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2days 10hours").arg(QString::number(days)).arg(QString::number(hours));
return QString::fromUtf8(C_INFINITY);
}
QString Utils::Misc::getUserIDString()
{
QString uid = "0";
#ifdef Q_OS_WIN
WCHAR buffer[UNLEN + 1] = {0};
DWORD buffer_len = sizeof(buffer)/sizeof(*buffer);
if (GetUserNameW(buffer, &buffer_len))
uid = QString::fromWCharArray(buffer);
#else
uid = QString::number(getuid());
#endif
return uid;
}
QStringList Utils::Misc::toStringList(const QList<bool> &l)
{
QStringList ret;
foreach (const bool &b, l)
ret << (b ? "1" : "0");
return ret;
}
QList<int> Utils::Misc::intListfromStringList(const QStringList &l)
{
QList<int> ret;
foreach (const QString &s, l)
ret << s.toInt();
return ret;
}
QList<bool> Utils::Misc::boolListfromStringList(const QStringList &l)
{
QList<bool> ret;
foreach (const QString &s, l)
ret << (s == "1");
return ret;
}
bool Utils::Misc::isUrl(const QString &s)
{
const QString scheme = QUrl(s).scheme();
QRegExp is_url("http[s]?|ftp", Qt::CaseInsensitive);
return is_url.exactMatch(scheme);
}
QString Utils::Misc::parseHtmlLinks(const QString &raw_text)
{
QString result = raw_text;
static QRegExp reURL(
"(\\s|^)" //start with whitespace or beginning of line
"("
"(" //case 1 -- URL with scheme
"(http(s?))\\://" //start with scheme
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
"([a-zA-Z0-9\\?%=&/_\\.:#;-]+)" // everything to 1st non-URI char, must be at least one char after the previous dot (cannot use ".*" because it can be too greedy)
")"
"|"
"(" //case 2a -- no scheme, contains common TLD example.com
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
"(?=" // must be followed by TLD
"AERO|aero|" //N.B. assertions are non-capturing
"ARPA|arpa|"
"ASIA|asia|"
"BIZ|biz|"
"CAT|cat|"
"COM|com|"
"COOP|coop|"
"EDU|edu|"
"GOV|gov|"
"INFO|info|"
"INT|int|"
"JOBS|jobs|"
"MIL|mil|"
"MOBI|mobi|"
"MUSEUM|museum|"
"NAME|name|"
"NET|net|"
"ORG|org|"
"PRO|pro|"
"RO|ro|"
"RU|ru|"
"TEL|tel|"
"TRAVEL|travel"
")"
"([a-zA-Z0-9\\?%=&/_\\.:#;-]+)" // everything to 1st non-URI char, must be at least one char after the previous dot (cannot use ".*" because it can be too greedy)
")"
"|"
"(" // case 2b no scheme, no TLD, must have at least 2 alphanum strings plus uncommon TLD string --> del.icio.us
"([a-zA-Z0-9_-]+\\.) {2,}" //2 or more domainpart. --> del.icio.
"[a-zA-Z]{2,}" //one ab (2 char or longer) --> us
"([a-zA-Z0-9\\?%=&/_\\.:#;-]*)" // everything to 1st non-URI char, maybe nothing in case of del.icio.us/path
")"
")"
);
// Capture links
result.replace(reURL, "\\1<a href=\"\\2\">\\2</a>");
// Capture links without scheme
static QRegExp reNoScheme("<a\\s+href=\"(?!http(s?))([a-zA-Z0-9\\?%=&/_\\.-:#]+)\\s*\">");
result.replace(reNoScheme, "<a href=\"http://\\1\">");
// to preserve plain text formatting
result = "<p style=\"white-space: pre-wrap;\">" + result + "</p>";
return result;
}
#ifndef DISABLE_GUI
// Open the given path with an appropriate application
void Utils::Misc::openPath(const QString& absolutePath)
{
const QString path = Utils::Fs::fromNativePath(absolutePath);
// Hack to access samba shares with QDesktopServices::openUrl
if (path.startsWith("//"))
QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + path));
else
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
// Open the parent directory of the given path with a file manager and select
// (if possible) the item at the given path
void Utils::Misc::openFolderSelect(const QString& absolutePath)
{
const QString path = Utils::Fs::fromNativePath(absolutePath);
#ifdef Q_OS_WIN
if (QFileInfo(path).exists()) {
// Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select"
// Dir separators MUST be win-style slashes
// QProcess::startDetached() has an obscure bug. If the path has
// no spaces and a comma(and maybe other special characters) it doesn't
// get wrapped in quotes. So explorer.exe can't find the correct path and
// displays the default one. If we wrap the path in quotes and pass it to
// QProcess::startDetached() explorer.exe still shows the default path. In
// this case QProcess::startDetached() probably puts its own quotes around ours.
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo;
::ZeroMemory(&processInfo, sizeof(processInfo));
QString cmd = QString("explorer.exe /select,\"%1\"").arg(Utils::Fs::toNativePath(absolutePath));
LPWSTR lpCmd = new WCHAR[cmd.size() + 1];
cmd.toWCharArray(lpCmd);
lpCmd[cmd.size()] = 0;
bool ret = ::CreateProcessW(NULL, lpCmd, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
delete [] lpCmd;
if (ret) {
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
}
}
else {
// If the item to select doesn't exist, try to open its parent
openPath(path.left(path.lastIndexOf("/")));
}
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
if (QFileInfo(path).exists()) {
QProcess proc;
QString output;
proc.start("xdg-mime", QStringList() << "query" << "default" << "inode/directory");
proc.waitForFinished();
output = proc.readLine().simplified();
if (output == "dolphin.desktop" || output == "org.kde.dolphin.desktop")
proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else if (output == "nautilus.desktop" || output == "org.gnome.Nautilus.desktop"
|| output == "nautilus-folder-handler.desktop")
proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "caja-folder-handler.desktop")
proc.startDetached("caja", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "nemo.desktop")
proc.startDetached("nemo", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(path));
else if (output == "konqueror.desktop" || output == "kfmclient_dir.desktop")
proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(path));
else
openPath(path.left(path.lastIndexOf("/")));
}
else {
// If the item to select doesn't exist, try to open its parent
openPath(path.left(path.lastIndexOf("/")));
}
#else
openPath(path.left(path.lastIndexOf("/")));
#endif
}
#endif // DISABLE_GUI
namespace
{
// Trick to get a portable sleep() function
class SleeperThread : public QThread
{
public:
static void msleep(unsigned long msecs)
{
QThread::msleep(msecs);
}
};
}
void Utils::Misc::msleep(unsigned long msecs)
{
SleeperThread::msleep(msecs);
}
#ifndef DISABLE_GUI
QSize Utils::Misc::smallIconSize()
{
// Get DPI scaled icon size (device-dependent), see QT source
int s = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
return QSize(s, s);
}
#endif

89
src/base/utils/misc.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* 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 "base/types.h"
/* Miscellaneous 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);
QSize smallIconSize();
#endif
int pythonVersion();
QString pythonExecutable();
QString pythonVersionComplete();
// 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);
#ifndef DISABLE_GUI
void openPath(const QString& absolutePath);
void openFolderSelect(const QString& absolutePath);
#endif
void msleep(unsigned long msecs);
}
}
#endif

211
src/base/utils/string.cpp Normal file
View File

@@ -0,0 +1,211 @@
/*
* 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(const QString &left, const 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;
}
Utils::String::NaturalCompare::NaturalCompare()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return;
#endif
m_collator.setNumericMode(true);
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
#endif
}
bool Utils::String::NaturalCompare::operator()(const QString &l, const QString &r)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#if defined(Q_OS_WIN)
// Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7
if(QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
return lessThan(l, r);
#endif
return (m_collator.compare(l, r) < 0);
#else
return lessThan(l, r);
#endif
}
bool Utils::String::NaturalCompare::lessThan(const QString &left, const QString &right)
{
// Return value `false` indicates `right` should go before `left`, otherwise, after
int posL = 0;
int posR = 0;
while (true) {
while (true) {
if (posL == left.size() || posR == right.size())
return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string
QChar leftChar = left[posL].toLower();
QChar rightChar = right[posR].toLower();
if (leftChar == rightChar)
; // compare next character
else if (leftChar.isDigit() && rightChar.isDigit())
break; // Both are digits, break this loop and compare numbers
else
return leftChar < rightChar;
++posL;
++posR;
}
int startL = posL;
while ((posL < left.size()) && left[posL].isDigit())
++posL;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numL = left.midRef(startL, posL - startL).toInt();
#else
int numL = left.mid(startL, posL - startL).toInt();
#endif
int startR = posR;
while ((posR < right.size()) && right[posR].isDigit())
++posR;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
int numR = right.midRef(startR, posR - startR).toInt();
#else
int numR = right.mid(startR, posR - startR).toInt();
#endif
if (numL != numR)
return (numL < numR);
// Strings + digits do match and we haven't hit string end
// Do another round
}
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);
}

70
src/base/utils/string.h Normal file
View File

@@ -0,0 +1,70 @@
/*
* 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>
#include <QtGlobal>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
#include <QCollator>
#endif
class QString;
class QByteArray;
namespace Utils
{
namespace String
{
QString fromStdString(const std::string &str);
std::string toStdString(const QString &str);
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);
bool naturalSort(const QString &left, const QString &right, bool &result);
class NaturalCompare
{
public:
NaturalCompare();
bool operator()(const QString &l, const QString &r);
bool lessThan(const QString &left, const QString &right);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
private:
QCollator m_collator;
#endif
};
}
}
#endif // UTILS_STRING_H