diff --git a/Changelog b/Changelog index 77a9a97ee..1153b3ebd 100644 --- a/Changelog +++ b/Changelog @@ -24,6 +24,7 @@ - FEATURE: Torrents can be rechecked from Web UI (Stephanos Antaris) - FEATURE: New peers can manually be added to the torrents - FEATURE: Support per-peer rate limiting + - FEATURE: Support peer manual ban - COSMETIC: Merged download / upload lists - COSMETIC: Torrents can be filtered based on their status - COSMETIC: Torrent properties are now displayed in main window diff --git a/src/Icons/oxygen/add_peer.png b/src/Icons/oxygen/add_peer.png deleted file mode 100644 index a28a8a2e9..000000000 Binary files a/src/Icons/oxygen/add_peer.png and /dev/null differ diff --git a/src/Icons/oxygen/user-group-delete.png b/src/Icons/oxygen/user-group-delete.png new file mode 100644 index 000000000..13d300c2d Binary files /dev/null and b/src/Icons/oxygen/user-group-delete.png differ diff --git a/src/Icons/oxygen/user-group-new.png b/src/Icons/oxygen/user-group-new.png new file mode 100644 index 000000000..c39092037 Binary files /dev/null and b/src/Icons/oxygen/user-group-new.png differ diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index df74ecca7..5ebc6e86f 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -363,6 +363,7 @@ void bittorrent::configureSession() { // * Maximum ratio setDeleteRatio(Preferences::getDeleteRatio()); // Ip Filter + FilterParserThread::processFilterList(s, Preferences::bannedIPs()); if(Preferences::isFilteringEnabled()) { enableIPFilter(Preferences::getFilter()); }else{ @@ -516,6 +517,11 @@ bool bittorrent::hasActiveTorrents() const { return false; } +void bittorrent::banIP(QString ip) { + FilterParserThread::processFilterList(s, QStringList(ip)); + Preferences::banIP(ip); +} + // Delete a torrent from the session, given its hash // permanent = true means that the torrent will be removed from the hard-drive too void bittorrent::deleteTorrent(QString hash, bool permanent) { diff --git a/src/bittorrent.h b/src/bittorrent.h index a2722e9f7..ea4954881 100644 --- a/src/bittorrent.h +++ b/src/bittorrent.h @@ -169,6 +169,7 @@ class bittorrent : public QObject { void addMagnetSkipAddDlg(QString uri); void downloadFromURLList(const QStringList& urls); void configureSession(); + void banIP(QString ip); protected slots: void addTorrentsFromScanFolder(QStringList&); diff --git a/src/filterParserThread.h b/src/filterParserThread.h index de1907fe8..9080834ab 100644 --- a/src/filterParserThread.h +++ b/src/filterParserThread.h @@ -389,6 +389,8 @@ class FilterParserThread : public QThread { // * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format // * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format void processFilterFile(QString _filePath){ + // First, import current filter + filter = s->get_ip_filter(); if(isRunning()) { // Already parsing a filter, abort first abort = true; @@ -400,6 +402,17 @@ class FilterParserThread : public QThread { start(); } + static void processFilterList(session *s, QStringList IPs) { + // First, import current filter + ip_filter filter = s->get_ip_filter(); + foreach(const QString &ip, IPs) { + qDebug("Manual ban of peer %s", ip.toLocal8Bit().data()); + address_v4 addr = address_v4::from_string(ip.toLocal8Bit().data()); + filter.add_rule(addr, addr, ip_filter::blocked); + } + s->set_ip_filter(filter); + } + }; #endif diff --git a/src/icons.qrc b/src/icons.qrc index 7214c0166..239136387 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -105,6 +105,8 @@ Icons/oxygen/edit-paste.png Icons/oxygen/run-build.png Icons/oxygen/proxy.png + Icons/oxygen/user-group-delete.png + Icons/oxygen/user-group-new.png Icons/oxygen/log.png Icons/oxygen/unavailable.png Icons/oxygen/button_ok.png @@ -142,6 +144,5 @@ Icons/oxygen/unsubscribe.png Icons/oxygen/draw-rectangle.png Icons/oxygen/subscribe16.png - Icons/oxygen/add_peer.png \ No newline at end of file diff --git a/src/peerlistdelegate.h b/src/peerlistdelegate.h index 9652b8fcd..7408a6de4 100644 --- a/src/peerlistdelegate.h +++ b/src/peerlistdelegate.h @@ -34,7 +34,7 @@ #include #include "misc.h" -enum PeerListColumns {IP, CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED, TOT_DOWN, TOT_UP}; +enum PeerListColumns {IP, CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED, TOT_DOWN, TOT_UP, IP_HIDDEN}; class PeerListDelegate: public QItemDelegate { Q_OBJECT diff --git a/src/peerlistwidget.cpp b/src/peerlistwidget.cpp index b7729c8ef..74e62e83e 100644 --- a/src/peerlistwidget.cpp +++ b/src/peerlistwidget.cpp @@ -48,8 +48,9 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent): properties(parent), di setRootIsDecorated(false); setItemsExpandable(false); setAllColumnsShowFocus(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); // List Model - listModel = new QStandardItemModel(0, 7); + listModel = new QStandardItemModel(0, 8); listModel->setHeaderData(IP, Qt::Horizontal, tr("IP")); listModel->setHeaderData(CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application")); listModel->setHeaderData(PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded")); @@ -62,6 +63,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent): properties(parent), di proxyModel->setDynamicSortFilter(true); proxyModel->setSourceModel(listModel); setModel(proxyModel); + hideColumn(IP_HIDDEN); // Context menu setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showPeerListMenu(QPoint))); @@ -112,26 +114,33 @@ void PeerListWidget::updatePeerCountryResolutionState() { void PeerListWidget::showPeerListMenu(QPoint) { QMenu menu; + bool empty_menu = true; QTorrentHandle h = properties->getCurrentTorrent(); if(!h.is_valid()) return; QModelIndexList selectedIndexes = selectionModel()->selectedRows(); QStringList selectedPeerIPs; foreach(const QModelIndex &index, selectedIndexes) { - QString IP = proxyModel->data(index).toString(); + int row = proxyModel->mapToSource(index).row(); + QString IP = listModel->data(listModel->index(row, IP_HIDDEN)).toString(); selectedPeerIPs << IP; } // Add Peer Action QAction *addPeerAct = 0; if(!h.is_queued() && !h.is_checking()) { - addPeerAct = menu.addAction(QIcon(":/Icons/oxygen/add_peer.png"), tr("Add a new peer")); + addPeerAct = menu.addAction(QIcon(":/Icons/oxygen/user-group-new.png"), tr("Add a new peer")); + empty_menu = false; } // Per Peer Speed limiting actions QAction *upLimitAct = 0; QAction *dlLimitAct = 0; + QAction *banAct = 0; if(!selectedPeerIPs.isEmpty()) { upLimitAct = menu.addAction(QIcon(":/Icons/skin/seeding.png"), tr("Limit upload rate")); dlLimitAct = menu.addAction(QIcon(":/Icons/skin/downloading.png"), tr("Limit download rate")); + banAct = menu.addAction(QIcon(":/Icons/oxygen/user-group-delete.png"), tr("Ban peer permanently")); + empty_menu = false; } + if(empty_menu) return; QAction *act = menu.exec(QCursor::pos()); if(act == addPeerAct) { boost::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint(); @@ -155,6 +164,25 @@ void PeerListWidget::showPeerListMenu(QPoint) { limitDlRateSelectedPeers(selectedPeerIPs); return; } + if(act == banAct) { + banSelectedPeers(selectedPeerIPs); + return; + } +} + +void PeerListWidget::banSelectedPeers(QStringList peer_ips) { + // Confirm first + int ret = QMessageBox::question(this, tr("Are you sure? -- qBittorrent"), tr("Are you sure you want to ban permanently the selected peers?"), + tr("&Yes"), tr("&No"), + QString(), 0, 1); + if(ret) return; + foreach(const QString &ip, peer_ips) { + qDebug("Banning peer %s...", ip.toLocal8Bit().data()); + properties->getBTSession()->addConsoleMessage(tr("Manually banning peer %1...").arg(ip)); + properties->getBTSession()->banIP(ip); + } + // Refresh list + loadPeers(properties->getCurrentTorrent()); } void PeerListWidget::limitUpRateSelectedPeers(QStringList peer_ips) { @@ -270,6 +298,7 @@ QStandardItem* PeerListWidget::addPeer(QString ip, peer_info peer) { // Adding Peer to peer list listModel->insertRow(row); listModel->setData(listModel->index(row, IP), ip); + listModel->setData(listModel->index(row, IP_HIDDEN), ip); // Resolve peer host name is asked if(resolver) resolver->resolve(peer.ip); diff --git a/src/peerlistwidget.h b/src/peerlistwidget.h index 35b9a0049..ee15cd7bb 100644 --- a/src/peerlistwidget.h +++ b/src/peerlistwidget.h @@ -78,6 +78,7 @@ protected slots: void showPeerListMenu(QPoint); void limitUpRateSelectedPeers(QStringList peer_ips); void limitDlRateSelectedPeers(QStringList peer_ips); + void banSelectedPeers(QStringList peer_ips); }; #endif // PEERLISTWIDGET_H diff --git a/src/preferences.h b/src/preferences.h index 6bf7df93c..cf02933e2 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -367,6 +367,20 @@ public: return settings.value(QString::fromUtf8("Preferences/IPFilter/File"), QString()).toString(); } + static void banIP(QString ip) { + QSettings settings("qBittorrent", "qBittorrent"); + QStringList banned_ips = settings.value(QString::fromUtf8("Preferences/IPFilter/BannedIPs"), QStringList()).toStringList(); + if(!banned_ips.contains(ip)) { + banned_ips << ip; + settings.setValue("Preferences/IPFilter/BannedIPs", banned_ips); + } + } + + static QStringList bannedIPs() { + QSettings settings("qBittorrent", "qBittorrent"); + return settings.value(QString::fromUtf8("Preferences/IPFilter/BannedIPs"), QStringList()).toStringList(); + } + // RSS static bool isRSSEnabled() { QSettings settings("qBittorrent", "qBittorrent"); @@ -423,6 +437,7 @@ public: QSettings settings("qBittorrent", "qBittorrent"); return settings.value("Preferences/WebUI/Password", "").toString(); } + }; #endif // PREFERENCES_H diff --git a/src/propertieswidget.cpp b/src/propertieswidget.cpp index a05a59fe3..ff1413f7b 100644 --- a/src/propertieswidget.cpp +++ b/src/propertieswidget.cpp @@ -186,6 +186,10 @@ const QTorrentHandle& PropertiesWidget::getCurrentTorrent() const { return h; } +bittorrent* PropertiesWidget::getBTSession() const { + return BTSession; +} + void PropertiesWidget::loadTorrentInfos(QTorrentHandle &_h) { h = _h; if(!h.is_valid()) { diff --git a/src/propertieswidget.h b/src/propertieswidget.h index cefce5e4b..896174705 100644 --- a/src/propertieswidget.h +++ b/src/propertieswidget.h @@ -113,6 +113,7 @@ public: PropertiesWidget(QWidget *parent, TransferListWidget *transferList, bittorrent* BTSession); ~PropertiesWidget(); const QTorrentHandle& getCurrentTorrent() const; + bittorrent* getBTSession() const; }; #endif // PROPERTIESWIDGET_H