aboutsummaryrefslogtreecommitdiff
path: root/src/qt/rpcconsole.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt/rpcconsole.cpp')
-rw-r--r--src/qt/rpcconsole.cpp195
1 files changed, 173 insertions, 22 deletions
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index d9d4f1d0ed..72a3023c9a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -3,12 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "rpcconsole.h"
-#include "ui_rpcconsole.h"
+#include "ui_debugwindow.h"
+#include "bantablemodel.h"
#include "clientmodel.h"
#include "guiutil.h"
-#include "peertablemodel.h"
#include "platformstyle.h"
+#include "bantablemodel.h"
#include "chainparams.h"
#include "rpcserver.h"
@@ -26,8 +27,10 @@
#include <QKeyEvent>
#include <QMenu>
#include <QScrollBar>
+#include <QSignalMapper>
#include <QThread>
#include <QTime>
+#include <QTimer>
#if QT_VERSION < 0x050000
#include <QUrl>
@@ -66,6 +69,40 @@ Q_SIGNALS:
void reply(int category, const QString &command);
};
+/** Class for handling RPC timers
+ * (used for e.g. re-locking the wallet after a timeout)
+ */
+class QtRPCTimerBase: public QObject, public RPCTimerBase
+{
+ Q_OBJECT
+public:
+ QtRPCTimerBase(boost::function<void(void)>& func, int64_t millis):
+ func(func)
+ {
+ timer.setSingleShot(true);
+ connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
+ timer.start(millis);
+ }
+ ~QtRPCTimerBase() {}
+private Q_SLOTS:
+ void timeout() { func(); }
+private:
+ QTimer timer;
+ boost::function<void(void)> func;
+};
+
+class QtRPCTimerInterface: public RPCTimerInterface
+{
+public:
+ ~QtRPCTimerInterface() {}
+ const char *Name() { return "Qt"; }
+ RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
+ {
+ return new QtRPCTimerBase(func, millis);
+ }
+};
+
+
#include "rpcconsole.moc"
/**
@@ -206,8 +243,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
clientModel(0),
historyPtr(0),
cachedNodeid(-1),
- contextMenu(0),
- platformStyle(platformStyle)
+ platformStyle(platformStyle),
+ peersTableContextMenu(0),
+ banTableContextMenu(0)
{
ui->setupUi(this);
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
@@ -232,6 +270,9 @@ RPCConsole::RPCConsole(const PlatformStyle *platformStyle, QWidget *parent) :
ui->label_berkeleyDBVersion->hide();
ui->berkeleyDBVersion->hide();
#endif
+ // Register RPC timer interface
+ rpcTimerInterface = new QtRPCTimerInterface();
+ RPCRegisterTimerInterface(rpcTimerInterface);
startExecutor();
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
@@ -246,6 +287,8 @@ RPCConsole::~RPCConsole()
{
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
Q_EMIT stopExecutor();
+ RPCUnregisterTimerInterface(rpcTimerInterface);
+ delete rpcTimerInterface;
delete ui;
}
@@ -289,8 +332,7 @@ void RPCConsole::setClientModel(ClientModel *model)
{
clientModel = model;
ui->trafficGraph->setClientModel(model);
- if(model)
- {
+ if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) {
// Keep up to date with client
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
@@ -311,23 +353,75 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
+ ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
- // create context menu actions
+ // create peer table context menu actions
QAction* disconnectAction = new QAction(tr("&Disconnect Node"), this);
-
- // create context menu
- contextMenu = new QMenu();
- contextMenu->addAction(disconnectAction);
-
- // context menu signals
- connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMenu(const QPoint&)));
+ QAction* banAction1h = new QAction(tr("Ban Node for") + " " + tr("1 &hour"), this);
+ QAction* banAction24h = new QAction(tr("Ban Node for") + " " + tr("1 &day"), this);
+ QAction* banAction7d = new QAction(tr("Ban Node for") + " " + tr("1 &week"), this);
+ QAction* banAction365d = new QAction(tr("Ban Node for") + " " + tr("1 &year"), this);
+
+ // create peer table context menu
+ peersTableContextMenu = new QMenu();
+ peersTableContextMenu->addAction(disconnectAction);
+ peersTableContextMenu->addAction(banAction1h);
+ peersTableContextMenu->addAction(banAction24h);
+ peersTableContextMenu->addAction(banAction7d);
+ peersTableContextMenu->addAction(banAction365d);
+
+ // Add a signal mapping to allow dynamic context menu arguments.
+ // We need to use int (instead of int64_t), because signal mapper only supports
+ // int or objects, which is okay because max bantime (1 year) is < int_max.
+ QSignalMapper* signalMapper = new QSignalMapper(this);
+ signalMapper->setMapping(banAction1h, 60*60);
+ signalMapper->setMapping(banAction24h, 60*60*24);
+ signalMapper->setMapping(banAction7d, 60*60*24*7);
+ signalMapper->setMapping(banAction365d, 60*60*24*365);
+ connect(banAction1h, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(banAction24h, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(banAction7d, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(banAction365d, SIGNAL(triggered()), signalMapper, SLOT(map()));
+ connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(banSelectedNode(int)));
+
+ // peer table context menu signals
+ connect(ui->peerWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPeersTableContextMenu(const QPoint&)));
connect(disconnectAction, SIGNAL(triggered()), this, SLOT(disconnectSelectedNode()));
- // connect the peerWidget selection model to our peerSelected() handler
+ // peer table signal handling - update peer details when selecting new node
connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
- this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
+ this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
+ // peer table signal handling - update peer details when new nodes are added to the model
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
+ // set up ban table
+ ui->banlistWidget->setModel(model->getBanTableModel());
+ ui->banlistWidget->verticalHeader()->hide();
+ ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
+ ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+ ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
+ ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
+ ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
+
+ // create ban table context menu action
+ QAction* unbanAction = new QAction(tr("&Unban Node"), this);
+
+ // create ban table context menu
+ banTableContextMenu = new QMenu();
+ banTableContextMenu->addAction(unbanAction);
+
+ // ban table context menu signals
+ connect(ui->banlistWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showBanTableContextMenu(const QPoint&)));
+ connect(unbanAction, SIGNAL(triggered()), this, SLOT(unbanSelectedNode()));
+
+ // ban table signal handling - clear peer details when clicking a peer in the ban table
+ connect(ui->banlistWidget, SIGNAL(clicked(const QModelIndex&)), this, SLOT(clearSelectedNode()));
+ // ban table signal handling - ensure ban table is shown or hidden (if empty)
+ connect(model->getBanTableModel(), SIGNAL(layoutChanged()), this, SLOT(showOrHideBanTableIfRequired()));
+ showOrHideBanTableIfRequired();
+
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientUserAgent->setText(model->formatSubVersion());
@@ -537,7 +631,7 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
{
Q_UNUSED(deselected);
- if (!clientModel || selected.indexes().isEmpty())
+ if (!clientModel || !clientModel->getPeerTableModel() || selected.indexes().isEmpty())
return;
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
@@ -547,7 +641,7 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
void RPCConsole::peerLayoutChanged()
{
- if (!clientModel)
+ if (!clientModel || !clientModel->getPeerTableModel())
return;
const CNodeCombinedStats *stats = NULL;
@@ -656,7 +750,7 @@ void RPCConsole::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
- if (!clientModel)
+ if (!clientModel || !clientModel->getPeerTableModel())
return;
// start PeerTableModel auto refresh
@@ -667,18 +761,25 @@ void RPCConsole::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
- if (!clientModel)
+ if (!clientModel || !clientModel->getPeerTableModel())
return;
// stop PeerTableModel auto refresh
clientModel->getPeerTableModel()->stopAutoRefresh();
}
-void RPCConsole::showMenu(const QPoint& point)
+void RPCConsole::showPeersTableContextMenu(const QPoint& point)
{
QModelIndex index = ui->peerWidget->indexAt(point);
if (index.isValid())
- contextMenu->exec(QCursor::pos());
+ peersTableContextMenu->exec(QCursor::pos());
+}
+
+void RPCConsole::showBanTableContextMenu(const QPoint& point)
+{
+ QModelIndex index = ui->banlistWidget->indexAt(point);
+ if (index.isValid())
+ banTableContextMenu->exec(QCursor::pos());
}
void RPCConsole::disconnectSelectedNode()
@@ -692,6 +793,46 @@ void RPCConsole::disconnectSelectedNode()
}
}
+void RPCConsole::banSelectedNode(int bantime)
+{
+ if (!clientModel)
+ return;
+
+ // Get currently selected peer address
+ QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address);
+ // Find possible nodes, ban it and clear the selected node
+ if (CNode *bannedNode = FindNode(strNode.toStdString())) {
+ std::string nStr = strNode.toStdString();
+ std::string addr;
+ int port = 0;
+ SplitHostPort(nStr, port, addr);
+
+ CNode::Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime);
+ bannedNode->fDisconnect = true;
+ DumpBanlist();
+
+ clearSelectedNode();
+ clientModel->getBanTableModel()->refresh();
+ }
+}
+
+void RPCConsole::unbanSelectedNode()
+{
+ if (!clientModel)
+ return;
+
+ // Get currently selected ban address
+ QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address);
+ CSubNet possibleSubnet(strNode.toStdString());
+
+ if (possibleSubnet.IsValid())
+ {
+ CNode::Unban(possibleSubnet);
+ DumpBanlist();
+ clientModel->getBanTableModel()->refresh();
+ }
+}
+
void RPCConsole::clearSelectedNode()
{
ui->peerWidget->selectionModel()->clearSelection();
@@ -699,3 +840,13 @@ void RPCConsole::clearSelectedNode()
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
}
+
+void RPCConsole::showOrHideBanTableIfRequired()
+{
+ if (!clientModel)
+ return;
+
+ bool visible = clientModel->getBanTableModel()->shouldShow();
+ ui->banlistWidget->setVisible(visible);
+ ui->banHeading->setVisible(visible);
+} \ No newline at end of file