diff --git a/package/lean/qBittorrent/Makefile b/package/lean/qBittorrent/Makefile
index 7ef3b1d22e..3bd02e2658 100644
--- a/package/lean/qBittorrent/Makefile
+++ b/package/lean/qBittorrent/Makefile
@@ -1,12 +1,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=qbittorrent
-PKG_VERSION:=4.2.0
-PKG_RELEASE=1
+PKG_VERSION:=4.1.9
+PKG_RELEASE=5
PKG_SOURCE:=$(PKG_NAME)-release-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/qbittorrent/qBittorrent/tar.gz/release-$(PKG_VERSION)?
-PKG_HASH:=2f367b89d7049c09e808fc5f7c338ce1820d91bcaecddcad681e9ba18b13206d
+PKG_HASH:=620127f73f88ed3f2b2e4195cbc641f7c27f967d3b045e45f7916c0995fd61fe
PKG_BUILD_DIR:=$(BUILD_DIR)/qBittorrent-release-$(PKG_VERSION)
diff --git a/package/lean/qBittorrent/patches/001-remove-ico-not-for-webui.patch b/package/lean/qBittorrent/patches/001-remove-ico-not-for-webui.patch
new file mode 100644
index 0000000000..ee27bbaca4
--- /dev/null
+++ b/package/lean/qBittorrent/patches/001-remove-ico-not-for-webui.patch
@@ -0,0 +1,126 @@
+--- a/src/icons/icons.qrc
++++ b/src/icons/icons.qrc
+@@ -250,38 +250,16 @@
+ flags/zm.svg
+ flags/zw.svg
+ L.gif
+- loading.png
+ qbt-theme/application-exit.svg
+- qbt-theme/application-rss+xml.svg
+- qbt-theme/application-x-mswinurl.svg
+ qbt-theme/checked.svg
+ qbt-theme/configure.svg
+- qbt-theme/dialog-cancel.svg
+- qbt-theme/dialog-information.svg
+- qbt-theme/dialog-warning.svg
+ qbt-theme/document-edit-verify.svg
+ qbt-theme/document-edit.svg
+- qbt-theme/document-encrypt.svg
+- qbt-theme/document-import.svg
+- qbt-theme/document-new.svg
+- qbt-theme/document-properties.svg
+- qbt-theme/document-save.svg
+- qbt-theme/download.svg
+- qbt-theme/edit-clear-history.svg
+ qbt-theme/edit-clear.svg
+ qbt-theme/edit-copy.svg
+- qbt-theme/edit-cut.svg
+ qbt-theme/edit-delete.svg
+- qbt-theme/edit-find-user.svg
+ qbt-theme/edit-find.svg
+- qbt-theme/edit-paste.svg
+ qbt-theme/edit-rename.svg
+- qbt-theme/folder-documents.svg
+- qbt-theme/folder-download.svg
+- qbt-theme/folder-new.svg
+- qbt-theme/folder-remote.svg
+- qbt-theme/gear.svg
+- qbt-theme/gear32.svg
+ qbt-theme/go-bottom.svg
+ qbt-theme/go-down.svg
+ qbt-theme/go-top.svg
+@@ -295,43 +273,15 @@
+ qbt-theme/kt-set-max-upload-speed.png
+ qbt-theme/list-add.svg
+ qbt-theme/list-remove.svg
+- qbt-theme/mail-folder-inbox.svg
+- qbt-theme/mail-mark-read.svg
+ qbt-theme/media-playback-pause.svg
+ qbt-theme/media-playback-start.svg
+ qbt-theme/media-seek-forward.svg
+- qbt-theme/network-server.svg
+- qbt-theme/network-wired.svg
+- qbt-theme/object-locked.svg
+- qbt-theme/office-chart-line.svg
+- qbt-theme/preferences-desktop.svg
+- qbt-theme/preferences-other.svg
+- qbt-theme/preferences-system-network.svg
+- qbt-theme/preferences-web-browser-cookies.svg
+- qbt-theme/rss-config.png
+ qbt-theme/security-high.svg
+ qbt-theme/security-low.svg
+- qbt-theme/services.svg
+- qbt-theme/speedometer.svg
+ qbt-theme/system-log-out.svg
+- qbt-theme/tab-close.svg
+- qbt-theme/task-attention.svg
+- qbt-theme/task-complete.png
+- qbt-theme/task-ongoing.png
+- qbt-theme/task-reject.png
+- qbt-theme/text-plain.svg
+- qbt-theme/tools-report-bug.svg
+- qbt-theme/unavailable.svg
+- qbt-theme/user-group-delete.svg
+- qbt-theme/user-group-new.svg
+- qbt-theme/view-calendar-journal.svg
+ qbt-theme/view-categories.svg
+- qbt-theme/view-filter.svg
+- qbt-theme/view-preview.svg
+- qbt-theme/view-refresh.svg
+ qbt-theme/view-statistics.svg
+ qbt-theme/wallet-open.svg
+- qbt-theme/webui.svg
+ skin/arrow-right.gif
+ skin/bg-dropdown.gif
+ skin/bg-handle-horizontal.gif
+@@ -342,7 +292,6 @@
+ skin/completed.svg
+ skin/connected.svg
+ skin/disconnected.svg
+- skin/dock-tabs.gif
+ skin/download.svg
+ skin/downloading.svg
+ skin/error.svg
+@@ -353,14 +302,11 @@
+ skin/handle-icon-horizontal.gif
+ skin/handle-icon.gif
+ skin/knob.gif
+- skin/logo-blank.gif
+ skin/logo.gif
+ skin/logo2.gif
+ skin/mascot.png
+ skin/paused.svg
+ skin/qbittorrent-tray.svg
+- skin/qbittorrent-tray-dark.svg
+- skin/qbittorrent-tray-light.svg
+ skin/qbittorrent32.png
+ skin/queued.svg
+ skin/ratio.svg
+@@ -370,17 +316,12 @@
+ skin/spacer.gif
+ skin/spinner-placeholder.gif
+ skin/spinner.gif
+- skin/splash.png
+ skin/stalledDL.svg
+ skin/stalledUP.svg
+ skin/tabs.gif
+ skin/toolbox-divider.gif
+- skin/toolbox-divider2.gif
+ skin/uploading.svg
+ slow.png
+ slow_off.png
+- sphere.png
+- sphere2.png
+- url.png
+
+
diff --git a/package/lean/qBittorrent/patches/002-add-autoban-function.patch b/package/lean/qBittorrent/patches/002-add-autoban-function.patch
new file mode 100644
index 0000000000..8de0f1e47b
--- /dev/null
+++ b/package/lean/qBittorrent/patches/002-add-autoban-function.patch
@@ -0,0 +1,807 @@
+--- a/src/base/bittorrent/peerinfo.cpp
++++ b/src/base/bittorrent/peerinfo.cpp
+@@ -186,11 +186,26 @@ PeerAddress PeerInfo::address() const
+ m_nativeInfo.ip.port());
+ }
+
++int PeerInfo::port() const
++{
++ return m_nativeInfo.ip.port();
++}
++
+ QString PeerInfo::client() const
+ {
+ return QString::fromStdString(m_nativeInfo.client);
+ }
+
++QString PeerInfo::pid() const
++{
++ return QString::fromStdString(m_nativeInfo.pid.to_string());
++}
++
++QString PeerInfo::pidtoclient() const
++{
++ return QString::fromStdString(libt::identify_client(m_nativeInfo.pid));
++}
++
+ qreal PeerInfo::progress() const
+ {
+ return m_nativeInfo.progress;
+--- a/src/base/bittorrent/peerinfo.h
++++ b/src/base/bittorrent/peerinfo.h
+@@ -34,6 +34,7 @@
+ #include
+
+ #include
++#include
+
+ namespace BitTorrent
+ {
+@@ -85,7 +86,10 @@ namespace BitTorrent
+ bool isPlaintextEncrypted() const;
+
+ PeerAddress address() const;
++ int port() const;
+ QString client() const;
++ QString pid() const;
++ QString pidtoclient() const;
+ qreal progress() const;
+ int payloadUpSpeed() const;
+ int payloadDownSpeed() const;
+--- a/src/base/bittorrent/session.cpp
++++ b/src/base/bittorrent/session.cpp
+@@ -69,6 +69,7 @@
+ #endif
+
+ #include "base/algorithm.h"
++#include "base/bittorrent/peerinfo.h"
+ #include "base/exceptions.h"
+ #include "base/global.h"
+ #include "base/logger.h"
+@@ -335,6 +336,8 @@ Session::Session(QObject *parent)
+ , m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
+ , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
+ , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 60)
++ , m_autoBanUnknownPeer(BITTORRENT_SESSION_KEY("AutoBanUnknownPeer"), true)
++ , m_showTrackerAuthWindow(BITTORRENT_SESSION_KEY("ShowTrackerAuthWindow"), true)
+ , m_port(BITTORRENT_SESSION_KEY("Port"), 8999)
+ , m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
+ , m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
+@@ -502,6 +505,7 @@ Session::Session(QObject *parent)
+ libt::ip_filter filter;
+ processBannedIPs(filter);
+ m_nativeSession->set_ip_filter(filter);
++ loadOfflineFilter();
+ }
+
+ m_categories = map_cast(m_storedCategories);
+@@ -518,6 +522,17 @@ Session::Session(QObject *parent)
+ connect(m_refreshTimer, &QTimer::timeout, this, &Session::refresh);
+ m_refreshTimer->start();
+
++ // Unban Timer
++ m_unbanTimer = new QTimer(this);
++ m_unbanTimer->setInterval(500);
++ connect(m_unbanTimer, &QTimer::timeout, this, &Session::processUnbanRequest);
++
++ // Ban Timer
++ m_banTimer = new QTimer(this);
++ m_banTimer->setInterval(500);
++ connect(m_banTimer, &QTimer::timeout, this, &Session::autoBanBadClient);
++ m_banTimer->start();
++
+ m_statistics = new Statistics(this);
+
+ updateSeedingLimitTimer();
+@@ -1077,6 +1092,7 @@ void Session::configure()
+ enableIPFilter();
+ else
+ disableIPFilter();
++ loadOfflineFilter();
+ m_IPFilteringChanged = false;
+ }
+
+@@ -1905,6 +1921,95 @@ void Session::banIP(const QString &ip)
+ }
+ }
+
++bool Session::checkAccessFlags(const QString &ip)
++{
++ libt::ip_filter filter = m_nativeSession->get_ip_filter();
++ boost::system::error_code ec;
++ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
++ Q_ASSERT(!ec);
++ if (ec) return false;
++ return filter.access(addr);
++}
++
++void Session::tempblockIP(const QString &ip)
++{
++ libt::ip_filter filter = m_nativeSession->get_ip_filter();
++ boost::system::error_code ec;
++ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
++ Q_ASSERT(!ec);
++ if (ec) return;
++ filter.add_rule(addr, addr, libt::ip_filter::blocked);
++ m_nativeSession->set_ip_filter(filter);
++ insertQueue(ip);
++}
++
++void Session::removeBlockedIP(const QString &ip)
++{
++ libt::ip_filter filter = m_nativeSession->get_ip_filter();
++ boost::system::error_code ec;
++ libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
++ Q_ASSERT(!ec);
++ if (ec) return;
++ filter.add_rule(addr, addr, 0);
++ m_nativeSession->set_ip_filter(filter);
++}
++
++void Session::eraseIPFilter()
++{
++ q_bannedIPs.clear();
++ q_unbanTime.clear();
++ if (isIPFilteringEnabled()) {
++ enableIPFilter();
++ } else {
++ disableIPFilter();
++ loadOfflineFilter();
++ }
++}
++
++void Session::autoBanBadClient()
++{
++ const BitTorrent::SessionStatus tStatus = BitTorrent::Session::instance()->status();
++ if (tStatus.peersCount > 0) {
++ bool m_AutoBan = BitTorrent::Session::instance()->isAutoBanUnknownPeerEnabled();
++ foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) {
++ QList peers = torrent->peers();
++ foreach (const BitTorrent::PeerInfo &peer, peers) {
++ BitTorrent::PeerAddress addr = peer.address();
++ if (addr.ip.isNull()) continue;
++ QString ip = addr.ip.toString();
++ int port = peer.port();
++ QString client = peer.client();
++ QString ptoc = peer.pidtoclient();
++ QString pid = peer.pid().left(8);
++ QString country = peer.country();
++
++ QRegExp IDFilter("-(XL|SD|XF|QD|BN|DL)(\\d+)-");
++ QRegExp UAFilter("\\d+.\\d+.\\d+.\\d+");
++ if (IDFilter.exactMatch(pid) || UAFilter.exactMatch(client)) {
++ qDebug("Auto Banning bad Peer %s...", ip.toLocal8Bit().data());
++ Logger::instance()->addMessage(tr("Auto banning bad Peer '%1'...'%2'...'%3'...'%4'").arg(ip).arg(pid).arg(ptoc).arg(country));
++ tempblockIP(ip);
++ continue;
++ }
++
++ if(m_AutoBan) {
++ if (client.contains("Unknown") && country == "CN") {
++ qDebug("Auto Banning Unknown Peer %s...", ip.toLocal8Bit().data());
++ Logger::instance()->addMessage(tr("Auto banning Unknown Peer '%1'...'%2'...'%3'...'%4'").arg(ip).arg(pid).arg(ptoc).arg(country));
++ tempblockIP(ip);
++ continue;
++ }
++ if (port >= 65000 && country == "CN" && client.contains("Transmission")) {
++ qDebug("Auto Banning Offline Downloader %s...", ip.toLocal8Bit().data());
++ Logger::instance()->addMessage(tr("Auto banning Offline Downloader '%1:%2'...'%3'...'%4'...'%5'").arg(ip).arg(port).arg(pid).arg(ptoc).arg(country));
++ tempblockIP(ip);
++ }
++ }
++ }
++ }
++ }
++}
++
+ // Delete a torrent from the session, given its hash
+ // deleteLocalFiles = true means that the torrent will be removed from the hard-drive too
+ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles)
+@@ -2787,6 +2892,30 @@ void Session::setSaveResumeDataInterval(
+ }
+ }
+
++bool Session::isAutoBanUnknownPeerEnabled() const
++{
++ return m_autoBanUnknownPeer;
++}
++
++void Session::setAutoBanUnknownPeer(bool value)
++{
++ if (value != isAutoBanUnknownPeerEnabled()) {
++ m_autoBanUnknownPeer = value;
++ }
++}
++
++bool Session::isShowTrackerAuthWindow() const
++{
++ return m_showTrackerAuthWindow;
++}
++
++void Session::setShowTrackerAuthWindow(bool value)
++{
++ if (value != isShowTrackerAuthWindow()) {
++ m_showTrackerAuthWindow = value;
++ }
++}
++
+ int Session::port() const
+ {
+ static int randomPort = Utils::Random::rand(1024, 65535);
+@@ -3877,6 +4006,220 @@ void Session::disableIPFilter()
+ m_nativeSession->set_ip_filter(filter);
+ }
+
++// Insert banned IP to Queue
++void Session::insertQueue(QString ip)
++{
++ q_bannedIPs.enqueue(ip);
++ q_unbanTime.enqueue(QDateTime::currentMSecsSinceEpoch() + 60 * 60 * 1000);
++
++ if (!m_unbanTimer->isActive()) {
++ m_unbanTimer->start();
++ }
++}
++
++// Process Unban Queue
++void Session::processUnbanRequest()
++{
++ if (q_bannedIPs.isEmpty() && q_unbanTime.isEmpty()) {
++ m_unbanTimer->stop();
++ }
++ else if (m_isActive) {
++ return;
++ }
++ else {
++ m_isActive = true;
++ int64_t currentTime = QDateTime::currentMSecsSinceEpoch();
++ int64_t nextTime = q_unbanTime.dequeue();
++ int delayTime = int(nextTime - currentTime);
++ QString nextIP = q_bannedIPs.dequeue();
++ if (delayTime < 0) {
++ QTimer::singleShot(0, [=] { BitTorrent::Session::instance()->removeBlockedIP(nextIP); m_isActive = false; });
++ }
++ else {
++ QTimer::singleShot(delayTime, [=] { BitTorrent::Session::instance()->removeBlockedIP(nextIP); m_isActive = false; });
++ }
++ }
++}
++
++// Handle ipfilter.dat
++int trim(char* const data, int start, int end)
++{
++ if (start >= end) return start;
++ int newStart = start;
++
++ for (int i = start; i <= end; ++i) {
++ if (isspace(data[i]) != 0) {
++ data[i] = '\0';
++ }
++ else {
++ newStart = i;
++ break;
++ }
++ }
++
++ for (int i = end; i >= start; --i) {
++ if (isspace(data[i]) != 0)
++ data[i] = '\0';
++ else
++ break;
++ }
++
++ return newStart;
++}
++
++int findAndNullDelimiter(char *const data, char delimiter, int start, int end)
++{
++ for (int i = start; i <= end; ++i) {
++ if (data[i] == delimiter) {
++ data[i] = '\0';
++ return i;
++ }
++ }
++
++ return -1;
++}
++
++int Session::parseOfflineFilterFile(QString ipDat, libt::ip_filter &filter)
++{
++ int ruleCount = 0;
++ QFile file(ipDat);
++ if (!file.exists()) return ruleCount;
++
++ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
++ LogMsg(tr("I/O Error: Could not open IP filter file in read mode."), Log::CRITICAL);
++ return ruleCount;
++ }
++
++ std::vector buffer(2 * 1024 * 1024, 0); // seems a bit faster than QVector
++ qint64 bytesRead = 0;
++ int offset = 0;
++ int start = 0;
++ int endOfLine = -1;
++ int nbLine = 0;
++
++ while (true) {
++ bytesRead = file.read(buffer.data() + offset, 2 * 1024 * 1024 - offset - 1);
++ if (bytesRead < 0)
++ break;
++ int dataSize = bytesRead + offset;
++ if (bytesRead == 0 && dataSize == 0)
++ break;
++
++ for (start = 0; start < dataSize; ++start) {
++ endOfLine = -1;
++ // The file might have ended without the last line having a newline
++ if (!(bytesRead == 0 && dataSize > 0)) {
++ for (int i = start; i < dataSize; ++i) {
++ if (buffer[i] == '\n') {
++ endOfLine = i;
++ // We need to NULL the newline in case the line has only an IP range.
++ // In that case the parser won't work for the end IP, because it ends
++ // with the newline and not with a number.
++ buffer[i] = '\0';
++ break;
++ }
++ }
++ }
++ else {
++ endOfLine = dataSize;
++ buffer[dataSize] = '\0';
++ }
++
++ if (endOfLine == -1) {
++ // read the next chunk from file
++ // but first move(copy) the leftover data to the front of the buffer
++ offset = dataSize - start;
++ memmove(buffer.data(), buffer.data() + start, offset);
++ break;
++ }
++ else {
++ ++nbLine;
++ }
++
++ if ((buffer[start] == '#')
++ || ((buffer[start] == '/') && ((start + 1 < dataSize) && (buffer[start + 1] == '/')))) {
++ start = endOfLine;
++ continue;
++ }
++
++ // Each line should follow this format:
++ // 001.009.096.105 - 001.009.096.105 , 000 , Some organization
++ // The 3rd entry is access level and if above 127 the IP range isn't blocked.
++ int firstComma = findAndNullDelimiter(buffer.data(), ',', start, endOfLine);
++ if (firstComma != -1)
++ findAndNullDelimiter(buffer.data(), ',', firstComma + 1, endOfLine);
++
++ // Check if there is an access value (apparently not mandatory)
++ if (firstComma != -1) {
++ // There is possibly one
++ const long int nbAccess = strtol(buffer.data() + firstComma + 1, nullptr, 10);
++ // Ignoring this rule because access value is too high
++ if (nbAccess > 127L) {
++ start = endOfLine;
++ continue;
++ }
++ }
++
++ // IP Range should be split by a dash
++ int endOfIPRange = ((firstComma == -1) ? (endOfLine - 1) : (firstComma - 1));
++ int delimIP = findAndNullDelimiter(buffer.data(), '-', start, endOfIPRange);
++ if (delimIP == -1) {
++ start = endOfLine;
++ continue;
++ }
++
++ boost::system::error_code ec;
++ int newStart = trim(buffer.data(), start, delimIP - 1);
++ libt::address startAddr = libt::address::from_string(buffer.data() + newStart, ec);
++ Q_ASSERT(!ec);
++ if (ec) {
++ start = endOfLine;
++ continue;
++ }
++
++ newStart = trim(buffer.data(), delimIP + 1, endOfIPRange);
++ libt::address endAddr = libt::address::from_string(buffer.data() + newStart, ec);
++ Q_ASSERT(!ec);
++ if (ec) {
++ start = endOfLine;
++ continue;
++ }
++
++ if ((startAddr.is_v4() != endAddr.is_v4())
++ || (startAddr.is_v6() != endAddr.is_v6())) {
++ start = endOfLine;
++ continue;
++ }
++
++ start = endOfLine;
++
++ filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked);
++ ++ruleCount;
++ }
++
++ if (start >= dataSize)
++ offset = 0;
++ }
++
++ return ruleCount;
++}
++
++void Session::loadOfflineFilter() {
++ int Count = 0;
++ libt::ip_filter offlineFilter = m_nativeSession->get_ip_filter();
++
++#if defined(Q_OS_WIN)
++ Count = parseOfflineFilterFile("./ipfilter.dat", offlineFilter);
++#endif
++
++#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
++ Count = parseOfflineFilterFile(QDir::home().absoluteFilePath(".config")+"/qBittorrent/ipfilter.dat", offlineFilter);
++#endif
++
++ m_nativeSession->set_ip_filter(offlineFilter);
++ Logger::instance()->addMessage(tr("Successfully parsed the offline downloader IP filter: %1 rules were applied.", "%1 is a number").arg(Count));
++}
++
+ void Session::recursiveTorrentDownload(const InfoHash &hash)
+ {
+ TorrentHandle *const torrent = m_torrents.value(hash);
+@@ -4063,6 +4406,7 @@ void Session::handleIPFilterParsed(int r
+ }
+ Logger::instance()->addMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
+ emit IPFilterParsed(false, ruleCount);
++ loadOfflineFilter();
+ }
+
+ void Session::handleIPFilterError()
+--- a/src/base/bittorrent/session.h
++++ b/src/base/bittorrent/session.h
+@@ -40,8 +40,10 @@
+ #include
+ #include
+ #include
++#include
+ #include
+ #include
++#include
+ #include
+ #include
+
+@@ -341,6 +343,10 @@ namespace BitTorrent
+
+ uint saveResumeDataInterval() const;
+ void setSaveResumeDataInterval(uint value);
++ bool isAutoBanUnknownPeerEnabled() const;
++ void setAutoBanUnknownPeer(bool value);
++ bool isShowTrackerAuthWindow() const;
++ void setShowTrackerAuthWindow(bool value);
+ int port() const;
+ void setPort(int port);
+ bool useRandomPort() const;
+@@ -467,6 +473,19 @@ namespace BitTorrent
+ void setMaxRatioAction(MaxRatioAction act);
+
+ void banIP(const QString &ip);
++ bool checkAccessFlags(const QString &ip);
++ void tempblockIP(const QString &ip);
++ void removeBlockedIP(const QString &ip);
++ void eraseIPFilter();
++ void autoBanBadClient();
++
++ // Unban Timer
++ bool m_isActive = false;
++ QQueue q_bannedIPs;
++ QQueue q_unbanTime;
++ QTimer *m_unbanTimer;
++ QTimer *m_banTimer;
++ void insertQueue(QString ip);
+
+ bool isKnownTorrent(const InfoHash &hash) const;
+ bool addTorrent(QString source, const AddTorrentParams ¶ms = AddTorrentParams());
+@@ -546,6 +565,9 @@ namespace BitTorrent
+ void tagAdded(const QString &tag);
+ void tagRemoved(const QString &tag);
+
++ public slots:
++ void processUnbanRequest();
++
+ private slots:
+ void configureDeferred();
+ void readAlerts();
+@@ -601,6 +623,8 @@ namespace BitTorrent
+ void populateAdditionalTrackers();
+ void enableIPFilter();
+ void disableIPFilter();
++ int parseOfflineFilterFile(QString ipDat, libtorrent::ip_filter &filter);
++ void loadOfflineFilter();
+
+ bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
+ TorrentInfo torrentInfo = TorrentInfo(),
+@@ -713,6 +737,8 @@ namespace BitTorrent
+ CachedSettingValue m_isAltGlobalSpeedLimitEnabled;
+ CachedSettingValue m_isBandwidthSchedulerEnabled;
+ CachedSettingValue m_saveResumeDataInterval;
++ CachedSettingValue m_autoBanUnknownPeer;
++ CachedSettingValue m_showTrackerAuthWindow;
+ CachedSettingValue m_port;
+ CachedSettingValue m_useRandomPort;
+ CachedSettingValue m_networkInterface;
+--- a/src/base/bittorrent/torrenthandle.cpp
++++ b/src/base/bittorrent/torrenthandle.cpp
+@@ -1561,7 +1561,8 @@ void TorrentHandle::handleTrackerErrorAl
+ m_trackerInfos[trackerUrl].lastMessage = message;
+
+ if (p->status_code == 401)
+- m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl);
++ if (Preferences::instance()->getShowTrackerAuthWindow())
++ m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl);
+
+ m_session->handleTorrentTrackerError(this, trackerUrl);
+ }
+--- a/src/base/preferences.cpp
++++ b/src/base/preferences.cpp
+@@ -1090,6 +1090,26 @@ void Preferences::setTrayIconStyle(TrayI
+ }
+ #endif
+
++bool Preferences::getAutoBanUnknownPeer() const
++{
++ return value("Preferences/Advanced/AutoBanUnknownPeer", false).toBool();
++}
++
++void Preferences::setAutoBanUnknownPeer(const bool checked)
++{
++ setValue("Preferences/Advanced/AutoBanUnknownPeer", checked);
++}
++
++bool Preferences::getShowTrackerAuthWindow() const
++{
++ return value("Preferences/Advanced/ShowTrackerAuthWindow", true).toBool();
++}
++
++void Preferences::setShowTrackerAuthWindow(const bool checked)
++{
++ setValue("Preferences/Advanced/ShowTrackerAuthWindow", checked);
++}
++
+ // Stuff that don't appear in the Options GUI but are saved
+ // in the same file.
+
+--- a/src/base/preferences.h
++++ b/src/base/preferences.h
+@@ -300,6 +300,10 @@ public:
+ TrayIcon::Style trayIconStyle() const;
+ void setTrayIconStyle(TrayIcon::Style style);
+ #endif // Q_OS_MAC
++ bool getAutoBanUnknownPeer() const;
++ void setAutoBanUnknownPeer(const bool checked);
++ bool getShowTrackerAuthWindow() const;
++ void setShowTrackerAuthWindow(const bool checked);
+
+ // Stuff that don't appear in the Options GUI but are saved
+ // in the same file.
+--- a/src/base/settingsstorage.cpp
++++ b/src/base/settingsstorage.cpp
+@@ -91,6 +91,8 @@ namespace
+ {"BitTorrent/Session/InterfaceName", "Preferences/Connection/InterfaceName"},
+ {"BitTorrent/Session/InterfaceAddress", "Preferences/Connection/InterfaceAddress"},
+ {"BitTorrent/Session/SaveResumeDataInterval", "Preferences/Downloads/SaveResumeDataInterval"},
++ {"BitTorrent/Session/AutoBanUnknownPeer", "Preferences/Advanced/AutoBanUnknownPeer"},
++ {"BitTorrent/Session/ShowTrackerAuthWindow", "Preferences/Advanced/ShowTrackerAuthWindow"},
+ {"BitTorrent/Session/Encryption", "Preferences/Bittorrent/Encryption"},
+ {"BitTorrent/Session/ForceProxy", "Preferences/Connection/ProxyForce"},
+ {"BitTorrent/Session/ProxyPeerConnections", "Preferences/Connection/ProxyPeerConnections"},
+--- a/src/gui/advancedsettings.cpp
++++ b/src/gui/advancedsettings.cpp
+@@ -60,6 +60,8 @@ enum AdvSettingsRows
+ NETWORK_LISTEN_IPV6,
+ // behavior
+ SAVE_RESUME_DATA_INTERVAL,
++ CONFIRM_AUTO_BAN,
++ SHOW_TRACKER_AUTH_WINDOW,
+ CONFIRM_RECHECK_TORRENT,
+ RECHECK_COMPLETED,
+ #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
+@@ -215,6 +217,10 @@ void AdvancedSettings::saveAdvancedSetti
+ // Announce IP
+ QHostAddress addr(lineEditAnnounceIP.text().trimmed());
+ session->setAnnounceIP(addr.isNull() ? "" : addr.toString());
++ // Auto ban Unknown Peer
++ session->setAutoBanUnknownPeer(cb_auto_ban_unknown_peer.isChecked());
++ // Show Tracker Authenticaion Window
++ session->setShowTrackerAuthWindow(cb_show_tracker_auth_window.isChecked());
+
+ // Program notification
+ MainWindow *const mainWindow = static_cast(QCoreApplication::instance())->mainWindow();
+@@ -465,6 +471,12 @@ void AdvancedSettings::loadAdvancedSetti
+ // Announce IP
+ lineEditAnnounceIP.setText(session->announceIP());
+ addRow(ANNOUNCE_IP, tr("IP Address to report to trackers (requires restart)"), &lineEditAnnounceIP);
++ // Auto Ban Unknown Peer from China
++ cb_auto_ban_unknown_peer.setChecked(session->isAutoBanUnknownPeerEnabled());
++ addRow(CONFIRM_AUTO_BAN, tr("Auto Ban Unknown Peer from China"), &cb_auto_ban_unknown_peer);
++ // Show Tracker Authenticaion Window
++ cb_show_tracker_auth_window.setChecked(session->isShowTrackerAuthWindow());
++ addRow(SHOW_TRACKER_AUTH_WINDOW, tr("Show Tracker Authenticaion Window"), &cb_show_tracker_auth_window);
+
+ // Program notifications
+ const MainWindow *const mainWindow = static_cast(QCoreApplication::instance())->mainWindow();
+--- a/src/gui/advancedsettings.h
++++ b/src/gui/advancedsettings.h
+@@ -65,7 +65,8 @@ private:
+ QCheckBox checkBoxOsCache, checkBoxRecheckCompleted, checkBoxResolveCountries, checkBoxResolveHosts, checkBoxSuperSeeding,
+ checkBoxProgramNotifications, checkBoxTorrentAddedNotifications, checkBoxTrackerFavicon, checkBoxTrackerStatus,
+ checkBoxConfirmTorrentRecheck, checkBoxConfirmRemoveAllTags, checkBoxListenIPv6, checkBoxAnnounceAllTrackers, checkBoxAnnounceAllTiers,
+- checkBoxGuidedReadCache, checkBoxMultiConnectionsPerIp, checkBoxSuggestMode, checkBoxCoalesceRW, checkBoxSpeedWidgetEnabled;
++ checkBoxGuidedReadCache, checkBoxMultiConnectionsPerIp, checkBoxSuggestMode, checkBoxCoalesceRW, checkBoxSpeedWidgetEnabled,
++ cb_auto_ban_unknown_peer, cb_show_tracker_auth_window;
+ QComboBox comboBoxInterface, comboBoxInterfaceAddress, comboBoxUtpMixedMode, comboBoxChokingAlgorithm, comboBoxSeedChokingAlgorithm;
+ QLineEdit lineEditAnnounceIP;
+
+--- a/src/gui/mainwindow.cpp
++++ b/src/gui/mainwindow.cpp
+@@ -73,6 +73,7 @@
+ #include "addnewtorrentdialog.h"
+ #include "application.h"
+ #include "autoexpandabledialog.h"
++#include "base/bittorrent/peerinfo.h"
+ #include "cookiesdialog.h"
+ #include "downloadfromurldialog.h"
+ #include "executionlogwidget.h"
+--- a/src/gui/properties/peerlistdelegate.h
++++ b/src/gui/properties/peerlistdelegate.h
+@@ -49,6 +49,7 @@ public:
+ CONNECTION,
+ FLAGS,
+ CLIENT,
++ PEERID,
+ PROGRESS,
+ DOWN_SPEED,
+ UP_SPEED,
+--- a/src/gui/properties/peerlistwidget.cpp
++++ b/src/gui/properties/peerlistwidget.cpp
+@@ -75,6 +75,7 @@ PeerListWidget::PeerListWidget(Propertie
+ m_listModel->setHeaderData(PeerListDelegate::FLAGS, Qt::Horizontal, tr("Flags"));
+ m_listModel->setHeaderData(PeerListDelegate::CONNECTION, Qt::Horizontal, tr("Connection"));
+ m_listModel->setHeaderData(PeerListDelegate::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
++ m_listModel->setHeaderData(PeerListDelegate::PEERID, Qt::Horizontal, tr("Peer ID", "i.e.: Client Peer ID"));
+ m_listModel->setHeaderData(PeerListDelegate::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
+ m_listModel->setHeaderData(PeerListDelegate::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
+ m_listModel->setHeaderData(PeerListDelegate::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
+@@ -288,8 +289,13 @@ void PeerListWidget::banSelectedPeers()
+ for (const QModelIndex &index : selectedIndexes) {
+ int row = m_proxyModel->mapToSource(index).row();
+ QString ip = m_listModel->data(m_listModel->index(row, PeerListDelegate::IP_HIDDEN)).toString();
++ QString client = m_listModel->data(m_listModel->index(row, PeerListDelegate::CLIENT)).toString();
++ QString peerid = m_listModel->data(m_listModel->index(row, PeerListDelegate::PEERID)).toString();
++ QHostAddress host;
++ host.setAddress(ip);
++ const QString countryName = Net::GeoIPManager::CountryName(Net::GeoIPManager::instance()->lookup(host));
+ qDebug("Banning peer %s...", ip.toLocal8Bit().data());
+- Logger::instance()->addMessage(tr("Manually banning peer '%1'...").arg(ip));
++ Logger::instance()->addMessage(tr("Manually banning peer '%1'...'%2'...'%3'...'%4'").arg(ip).arg(peerid).arg(client).arg(countryName));
+ BitTorrent::Session::instance()->banIP(ip);
+ }
+ // Refresh list
+@@ -398,6 +404,7 @@ QStandardItem *PeerListWidget::addPeer(c
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flags());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flagsDescription(), Qt::ToolTipRole);
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client().toHtmlEscaped());
++ m_listModel->setData(m_listModel->index(row, PeerListDelegate::PEERID), peer.pid().left(8).toHtmlEscaped());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
+@@ -429,6 +436,7 @@ void PeerListWidget::updatePeer(const QS
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flags());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), peer.flagsDescription(), Qt::ToolTipRole);
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client().toHtmlEscaped());
++ m_listModel->setData(m_listModel->index(row, PeerListDelegate::PEERID), peer.pid().left(8).toHtmlEscaped());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed());
+ m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed());
+--- a/src/webui/api/appcontroller.cpp
++++ b/src/webui/api/appcontroller.cpp
+@@ -153,6 +153,7 @@ void AppController::preferencesAction()
+ data["ip_filter_path"] = Utils::Fs::toNativePath(session->IPFilterFile());
+ data["ip_filter_trackers"] = session->isTrackerFilteringEnabled();
+ data["banned_IPs"] = session->bannedIPs().join("\n");
++ data["auto_ban_unknown_peer"] = session->isAutoBanUnknownPeerEnabled();
+
+ // Speed
+ // Global Rate Limits
+@@ -401,6 +402,8 @@ void AppController::setPreferencesAction
+ session->setTrackerFilteringEnabled(m["ip_filter_trackers"].toBool());
+ if (m.contains("banned_IPs"))
+ session->setBannedIPs(m["banned_IPs"].toString().split('\n'));
++ if (m.contains("auto_ban_unknown_peer"))
++ session->setAutoBanUnknownPeer(m["auto_ban_unknown_peer"].toBool());
+
+ // Speed
+ // Global Rate Limits
+--- a/src/webui/api/transfercontroller.cpp
++++ b/src/webui/api/transfercontroller.cpp
+@@ -30,6 +30,7 @@
+
+ #include
+
++#include "base/logger.h"
+ #include "base/bittorrent/session.h"
+
+ const char KEY_TRANSFER_DLSPEED[] = "dl_info_speed";
+@@ -111,3 +112,37 @@ void TransferController::speedLimitsMode
+ {
+ setResult(QString::number(BitTorrent::Session::instance()->isAltGlobalSpeedLimitEnabled()));
+ }
++
++void TransferController::tempblockPeerAction()
++{
++ checkParams({"ip"});
++ QString ip = params()["ip"];
++ boost::system::error_code ec;
++ boost::asio::ip::address addr = boost::asio::ip::address::from_string(ip.toStdString(), ec);
++ bool isBanned = BitTorrent::Session::instance()->checkAccessFlags(QString::fromStdString(addr.to_string()));
++
++ if (ip.isEmpty()) {
++ setResult(QLatin1String("IP field should not be empty."));
++ return;
++ }
++
++ if (ec) {
++ setResult(QLatin1String("The given IP address is not valid."));
++ return;
++ }
++
++ if (isBanned) {
++ setResult(QLatin1String("The given IP address already exists."));
++ return;
++ }
++
++ BitTorrent::Session::instance()->tempblockIP(ip);
++ Logger::instance()->addMessage(tr("Peer '%1' banned via Web API.").arg(ip));
++ setResult(QLatin1String("Done."));
++}
++
++void TransferController::resetIPFilterAction()
++{
++ BitTorrent::Session::instance()->eraseIPFilter();
++ setResult(QLatin1String("Erased."));
++}
+--- a/src/webui/api/transfercontroller.h
++++ b/src/webui/api/transfercontroller.h
+@@ -46,4 +46,6 @@ private slots:
+ void downloadLimitAction();
+ void setUploadLimitAction();
+ void setDownloadLimitAction();
++ void tempblockPeerAction();
++ void resetIPFilterAction();
+ };
+--- a/src/webui/www/private/preferences_content.html
++++ b/src/webui/www/private/preferences_content.html
+@@ -377,6 +377,8 @@
+
+
+
++
++
+
+
+
+@@ -1252,6 +1254,7 @@
+ $('ipfilter_text').setProperty('value', pref.ip_filter_path);
+ $('ipfilter_trackers_checkbox').setProperty('checked', pref.ip_filter_trackers);
+ $('banned_IPs_textarea').setProperty('value', pref.banned_IPs);
++ $('auto_ban_unknown_peer_checkbox').setProperty('checked', pref.auto_ban_unknown_peer);
+ updateFilterSettings();
+
+ // Speed tab
+@@ -1502,6 +1505,7 @@
+ settings.set('ip_filter_path', $('ipfilter_text').getProperty('value'));
+ settings.set('ip_filter_trackers', $('ipfilter_trackers_checkbox').getProperty('checked'));
+ settings.set('banned_IPs', $('banned_IPs_textarea').getProperty('value'));
++ settings.set('auto_ban_unknown_peer', $('auto_ban_unknown_peer_checkbox').getProperty('checked'));
+
+ // Speed tab
+ // Global Rate Limits