PIVX Core  5.6.99
P2P Digital Currency
rpcexecutor.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2017 The Bitcoin developers
2 // Copyright (c) 2015-2021 The PIVX Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "qt/rpcexecutor.h"
7 
8 #include "rpc/client.h"
9 
10 #ifdef ENABLE_WALLET
11 #include "wallet/wallet.h"
12 #endif
13 
14 #include <QUrlQuery>
15 
16 #include <univalue.h>
17 
18 QString RPCExecutor::categoryClass(int category)
19 {
20  switch (category) {
21  case CMD_REQUEST:
22  return "cmd-request";
23  break;
24  case CMD_REPLY:
25  return "cmd-reply";
26  break;
27  case CMD_ERROR:
28  return "cmd-error";
29  break;
30  default:
31  return "misc";
32  }
33 }
34 
35 void RPCExecutor::request(const QString& command)
36 {
37  try
38  {
39  std::string result;
40  std::string executableCommand = command.toStdString() + "\n";
41 
42  // Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
43  if(executableCommand == "help-console\n")
44  {
45  Q_EMIT reply(CMD_REPLY, QString(("\n"
46  "This console accepts RPC commands using the standard syntax.\n"
47  " example: getblockhash 0\n\n"
48 
49  "This console can also accept RPC commands using parenthesized syntax.\n"
50  " example: getblockhash(0)\n\n"
51 
52  "Commands may be nested when specified with the parenthesized syntax.\n"
53  " example: getblock(getblockhash(0) true)\n\n"
54 
55  "A space or a comma can be used to delimit arguments for either syntax.\n"
56  " example: getblockhash 0\n"
57  " getblockhash,0\n\n"
58 
59  "Named results can be queried with a non-quoted key string in brackets.\n"
60  " example: getblock(getblockhash(0) true)[tx]\n\n"
61 
62  "Results without keys can be queried using an integer in brackets.\n"
63  " example: getblock(getblockhash(0),true)[tx][0]\n\n")));
64  return;
65  }
66  if(!ExecuteCommandLine(result, executableCommand))
67  {
68  Q_EMIT reply(CMD_ERROR, QString("Parse error: unbalanced ' or \""));
69  return;
70  }
71  Q_EMIT reply(CMD_REPLY, QString::fromStdString(result));
72  }
73  catch (UniValue& objError)
74  {
75  try {
76  // Nice formatting for standard-format error
77  int code = find_value(objError, "code").get_int();
78  std::string message = find_value(objError, "message").get_str();
79  Q_EMIT reply(CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
80 
81  } catch (const std::runtime_error&) {
82  // raised when converting to invalid type, i.e. missing code or message
83  // Show raw JSON object
84  Q_EMIT reply(CMD_ERROR, QString::fromStdString(objError.write()));
85  }
86  } catch (const std::exception& e) {
87  Q_EMIT reply(CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));
88  }
89 }
90 
107 bool RPCExecutor::ExecuteCommandLine(std::string& strResult, const std::string& strCommand)
108 {
109  std::vector< std::vector<std::string> > stack;
110  stack.push_back(std::vector<std::string>());
111 
112  enum CmdParseState
113  {
114  STATE_EATING_SPACES,
115  STATE_EATING_SPACES_IN_ARG,
116  STATE_EATING_SPACES_IN_BRACKETS,
117  STATE_ARGUMENT,
118  STATE_SINGLEQUOTED,
119  STATE_DOUBLEQUOTED,
120  STATE_ESCAPE_OUTER,
121  STATE_ESCAPE_DOUBLEQUOTED,
122  STATE_COMMAND_EXECUTED,
123  STATE_COMMAND_EXECUTED_INNER
124  } state = STATE_EATING_SPACES;
125  std::string curarg;
126  UniValue lastResult;
127 
128  std::string strCommandTerminated = strCommand;
129  if (strCommandTerminated.back() != '\n')
130  strCommandTerminated += "\n";
131  for(char ch: strCommandTerminated)
132  {
133  switch(state)
134  {
135  case STATE_COMMAND_EXECUTED_INNER:
136  case STATE_COMMAND_EXECUTED:
137  {
138  bool breakParsing = true;
139  switch(ch)
140  {
141  case '[': curarg.clear(); state = STATE_COMMAND_EXECUTED_INNER; break;
142  default:
143  if (state == STATE_COMMAND_EXECUTED_INNER)
144  {
145  if (ch != ']')
146  {
147  // append char to the current argument (which is also used for the query command)
148  curarg += ch;
149  break;
150  }
151  if (curarg.size())
152  {
153  // if we have a value query, query arrays with index and objects with a string key
154  UniValue subelement;
155  if (lastResult.isArray())
156  {
157  for(char argch: curarg)
158  if (!std::isdigit(argch))
159  throw std::runtime_error("Invalid result query");
160  subelement = lastResult[atoi(curarg.c_str())];
161  }
162  else if (lastResult.isObject())
163  subelement = find_value(lastResult, curarg);
164  else
165  throw std::runtime_error("Invalid result query"); //no array or object: abort
166  lastResult = subelement;
167  }
168 
169  state = STATE_COMMAND_EXECUTED;
170  break;
171  }
172  // don't break parsing when the char is required for the next argument
173  breakParsing = false;
174 
175  // pop the stack and return the result to the current command arguments
176  stack.pop_back();
177 
178  // don't stringify the json in case of a string to avoid doublequotes
179  if (lastResult.isStr())
180  curarg = lastResult.get_str();
181  else
182  curarg = lastResult.write(2);
183 
184  // if we have a non empty result, use it as stack argument otherwise as general result
185  if (curarg.size())
186  {
187  if (stack.size())
188  stack.back().push_back(curarg);
189  else
190  strResult = curarg;
191  }
192  curarg.clear();
193  // assume eating space state
194  state = STATE_EATING_SPACES;
195  }
196  if (breakParsing)
197  break;
198  }
199  case STATE_ARGUMENT: // In or after argument
200  case STATE_EATING_SPACES_IN_ARG:
201  case STATE_EATING_SPACES_IN_BRACKETS:
202  case STATE_EATING_SPACES: // Handle runs of whitespace
203  switch(ch)
204  {
205  case '"': state = STATE_DOUBLEQUOTED; break;
206  case '\'': state = STATE_SINGLEQUOTED; break;
207  case '\\': state = STATE_ESCAPE_OUTER; break;
208  case '(': case ')': case '\n':
209  if (state == STATE_EATING_SPACES_IN_ARG)
210  throw std::runtime_error("Invalid Syntax");
211  if (state == STATE_ARGUMENT)
212  {
213  if (ch == '(' && stack.size() && stack.back().size() > 0)
214  stack.push_back(std::vector<std::string>());
215 
216  // don't allow commands after executed commands on baselevel
217  if (!stack.size())
218  throw std::runtime_error("Invalid Syntax");
219 
220  stack.back().push_back(curarg);
221  curarg.clear();
222  state = STATE_EATING_SPACES_IN_BRACKETS;
223  }
224  if ((ch == ')' || ch == '\n') && stack.size() > 0)
225  {
226  std::string strPrint;
227  // Convert argument list to JSON objects in method-dependent way,
228  // and pass it along with the method name to the dispatcher.
229  JSONRPCRequest req;
230  req.params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
231  req.strMethod = stack.back()[0];
232 #ifdef ENABLE_WALLET
233  // TODO: Move this logic to WalletModel
234  if (!vpwallets.empty()) {
235  // in Qt, use always the wallet with index 0 when running with multiple wallets
236  QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(vpwallets[0]->GetName()));
237  req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
238  }
239 #endif
240  lastResult = tableRPC.execute(req);
241  state = STATE_COMMAND_EXECUTED;
242  curarg.clear();
243  }
244  break;
245  case ' ': case ',': case '\t':
246  if(state == STATE_EATING_SPACES_IN_ARG && curarg.empty() && ch == ',')
247  throw std::runtime_error("Invalid Syntax");
248 
249  else if(state == STATE_ARGUMENT) // Space ends argument
250  {
251  stack.back().push_back(curarg);
252  curarg.clear();
253  }
254  if ((state == STATE_EATING_SPACES_IN_BRACKETS || state == STATE_ARGUMENT) && ch == ',')
255  {
256  state = STATE_EATING_SPACES_IN_ARG;
257  break;
258  }
259  state = STATE_EATING_SPACES;
260  break;
261  default: curarg += ch; state = STATE_ARGUMENT;
262  }
263  break;
264  case STATE_SINGLEQUOTED: // Single-quoted string
265  switch(ch)
266  {
267  case '\'': state = STATE_ARGUMENT; break;
268  default: curarg += ch;
269  }
270  break;
271  case STATE_DOUBLEQUOTED: // Double-quoted string
272  switch(ch)
273  {
274  case '"': state = STATE_ARGUMENT; break;
275  case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break;
276  default: curarg += ch;
277  }
278  break;
279  case STATE_ESCAPE_OUTER: // '\' outside quotes
280  curarg += ch; state = STATE_ARGUMENT;
281  break;
282  case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
283  if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself
284  curarg += ch; state = STATE_DOUBLEQUOTED;
285  break;
286  }
287  }
288  switch (state) // final state
289  {
290  case STATE_COMMAND_EXECUTED:
291  if (lastResult.isStr())
292  strResult = lastResult.get_str();
293  else
294  strResult = lastResult.write(2);
295  case STATE_ARGUMENT:
296  case STATE_EATING_SPACES:
297  return true;
298  default: // ERROR to end in one of the other states
299  return false;
300  }
301 }
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:493
UniValue params
Definition: server.h:47
std::string strMethod
Definition: server.h:46
std::string URI
Definition: server.h:49
void reply(int category, const QString &command)
void request(const QString &command)
Definition: rpcexecutor.cpp:35
static QString categoryClass(int category)
Definition: rpcexecutor.cpp:18
bool ExecuteCommandLine(std::string &strResult, const std::string &strCommand)
Split shell command line into a list of arguments and execute the command(s).
const std::string & get_str() const
bool isArray() const
Definition: univalue.h:83
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void clear()
Definition: univalue.cpp:15
bool isStr() const
Definition: univalue.h:81
bool isObject() const
Definition: univalue.h:84
int get_int() const
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:250
CRPCTable tableRPC
Definition: server.cpp:565
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:234
int atoi(const std::string &str)
std::vector< CWalletRef > vpwallets
Definition: wallet.cpp:33