Changeset View
Changeset View
Standalone View
Standalone View
src/qt/rpcconsole.cpp
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | const QStringList historyFilter = QStringList() << "importprivkey" | ||||
<< "walletpassphrasechange" | << "walletpassphrasechange" | ||||
<< "encryptwallet"; | << "encryptwallet"; | ||||
} // namespace | } // namespace | ||||
/* Object for executing console RPC commands in a separate thread. | /* Object for executing console RPC commands in a separate thread. | ||||
*/ | */ | ||||
class RPCExecutor : public QObject { | class RPCExecutor : public QObject { | ||||
Q_OBJECT | Q_OBJECT | ||||
public: | |||||
RPCExecutor(interfaces::Node &node) : m_node(node) {} | |||||
public Q_SLOTS: | public Q_SLOTS: | ||||
void request(const QString &command, const QString &walletID); | void request(const QString &command, const QString &walletID); | ||||
Q_SIGNALS: | Q_SIGNALS: | ||||
void reply(int category, const QString &command); | void reply(int category, const QString &command); | ||||
private: | |||||
interfaces::Node &m_node; | |||||
}; | }; | ||||
/** Class for handling RPC timers | /** Class for handling RPC timers | ||||
* (used for e.g. re-locking the wallet after a timeout) | * (used for e.g. re-locking the wallet after a timeout) | ||||
*/ | */ | ||||
class QtRPCTimerBase : public QObject, public RPCTimerBase { | class QtRPCTimerBase : public QObject, public RPCTimerBase { | ||||
Q_OBJECT | Q_OBJECT | ||||
public: | public: | ||||
Show All 37 Lines | |||||
* - Text can be "double" or 'single' quoted | * - Text can be "double" or 'single' quoted | ||||
* - The backslash \c \ is used as escape character | * - The backslash \c \ is used as escape character | ||||
* - Outside quotes, any character can be escaped | * - Outside quotes, any character can be escaped | ||||
* - Within double quotes, only escape \c " and backslashes before a \c " or | * - Within double quotes, only escape \c " and backslashes before a \c " or | ||||
* another backslash | * another backslash | ||||
* - Within single quotes, no escaping is possible and no special | * - Within single quotes, no escaping is possible and no special | ||||
* interpretation takes place | * interpretation takes place | ||||
* | * | ||||
* @param[in] node optional node to execute command on | |||||
* @param[out] result stringified Result from the executed command(chain) | * @param[out] result stringified Result from the executed command(chain) | ||||
* @param[in] strCommand Command line to split | * @param[in] strCommand Command line to split | ||||
* @param[in] fExecute set true if you want the command to be executed | * @param[in] fExecute set true if you want the command to be executed | ||||
* @param[out] pstrFilteredOut Command line, filtered to remove any sensitive | * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive | ||||
* data | * data | ||||
*/ | */ | ||||
bool RPCConsole::RPCParseCommandLine(std::string &strResult, | bool RPCConsole::RPCParseCommandLine(interfaces::Node *node, | ||||
std::string &strResult, | |||||
const std::string &strCommand, | const std::string &strCommand, | ||||
const bool fExecute, | const bool fExecute, | ||||
std::string *const pstrFilteredOut, | std::string *const pstrFilteredOut, | ||||
const std::string *walletID) { | const std::string *walletID) { | ||||
std::vector<std::vector<std::string>> stack; | std::vector<std::vector<std::string>> stack; | ||||
stack.push_back(std::vector<std::string>()); | stack.push_back(std::vector<std::string>()); | ||||
enum CmdParseState { | enum CmdParseState { | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) { | ||||
curarg.clear(); | curarg.clear(); | ||||
state = STATE_EATING_SPACES_IN_BRACKETS; | state = STATE_EATING_SPACES_IN_BRACKETS; | ||||
} | } | ||||
if ((ch == ')' || ch == '\n') && stack.size() > 0) { | if ((ch == ')' || ch == '\n') && stack.size() > 0) { | ||||
if (fExecute) { | if (fExecute) { | ||||
// Convert argument list to JSON objects in | // Convert argument list to JSON objects in | ||||
// method-dependent way, and pass it along with | // method-dependent way, and pass it along with | ||||
// the method name to the dispatcher. | // the method name to the dispatcher. | ||||
JSONRPCRequest req; | UniValue params = RPCConvertValues( | ||||
req.params = RPCConvertValues( | |||||
stack.back()[0], | stack.back()[0], | ||||
std::vector<std::string>( | std::vector<std::string>( | ||||
stack.back().begin() + 1, | stack.back().begin() + 1, | ||||
stack.back().end())); | stack.back().end())); | ||||
req.strMethod = stack.back()[0]; | std::string method = stack.back()[0]; | ||||
std::string uri; | |||||
#ifdef ENABLE_WALLET | #ifdef ENABLE_WALLET | ||||
if (walletID && !walletID->empty()) { | if (walletID && !walletID->empty()) { | ||||
QByteArray encodedName = | QByteArray encodedName = | ||||
QUrl::toPercentEncoding( | QUrl::toPercentEncoding( | ||||
QString::fromStdString(*walletID)); | QString::fromStdString(*walletID)); | ||||
req.URI = | uri = "/wallet/" + | ||||
"/wallet/" + | |||||
std::string(encodedName.constData(), | std::string(encodedName.constData(), | ||||
encodedName.length()); | encodedName.length()); | ||||
} | } | ||||
#endif | #endif | ||||
GlobalConfig config; | GlobalConfig config; | ||||
lastResult = tableRPC.execute(config, req); | assert(node); | ||||
lastResult = node->executeRpc(config, method, | |||||
params, uri); | |||||
} | } | ||||
state = STATE_COMMAND_EXECUTED; | state = STATE_COMMAND_EXECUTED; | ||||
curarg.clear(); | curarg.clear(); | ||||
} | } | ||||
break; | break; | ||||
case ' ': | case ' ': | ||||
case ',': | case ',': | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | try { | ||||
"Results without keys can be queried using an integer " | "Results without keys can be queried using an integer " | ||||
"in brackets.\n" | "in brackets.\n" | ||||
" example: " | " example: " | ||||
"getblock(getblockhash(0),true)[tx][0]\n\n"))); | "getblock(getblockhash(0),true)[tx][0]\n\n"))); | ||||
return; | return; | ||||
} | } | ||||
std::string wallet_id = walletID.toStdString(); | std::string wallet_id = walletID.toStdString(); | ||||
if (!RPCConsole::RPCExecuteCommandLine(result, executableCommand, | if (!RPCConsole::RPCExecuteCommandLine( | ||||
nullptr, &wallet_id)) { | m_node, result, executableCommand, nullptr, &wallet_id)) { | ||||
Q_EMIT reply(RPCConsole::CMD_ERROR, | Q_EMIT reply(RPCConsole::CMD_ERROR, | ||||
QString("Parse error: unbalanced ' or \"")); | QString("Parse error: unbalanced ' or \"")); | ||||
return; | return; | ||||
} | } | ||||
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); | Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result)); | ||||
} catch (UniValue &objError) { | } catch (UniValue &objError) { | ||||
// Nice formatting for standard-format error | // Nice formatting for standard-format error | ||||
Show All 10 Lines | try { | ||||
QString::fromStdString(objError.write())); | QString::fromStdString(objError.write())); | ||||
} | } | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
Q_EMIT reply(RPCConsole::CMD_ERROR, | Q_EMIT reply(RPCConsole::CMD_ERROR, | ||||
QString("Error: ") + QString::fromStdString(e.what())); | QString("Error: ") + QString::fromStdString(e.what())); | ||||
} | } | ||||
} | } | ||||
RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) | RPCConsole::RPCConsole(interfaces::Node &node, | ||||
: QWidget(parent), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), | const PlatformStyle *_platformStyle, QWidget *parent) | ||||
platformStyle(_platformStyle), peersTableContextMenu(0), | : QWidget(parent), m_node(node), ui(new Ui::RPCConsole), clientModel(0), | ||||
historyPtr(0), platformStyle(_platformStyle), peersTableContextMenu(0), | |||||
banTableContextMenu(0), consoleFontSize(0) { | banTableContextMenu(0), consoleFontSize(0) { | ||||
ui->setupUi(this); | ui->setupUi(this); | ||||
QSettings settings; | QSettings settings; | ||||
if (!restoreGeometry( | if (!restoreGeometry( | ||||
settings.value("RPCConsoleWindowGeometry").toByteArray())) { | settings.value("RPCConsoleWindowGeometry").toByteArray())) { | ||||
// Restore failed (perhaps missing setting), center the window | // Restore failed (perhaps missing setting), center the window | ||||
move(QApplication::desktop()->availableGeometry().center() - | move(QApplication::desktop()->availableGeometry().center() - | ||||
frameGeometry().center()); | frameGeometry().center()); | ||||
Show All 33 Lines | |||||
#else | #else | ||||
ui->label_berkeleyDBVersion->hide(); | ui->label_berkeleyDBVersion->hide(); | ||||
ui->berkeleyDBVersion->hide(); | ui->berkeleyDBVersion->hide(); | ||||
#endif | #endif | ||||
// Register RPC timer interface | // Register RPC timer interface | ||||
rpcTimerInterface = new QtRPCTimerInterface(); | rpcTimerInterface = new QtRPCTimerInterface(); | ||||
// avoid accidentally overwriting an existing, non QTThread | // avoid accidentally overwriting an existing, non QTThread | ||||
// based timer interface | // based timer interface | ||||
RPCSetTimerInterfaceIfUnset(rpcTimerInterface); | m_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface); | ||||
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); | setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); | ||||
ui->detailWidget->hide(); | ui->detailWidget->hide(); | ||||
ui->peerHeading->setText(tr("Select a peer to view detailed information.")); | ui->peerHeading->setText(tr("Select a peer to view detailed information.")); | ||||
consoleFontSize = | consoleFontSize = | ||||
settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()) | settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()) | ||||
.toInt(); | .toInt(); | ||||
clear(); | clear(); | ||||
} | } | ||||
RPCConsole::~RPCConsole() { | RPCConsole::~RPCConsole() { | ||||
QSettings settings; | QSettings settings; | ||||
settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); | settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); | ||||
RPCUnsetTimerInterface(rpcTimerInterface); | m_node.rpcUnsetTimerInterface(rpcTimerInterface); | ||||
delete rpcTimerInterface; | delete rpcTimerInterface; | ||||
delete ui; | delete ui; | ||||
} | } | ||||
bool RPCConsole::eventFilter(QObject *obj, QEvent *event) { | bool RPCConsole::eventFilter(QObject *obj, QEvent *event) { | ||||
// Special key handling | // Special key handling | ||||
if (event->type() == QEvent::KeyPress) { | if (event->type() == QEvent::KeyPress) { | ||||
QKeyEvent *keyevt = static_cast<QKeyEvent *>(event); | QKeyEvent *keyevt = static_cast<QKeyEvent *>(event); | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | if (model && clientModel->getPeerTableModel() && | ||||
ui->clientUserAgent->setText(model->formatSubVersion()); | ui->clientUserAgent->setText(model->formatSubVersion()); | ||||
ui->dataDir->setText(model->dataDir()); | ui->dataDir->setText(model->dataDir()); | ||||
ui->startupTime->setText(model->formatClientStartupTime()); | ui->startupTime->setText(model->formatClientStartupTime()); | ||||
ui->networkName->setText( | ui->networkName->setText( | ||||
QString::fromStdString(Params().NetworkIDString())); | QString::fromStdString(Params().NetworkIDString())); | ||||
// Setup autocomplete and attach it | // Setup autocomplete and attach it | ||||
QStringList wordList; | QStringList wordList; | ||||
std::vector<std::string> commandList = tableRPC.listCommands(); | std::vector<std::string> commandList = m_node.listRpcCommands(); | ||||
for (size_t i = 0; i < commandList.size(); ++i) { | for (size_t i = 0; i < commandList.size(); ++i) { | ||||
wordList << commandList[i].c_str(); | wordList << commandList[i].c_str(); | ||||
wordList << ("help " + commandList[i]).c_str(); | wordList << ("help " + commandList[i]).c_str(); | ||||
} | } | ||||
wordList << "help-console"; | wordList << "help-console"; | ||||
wordList.sort(); | wordList.sort(); | ||||
autoCompleter = new QCompleter(wordList, this); | autoCompleter = new QCompleter(wordList, this); | ||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | |||||
void RPCConsole::on_lineEdit_returnPressed() { | void RPCConsole::on_lineEdit_returnPressed() { | ||||
QString cmd = ui->lineEdit->text(); | QString cmd = ui->lineEdit->text(); | ||||
if (!cmd.isEmpty()) { | if (!cmd.isEmpty()) { | ||||
std::string strFilteredCmd; | std::string strFilteredCmd; | ||||
try { | try { | ||||
std::string dummy; | std::string dummy; | ||||
if (!RPCParseCommandLine(dummy, cmd.toStdString(), false, | if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, | ||||
&strFilteredCmd)) { | &strFilteredCmd)) { | ||||
// Failed to parse command, so we cannot even filter it for the | // Failed to parse command, so we cannot even filter it for the | ||||
// history | // history | ||||
throw std::runtime_error("Invalid command line"); | throw std::runtime_error("Invalid command line"); | ||||
} | } | ||||
} catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
QMessageBox::critical(this, "Error", | QMessageBox::critical(this, "Error", | ||||
QString("Error: ") + | QString("Error: ") + | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if (historyPtr < history.size()) { | ||||
cmd = history.at(historyPtr); | cmd = history.at(historyPtr); | ||||
} else if (!cmdBeforeBrowsing.isNull()) { | } else if (!cmdBeforeBrowsing.isNull()) { | ||||
cmd = cmdBeforeBrowsing; | cmd = cmdBeforeBrowsing; | ||||
} | } | ||||
ui->lineEdit->setText(cmd); | ui->lineEdit->setText(cmd); | ||||
} | } | ||||
void RPCConsole::startExecutor() { | void RPCConsole::startExecutor() { | ||||
RPCExecutor *executor = new RPCExecutor(); | RPCExecutor *executor = new RPCExecutor(m_node); | ||||
executor->moveToThread(&thread); | executor->moveToThread(&thread); | ||||
// Replies from executor object must go to this object | // Replies from executor object must go to this object | ||||
connect(executor, SIGNAL(reply(int, QString)), this, | connect(executor, SIGNAL(reply(int, QString)), this, | ||||
SLOT(message(int, QString))); | SLOT(message(int, QString))); | ||||
// Requests from this object must go to executor | // Requests from this object must go to executor | ||||
connect(this, SIGNAL(cmdRequest(QString, QString)), executor, | connect(this, SIGNAL(cmdRequest(QString, QString)), executor, | ||||
SLOT(request(QString, QString))); | SLOT(request(QString, QString))); | ||||
▲ Show 20 Lines • Show All 231 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
void RPCConsole::showBanTableContextMenu(const QPoint &point) { | void RPCConsole::showBanTableContextMenu(const QPoint &point) { | ||||
QModelIndex index = ui->banlistWidget->indexAt(point); | QModelIndex index = ui->banlistWidget->indexAt(point); | ||||
if (index.isValid()) banTableContextMenu->exec(QCursor::pos()); | if (index.isValid()) banTableContextMenu->exec(QCursor::pos()); | ||||
} | } | ||||
void RPCConsole::disconnectSelectedNode() { | void RPCConsole::disconnectSelectedNode() { | ||||
if (!g_connman) { | |||||
return; | |||||
} | |||||
// Get selected peer addresses | // Get selected peer addresses | ||||
QList<QModelIndex> nodes = | QList<QModelIndex> nodes = | ||||
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); | GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); | ||||
for (int i = 0; i < nodes.count(); i++) { | for (int i = 0; i < nodes.count(); i++) { | ||||
// Get currently selected peer address | // Get currently selected peer address | ||||
NodeId id = nodes.at(i).data().toLongLong(); | NodeId id = nodes.at(i).data().toLongLong(); | ||||
// Find the node, disconnect it and clear the selected node | // Find the node, disconnect it and clear the selected node | ||||
if (g_connman->DisconnectNode(id)) clearSelectedNode(); | if (m_node.disconnect(id)) { | ||||
clearSelectedNode(); | |||||
} | |||||
} | } | ||||
} | } | ||||
void RPCConsole::banSelectedNode(int bantime) { | void RPCConsole::banSelectedNode(int bantime) { | ||||
if (!clientModel || !g_connman) { | if (!clientModel) { | ||||
return; | return; | ||||
} | } | ||||
// Get selected peer addresses | // Get selected peer addresses | ||||
QList<QModelIndex> nodes = | QList<QModelIndex> nodes = | ||||
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); | GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); | ||||
for (int i = 0; i < nodes.count(); i++) { | for (int i = 0; i < nodes.count(); i++) { | ||||
// Get currently selected peer address | // Get currently selected peer address | ||||
NodeId id = nodes.at(i).data().toLongLong(); | NodeId id = nodes.at(i).data().toLongLong(); | ||||
// Get currently selected peer address | // Get currently selected peer address | ||||
int detailNodeRow = | int detailNodeRow = | ||||
clientModel->getPeerTableModel()->getRowByNodeId(id); | clientModel->getPeerTableModel()->getRowByNodeId(id); | ||||
if (detailNodeRow < 0) { | if (detailNodeRow < 0) { | ||||
return; | return; | ||||
} | } | ||||
// Find possible nodes, ban it and clear the selected node | // Find possible nodes, ban it and clear the selected node | ||||
const CNodeCombinedStats *stats = | const CNodeCombinedStats *stats = | ||||
clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); | clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); | ||||
if (stats) { | if (stats) { | ||||
g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, | m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); | ||||
bantime); | |||||
} | } | ||||
} | } | ||||
clearSelectedNode(); | clearSelectedNode(); | ||||
clientModel->getBanTableModel()->refresh(); | clientModel->getBanTableModel()->refresh(); | ||||
} | } | ||||
void RPCConsole::unbanSelectedNode() { | void RPCConsole::unbanSelectedNode() { | ||||
if (!clientModel) { | if (!clientModel) { | ||||
return; | return; | ||||
} | } | ||||
// Get selected ban addresses | // Get selected ban addresses | ||||
QList<QModelIndex> nodes = | QList<QModelIndex> nodes = | ||||
GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address); | GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address); | ||||
for (int i = 0; i < nodes.count(); i++) { | for (int i = 0; i < nodes.count(); i++) { | ||||
// Get currently selected ban address | // Get currently selected ban address | ||||
QString strNode = nodes.at(i).data().toString(); | QString strNode = nodes.at(i).data().toString(); | ||||
CSubNet possibleSubnet; | CSubNet possibleSubnet; | ||||
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet); | LookupSubNet(strNode.toStdString().c_str(), possibleSubnet); | ||||
if (possibleSubnet.IsValid() && g_connman) { | if (possibleSubnet.IsValid() && m_node.unban(possibleSubnet)) { | ||||
g_connman->Unban(possibleSubnet); | |||||
clientModel->getBanTableModel()->refresh(); | clientModel->getBanTableModel()->refresh(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void RPCConsole::clearSelectedNode() { | void RPCConsole::clearSelectedNode() { | ||||
ui->peerWidget->selectionModel()->clearSelection(); | ui->peerWidget->selectionModel()->clearSelection(); | ||||
cachedNodeids.clear(); | cachedNodeids.clear(); | ||||
Show All 17 Lines |