diff --git a/src/base/bittorrent/peer_shadowban_plugin.hpp b/src/base/bittorrent/peer_shadowban_plugin.hpp new file mode 100644 index 00000000000..aae33fdf74c --- /dev/null +++ b/src/base/bittorrent/peer_shadowban_plugin.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#if (LIBTORRENT_VERSION_NUM >= 20000) +using client_data = lt::client_data_t; +#else +using client_data = void *; +#endif + +using filter_function = std::function; + +class peer_shadowban_plugin final : public lt::peer_plugin +{ +public: + peer_shadowban_plugin(lt::peer_connection_handle p) + : m_peer_connection(p) + { + } + + bool on_request(lt::peer_request const &r) override + { + if (is_shadowbanned_peer()) + { + return true; + } + return peer_plugin::on_request(r); + } + +protected: + bool is_shadowbanned_peer() + { + lt::peer_info info; + m_peer_connection.get_peer_info(info); + + QString peer_ip = QString::fromStdString(info.ip.address().to_string()); + QStringList shadowbannedIPs = + CachedSettingValue(u"State/ShadowBannedIPs"_s, QStringList(), Algorithm::sorted).get(); + + return shadowbannedIPs.contains(peer_ip); + } + +private: + lt::peer_connection_handle m_peer_connection; +}; + +class peer_shadowban_action_plugin : public lt::torrent_plugin +{ +public: + peer_shadowban_action_plugin() + { + } + + std::shared_ptr new_connection(lt::peer_connection_handle const &p) override + { + return std::make_shared(p); + } +}; + +std::shared_ptr create_peer_shadowban_plugin(const lt::torrent_handle &th, client_data) +{ + // ignore private torrents + if (th.torrent_file() && th.torrent_file()->priv()) + return nullptr; + + return std::make_shared(); +} diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 67ec9e2cee1..82a4c8899f5 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -240,6 +240,12 @@ namespace BitTorrent virtual bool isAutoBanBTPlayerPeerEnabled() const = 0; virtual void setAutoBanBTPlayerPeer(bool value) = 0; + // Shadowban IP + virtual bool isShadowBanEnabled() const = 0; + virtual void setShadowBan(bool value) = 0; + virtual QStringList shadowBannedIPs() const = 0; + virtual void setShadowBannedIPs(const QStringList &newList) = 0; + // Trackers list virtual bool isAutoUpdateTrackersEnabled() const = 0; virtual void setAutoUpdateTrackersEnabled(bool enabled) = 0; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 3d16abb731a..dfed6ccbdeb 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -108,6 +108,7 @@ #include "nativesessionextension.h" #include "peer_blacklist.hpp" #include "peer_filter_session_plugin.hpp" +#include "peer_shadowban_plugin.hpp" #include "portforwarderimpl.h" #include "resumedatastorage.h" #include "torrentimpl.h" @@ -537,6 +538,8 @@ SessionImpl::SessionImpl(QObject *parent) , m_publicTrackers(BITTORRENT_SESSION_KEY(u"PublicTrackersList"_s)) , m_autoBanUnknownPeer(BITTORRENT_SESSION_KEY(u"AutoBanUnknownPeer"_s), false) , m_autoBanBTPlayerPeer(BITTORRENT_SESSION_KEY(u"AutoBanBTPlayerPeer"_s), false) + , m_shadowBan(BITTORRENT_SESSION_KEY(u"ShadowBan"_s), false) + , m_shadowBannedIPs(u"State/ShadowBannedIPs"_s, QStringList(), Algorithm::sorted) , m_isAutoUpdateTrackersEnabled(BITTORRENT_SESSION_KEY(u"AutoUpdateTrackersEnabled"_s), false) , m_seedingLimitTimer {new QTimer(this)} , m_resumeDataTimer {new QTimer(this)} @@ -1648,6 +1651,8 @@ void SessionImpl::initializeNativeSession() if (isAutoBanBTPlayerPeerEnabled()) m_nativeSession->add_extension(&create_drop_bittorrent_media_player_plugin); m_nativeSession->add_extension(std::make_shared()); + if (isShadowBanEnabled()) + m_nativeSession->add_extension(&create_peer_shadowban_plugin); LogMsg(tr("Peer Exchange (PeX) support: %1").arg(isPeXEnabled() ? tr("ON") : tr("OFF")), Log::INFO); LogMsg(tr("Anonymous mode: %1").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF")), Log::INFO); @@ -5051,6 +5056,59 @@ void SessionImpl::setAutoBanBTPlayerPeer(bool value) } } +bool SessionImpl::isShadowBanEnabled() const +{ + return m_shadowBan; +} + +void SessionImpl::setShadowBan(bool value) +{ + if (value != isShadowBanEnabled()) { + m_shadowBan = value; + LogMsg(tr("Restart is required to toggle Auto Ban Shadow Ban"), Log::WARNING); + } +} + +QStringList SessionImpl::shadowBannedIPs() const +{ + return m_shadowBannedIPs; +} + +void SessionImpl::setShadowBannedIPs(const QStringList &newList) +{ + if (newList == m_shadowBannedIPs) + return; // do nothing + // here filter out incorrect IP + QStringList filteredList; + for (const QString &ip : newList) + { + if (Utils::Net::isValidIP(ip)) + { + // the same IPv6 addresses could be written in different forms; + // QHostAddress::toString() result format follows RFC5952; + // thus we avoid duplicate entries pointing to the same address + filteredList << QHostAddress(ip).toString(); + } + else + { + LogMsg(tr("Rejected invalid IP address while applying the list of shadow banned IP addresses. IP: \"%1\"") + .arg(ip) + , Log::WARNING); + } + } + // now we have to sort IPs and make them unique + filteredList.sort(); + filteredList.removeDuplicates(); + // Again ensure that the new list is different from the stored one. + if (filteredList == m_shadowBannedIPs) + return; // do nothing + // store to session settings + // also here we have to recreate filter list including 3rd party ban file + // and install it again into m_session + m_shadowBannedIPs = filteredList; + configureDeferred(); +} + bool SessionImpl::isListening() const { return m_nativeSessionExtension->isSessionListening(); diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index ce4e2393b09..e0e0880ed53 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -483,6 +483,12 @@ namespace BitTorrent bool isAutoBanBTPlayerPeerEnabled() const override; void setAutoBanBTPlayerPeer(bool value) override; + // Shadowban Peers + bool isShadowBanEnabled() const override; + void setShadowBan(bool value) override; + QStringList shadowBannedIPs() const override; + void setShadowBannedIPs(const QStringList &newList) override; + // Trackers list bool isAutoUpdateTrackersEnabled() const override; void setAutoUpdateTrackersEnabled(bool enabled) override; @@ -751,6 +757,8 @@ namespace BitTorrent CachedSettingValue m_publicTrackers; CachedSettingValue m_autoBanUnknownPeer; CachedSettingValue m_autoBanBTPlayerPeer; + CachedSettingValue m_shadowBan; + CachedSettingValue m_shadowBannedIPs; CachedSettingValue m_isAutoUpdateTrackersEnabled; QTimer *m_updateTimer; diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index c1733950e48..f4d93bd8402 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -2007,6 +2007,16 @@ void Preferences::setAutoBanBTPlayerPeer(const bool checked) setValue(u"Preferences/Advanced/AutoBanBTPlayerPeer"_s, checked); } +bool Preferences::getShadowBan() const +{ + return value(u"Preferences/Advanced/ShadowBan"_s, false); +} + +void Preferences::setShadowBan(const bool checked) +{ + setValue(u"Preferences/Advanced/ShadowBan"_s, checked); +} + QString Preferences::customizeTrackersListUrl() const { return value(u"Preferences/Bittorrent/CustomizeTrackersListUrl"_s, u"https://cdn.jsdelivr.net/gh/ngosang/trackerslist/trackers_best.txt"_s); diff --git a/src/base/preferences.h b/src/base/preferences.h index a9204d7d317..667ffaa8582 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -420,6 +420,8 @@ class Preferences final : public QObject void setAutoBanUnknownPeer(const bool checked); bool getAutoBanBTPlayerPeer() const; void setAutoBanBTPlayerPeer(const bool checked); + bool getShadowBan() const; + void setShadowBan(const bool checked); QString customizeTrackersListUrl() const; void setCustomizeTrackersListUrl(const QString &trackersUrl); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 78ed703da64..7b9662d3136 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -31,6 +31,7 @@ qt_wrap_ui(UI_HEADERS search/pluginsourcedialog.ui search/searchjobwidget.ui search/searchwidget.ui + shadowbanlistoptionsdialog.ui shutdownconfirmdialog.ui speedlimitdialog.ui statsdialog.ui @@ -98,6 +99,7 @@ add_library(qbt_gui STATIC search/searchjobwidget.h search/searchsortmodel.h search/searchwidget.h + shadowbanlistoptionsdialog.h shutdownconfirmdialog.h speedlimitdialog.h statsdialog.h @@ -191,6 +193,7 @@ add_library(qbt_gui STATIC search/searchjobwidget.cpp search/searchsortmodel.cpp search/searchwidget.cpp + shadowbanlistoptionsdialog.cpp shutdownconfirmdialog.cpp speedlimitdialog.cpp statsdialog.cpp diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 29c5a429c72..51821b6df9e 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -54,6 +54,7 @@ HEADERS += \ $$PWD/search/searchjobwidget.h \ $$PWD/search/searchsortmodel.h \ $$PWD/search/searchwidget.h \ + $$PWD/shadowbanlistoptionsdialog.h \ $$PWD/shutdownconfirmdialog.h \ $$PWD/speedlimitdialog.h \ $$PWD/statsdialog.h \ @@ -147,6 +148,7 @@ SOURCES += \ $$PWD/search/searchjobwidget.cpp \ $$PWD/search/searchsortmodel.cpp \ $$PWD/search/searchwidget.cpp \ + $$PWD/shadowbanlistoptionsdialog.cpp \ $$PWD/shutdownconfirmdialog.cpp \ $$PWD/speedlimitdialog.cpp \ $$PWD/statsdialog.cpp \ @@ -209,6 +211,7 @@ FORMS += \ $$PWD/search/pluginsourcedialog.ui \ $$PWD/search/searchjobwidget.ui \ $$PWD/search/searchwidget.ui \ + $$PWD/shadowbanlistoptionsdialog.ui \ $$PWD/shutdownconfirmdialog.ui \ $$PWD/speedlimitdialog.ui \ $$PWD/statsdialog.ui \ diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index daeb2ff16b5..50dce8f6caa 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -61,6 +61,7 @@ #include "addnewtorrentdialog.h" #include "advancedsettings.h" #include "banlistoptionsdialog.h" +#include "shadowbanlistoptionsdialog.h" #include "interfaces/iguiapplication.h" #include "ipsubnetwhitelistoptionsdialog.h" #include "rss/automatedrssdownloader.h" @@ -1436,6 +1437,11 @@ bool OptionsDialog::isIPFilteringEnabled() const return m_ui->checkIPFilter->isChecked(); } +bool OptionsDialog::isShadowBanEnabled() const +{ + return m_ui->shadowBanEnabled->isChecked(); +} + Net::ProxyType OptionsDialog::getProxyType() const { return m_ui->comboProxyType->currentData().value(); @@ -1977,6 +1983,14 @@ void OptionsDialog::on_banListButton_clicked() dialog->open(); } +void OptionsDialog::on_shadowBanListButton_clicked() +{ + auto *dialog = new ShadowBanListOptionsDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + connect(dialog, &QDialog::accepted, this, &OptionsDialog::enableApplyButton); + dialog->open(); +} + void OptionsDialog::on_IPSubnetWhitelistButton_clicked() { auto *dialog = new IPSubnetWhitelistOptionsDialog(this); diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h index 83b55a32539..6a95b93d90e 100644 --- a/src/gui/optionsdialog.h +++ b/src/gui/optionsdialog.h @@ -98,6 +98,7 @@ private slots: void on_IpFilterRefreshBtn_clicked(); void handleIPFilterParsed(bool error, int ruleCount); void on_banListButton_clicked(); + void on_shadowBanListButton_clicked(); void on_IPSubnetWhitelistButton_clicked(); void on_randomButton_clicked(); void on_addWatchedFolderButton_clicked(); @@ -180,6 +181,7 @@ private slots: // IP Filter bool isIPFilteringEnabled() const; Path getFilter() const; + bool isShadowBanEnabled() const; // Queueing system bool isQueueingSystemEnabled() const; int getMaxActiveDownloads() const; diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 9a7f1b4b1e0..81af9f660f2 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -2154,6 +2154,39 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'. + + + + Shadow Ban Settings + + + + + + + + Enable Shadow Ban + + + + + + + + + + 0 + 0 + + + + Manually shadow banned IP addresses... + + + + + + diff --git a/src/gui/shadowbanlistoptionsdialog.cpp b/src/gui/shadowbanlistoptionsdialog.cpp new file mode 100644 index 00000000000..cf624a240f1 --- /dev/null +++ b/src/gui/shadowbanlistoptionsdialog.cpp @@ -0,0 +1,136 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Alexandr Milovantsev + * + * 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 "shadowbanlistoptionsdialog.h" + +#include +#include +#include +#include + +#include "base/bittorrent/session.h" +#include "base/utils/net.h" +#include "ui_shadowbanlistoptionsdialog.h" +#include "utils.h" + +#define SETTINGS_KEY(name) u"ShadowBanListOptionsDialog/" name + +ShadowBanListOptionsDialog::ShadowBanListOptionsDialog(QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::ShadowBanListOptionsDialog) + , m_storeDialogSize(SETTINGS_KEY(u"Size"_s)) + , m_model(new QStringListModel(BitTorrent::Session::instance()->shadowBannedIPs(), this)) +{ + m_ui->setupUi(this); + + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + m_sortFilter = new QSortFilterProxyModel(this); + m_sortFilter->setDynamicSortFilter(true); + m_sortFilter->setSourceModel(m_model); + + m_ui->bannedIPList->setModel(m_sortFilter); + m_ui->bannedIPList->sortByColumn(0, Qt::AscendingOrder); + m_ui->buttonBanIP->setEnabled(false); + + if (const QSize dialogSize = m_storeDialogSize; dialogSize.isValid()) + resize(dialogSize); +} + +ShadowBanListOptionsDialog::~ShadowBanListOptionsDialog() +{ + m_storeDialogSize = size(); + delete m_ui; +} + +void ShadowBanListOptionsDialog::on_buttonBox_accepted() +{ + if (m_modified) + { + // save to session + QStringList IPList; + // Operate on the m_sortFilter to grab the strings in sorted order + for (int i = 0; i < m_sortFilter->rowCount(); ++i) + { + QModelIndex index = m_sortFilter->index(i, 0); + IPList << index.data().toString(); + } + BitTorrent::Session::instance()->setShadowBannedIPs(IPList); + QDialog::accept(); + } + else + { + QDialog::reject(); + } +} + +void ShadowBanListOptionsDialog::on_buttonBanIP_clicked() +{ + QString ip = m_ui->txtIP->text(); + if (!Utils::Net::isValidIP(ip)) + { + QMessageBox::warning(this, tr("Warning"), tr("The entered IP address is invalid.")); + return; + } + // the same IPv6 addresses could be written in different forms; + // QHostAddress::toString() result format follows RFC5952; + // thus we avoid duplicate entries pointing to the same address + ip = QHostAddress(ip).toString(); + for (int i = 0; i < m_sortFilter->rowCount(); ++i) + { + QModelIndex index = m_sortFilter->index(i, 0); + if (ip == index.data().toString()) + { + QMessageBox::warning(this, tr("Warning"), tr("The entered IP is already banned.")); + return; + } + } + + m_model->insertRow(m_model->rowCount()); + m_model->setData(m_model->index(m_model->rowCount() - 1, 0), ip); + m_ui->txtIP->clear(); + m_modified = true; +} + +void ShadowBanListOptionsDialog::on_buttonDeleteIP_clicked() +{ + QModelIndexList selection = m_ui->bannedIPList->selectionModel()->selectedIndexes(); + std::sort(selection.begin(), selection.end(), [](const QModelIndex &left, const QModelIndex &right) + { + return (left.row() > right.row()); + }); + for (const QModelIndex &i : selection) + m_sortFilter->removeRow(i.row()); + + m_modified = true; +} + +void ShadowBanListOptionsDialog::on_txtIP_textChanged(const QString &ip) +{ + m_ui->buttonBanIP->setEnabled(Utils::Net::isValidIP(ip)); +} diff --git a/src/gui/shadowbanlistoptionsdialog.h b/src/gui/shadowbanlistoptionsdialog.h new file mode 100644 index 00000000000..f710c5dcb87 --- /dev/null +++ b/src/gui/shadowbanlistoptionsdialog.h @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Alexandr Milovantsev + * + * 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. + */ + +#pragma once + +#include + +#include "base/settingvalue.h" + +class QSortFilterProxyModel; +class QStringListModel; + +namespace Ui +{ + class ShadowBanListOptionsDialog; +} + +class ShadowBanListOptionsDialog final : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(ShadowBanListOptionsDialog) + +public: + explicit ShadowBanListOptionsDialog(QWidget *parent = nullptr); + ~ShadowBanListOptionsDialog() override; + +private slots: + void on_buttonBox_accepted(); + void on_buttonBanIP_clicked(); + void on_buttonDeleteIP_clicked(); + void on_txtIP_textChanged(const QString &ip); + +private: + Ui::ShadowBanListOptionsDialog *m_ui = nullptr; + SettingValue m_storeDialogSize; + QStringListModel *m_model = nullptr; + QSortFilterProxyModel *m_sortFilter = nullptr; + bool m_modified = false; +}; diff --git a/src/gui/shadowbanlistoptionsdialog.ui b/src/gui/shadowbanlistoptionsdialog.ui new file mode 100644 index 00000000000..2bc4ac17b1d --- /dev/null +++ b/src/gui/shadowbanlistoptionsdialog.ui @@ -0,0 +1,113 @@ + + + ShadowBanListOptionsDialog + + + + 0 + 0 + 360 + 450 + + + + List of shadow banned IP addresses + + + + + + true + + + + 0 + 0 + + + + true + + + QFrame::Panel + + + QFrame::Raised + + + 1 + + + 0 + + + + + + + 0 + 0 + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + false + + + true + + + false + + + + + + + + + + + + Ban IP + + + + + + + Delete + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + bannedIPList + txtIP + buttonBanIP + buttonDeleteIP + + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index e121b06e37c..c15db0f2f8d 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -226,6 +226,8 @@ void AppController::preferencesAction() data[u"ip_filter_path"_s] = session->IPFilterFile().toString(); data[u"ip_filter_trackers"_s] = session->isTrackerFilteringEnabled(); data[u"banned_IPs"_s] = session->bannedIPs().join(u'\n'); + data[u"shadow_ban_enabled"_s] = session->isShadowBanEnabled(); + data[u"shadow_banned_IPs"_s] = session->shadowBannedIPs().join(u'\n'); // Speed // Global Rate Limits @@ -698,6 +700,10 @@ void AppController::setPreferencesAction() session->setTrackerFilteringEnabled(it.value().toBool()); if (hasKey(u"banned_IPs"_s)) session->setBannedIPs(it.value().toString().split(u'\n', Qt::SkipEmptyParts)); + if (hasKey(u"shadow_ban_enabled"_s)) + session->setShadowBan(it.value().toBool()); + if (hasKey(u"shadow_banned_IPs"_s)) + session->setShadowBannedIPs(it.value().toString().split(u'\n', Qt::SkipEmptyParts)); if (hasKey(u"auto_ban_unknown_peer"_s)) session->setAutoBanUnknownPeer(it.value().toBool()); if (hasKey(u"auto_ban_bt_player_peer"_s)) diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 34a50316836..032831c2c1c 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1114,6 +1114,21 @@ +
+ + + +
+ + +
+
+
+ QBT_TR(Manually shadow banned IP addresses...)QBT_TR[CONTEXT=OptionsDialog] + +
+
+
QBT_TR(libtorrent Section)QBT_TR[CONTEXT=OptionsDialog] (QBT_TR(Open documentation)QBT_TR[CONTEXT=HttpServer]) @@ -2308,6 +2323,8 @@ $('reannounceWhenAddressChanged').setProperty('checked', pref.reannounce_when_address_changed); $('autoBanUnknownPeer').setProperty('checked', pref.auto_ban_unknown_peer); $('autoBanBittorrentPlayer').setProperty('checked', pref.auto_ban_bt_player_peer); + $('shadowBan').setProperty('checked', pref.shadow_ban_enabled); + $('shadowBannedIPs').setProperty('value', pref.shadow_banned_IPs); // libtorrent section $('bdecodeDepthLimit').setProperty('value', pref.bdecode_depth_limit); $('bdecodeTokenLimit').setProperty('value', pref.bdecode_token_limit); @@ -2752,6 +2769,8 @@ settings.set('resolve_peer_countries', $('resolvePeerCountries').getProperty('checked')); settings.set('reannounce_when_address_changed', $('reannounceWhenAddressChanged').getProperty('checked')); settings.set('auto_ban_unknown_peer', $('autoBanUnknownPeer').getProperty('checked')); + settings.set('shadow_ban', $('shadowBan').getProperty('checked')); + settings.set('shadow_banned_IPs', $('shadowBannedIPs').getProperty('value')); settings.set('auto_ban_bt_player_peer', $('autoBanBittorrentPlayer').getProperty('checked')); // libtorrent section