@@ -100,97 +100,191 @@ class QtRPCTimerInterface: public RPCTimerInterface
100100#include " qt/pivx/settings/moc_settingsconsolewidget.cpp"
101101
102102/* *
103- * Split shell command line into a list of arguments. Aims to emulate \c bash and friends.
103+ * Split shell command line into a list of arguments and execute the command(s).
104+ * Aims to emulate \c bash and friends.
104105 *
105- * - Arguments are delimited with whitespace
106+ * - Command nesting is possible with brackets [example: validateaddress(getnewaddress())]
107+ * - Arguments are delimited with whitespace or comma
106108 * - Extra whitespace at the beginning and end and between arguments will be ignored
107109 * - Text can be "double" or 'single' quoted
108110 * - The backslash \c \ is used as escape character
109111 * - Outside quotes, any character can be escaped
110112 * - Within double quotes, only escape \c " and backslashes before a \c " or another backslash
111113 * - Within single quotes, no escaping is possible and no special interpretation takes place
112114 *
113- * @param[out] args Parsed arguments will be appended to this list
115+ * @param[out] result stringified Result from the executed command(chain)
114116 * @param[in] strCommand Command line to split
115117 */
116- bool parseCommandLineSettings (std::vector<std:: string>& args , const std::string& strCommand)
118+ bool RPCExecuteCommandLine (std::string &strResult , const std::string & strCommand)
117119{
118- enum CmdParseState {
120+ std::vector< std::vector<std::string> > stack;
121+ stack.push_back (std::vector<std::string>());
122+
123+ enum CmdParseState
124+ {
119125 STATE_EATING_SPACES,
120126 STATE_ARGUMENT,
121127 STATE_SINGLEQUOTED,
122128 STATE_DOUBLEQUOTED,
123129 STATE_ESCAPE_OUTER,
124- STATE_ESCAPE_DOUBLEQUOTED
130+ STATE_ESCAPE_DOUBLEQUOTED,
131+ STATE_COMMAND_EXECUTED,
132+ STATE_COMMAND_EXECUTED_INNER
125133 } state = STATE_EATING_SPACES;
126134 std::string curarg;
127- for (char ch : strCommand) {
128- switch (state) {
129- case STATE_ARGUMENT: // In or after argument
130- case STATE_EATING_SPACES: // Handle runs of whitespace
131- switch (ch) {
132- case ' "' :
133- state = STATE_DOUBLEQUOTED;
134- break ;
135- case ' \' ' :
136- state = STATE_SINGLEQUOTED;
137- break ;
138- case ' \\ ' :
139- state = STATE_ESCAPE_OUTER;
140- break ;
141- case ' ' :
142- case ' \n ' :
143- case ' \t ' :
144- if (state == STATE_ARGUMENT) // Space ends argument
135+ UniValue lastResult;
136+
137+ std::string strCommandTerminated = strCommand;
138+ if (strCommandTerminated.back () != ' \n ' )
139+ strCommandTerminated += " \n " ;
140+ for (char ch: strCommandTerminated)
141+ {
142+ switch (state)
143+ {
144+ case STATE_COMMAND_EXECUTED_INNER:
145+ case STATE_COMMAND_EXECUTED:
146+ {
147+ bool breakParsing = true ;
148+ switch (ch)
149+ {
150+ case ' [' : curarg.clear (); state = STATE_COMMAND_EXECUTED_INNER; break ;
151+ default :
152+ if (state == STATE_COMMAND_EXECUTED_INNER)
153+ {
154+ if (ch != ' ]' )
155+ {
156+ // append char to the current argument (which is also used for the query command)
157+ curarg += ch;
158+ break ;
159+ }
160+ if (curarg.size ())
161+ {
162+ // if we have a value query, query arrays with index and objects with a string key
163+ UniValue subelement;
164+ if (lastResult.isArray ())
165+ {
166+ for (char argch: curarg)
167+ if (!std::isdigit (argch))
168+ throw std::runtime_error (" Invalid result query" );
169+ subelement = lastResult[atoi (curarg.c_str ())];
170+ }
171+ else if (lastResult.isObject ())
172+ subelement = find_value (lastResult, curarg);
173+ else
174+ throw std::runtime_error (" Invalid result query" ); // no array or object: abort
175+ lastResult = subelement;
176+ }
177+
178+ state = STATE_COMMAND_EXECUTED;
179+ break ;
180+ }
181+ // don't break parsing when the char is required for the next argument
182+ breakParsing = false ;
183+
184+ // pop the stack and return the result to the current command arguments
185+ stack.pop_back ();
186+
187+ // don't stringify the json in case of a string to avoid doublequotes
188+ if (lastResult.isStr ())
189+ curarg = lastResult.get_str ();
190+ else
191+ curarg = lastResult.write (2 );
192+
193+ // if we have a non empty result, use it as stack argument otherwise as general result
194+ if (curarg.size ())
145195 {
146- args.push_back (curarg);
147- curarg.clear ();
196+ if (stack.size ())
197+ stack.back ().push_back (curarg);
198+ else
199+ strResult = curarg;
148200 }
201+ curarg.clear ();
202+ // assume eating space state
149203 state = STATE_EATING_SPACES;
150- break ;
151- default :
152- curarg += ch;
153- state = STATE_ARGUMENT;
154204 }
205+ if (breakParsing)
206+ break ;
207+ }
208+ case STATE_ARGUMENT: // In or after argument
209+ case STATE_EATING_SPACES: // Handle runs of whitespace
210+ switch (ch)
211+ {
212+ case ' "' : state = STATE_DOUBLEQUOTED; break ;
213+ case ' \' ' : state = STATE_SINGLEQUOTED; break ;
214+ case ' \\ ' : state = STATE_ESCAPE_OUTER; break ;
215+ case ' (' : case ' )' : case ' \n ' :
216+ if (state == STATE_ARGUMENT)
217+ {
218+ if (ch == ' (' && stack.size () && stack.back ().size () > 0 )
219+ stack.push_back (std::vector<std::string>());
220+ if (curarg.size ())
221+ {
222+ // don't allow commands after executed commands on baselevel
223+ if (!stack.size ())
224+ throw std::runtime_error (" Invalid Syntax" );
225+ stack.back ().push_back (curarg);
226+ }
227+ curarg.clear ();
228+ state = STATE_EATING_SPACES;
229+ }
230+ if ((ch == ' )' || ch == ' \n ' ) && stack.size () > 0 )
231+ {
232+ std::string strPrint;
233+ // Convert argument list to JSON objects in method-dependent way,
234+ // and pass it along with the method name to the dispatcher.
235+ JSONRPCRequest req;
236+ req.params = RPCConvertValues (stack.back ()[0 ], std::vector<std::string>(stack.back ().begin () + 1 , stack.back ().end ()));
237+ req.strMethod = stack.back ()[0 ];
238+ lastResult = tableRPC.execute (req);
239+ state = STATE_COMMAND_EXECUTED;
240+ curarg.clear ();
241+ }
242+ break ;
243+ case ' ' : case ' ,' : case ' \t ' :
244+ if (state == STATE_ARGUMENT) // Space ends argument
245+ {
246+ if (curarg.size ())
247+ stack.back ().push_back (curarg);
248+ curarg.clear ();
249+ }
250+ state = STATE_EATING_SPACES;
251+ break ;
252+ default : curarg += ch; state = STATE_ARGUMENT;
253+ }
155254 break ;
156255 case STATE_SINGLEQUOTED: // Single-quoted string
157- switch (ch) {
158- case ' \' ' :
159- state = STATE_ARGUMENT;
160- break ;
161- default :
162- curarg += ch;
163- }
256+ switch (ch)
257+ {
258+ case ' \' ' : state = STATE_ARGUMENT; break ;
259+ default : curarg += ch;
260+ }
164261 break ;
165262 case STATE_DOUBLEQUOTED: // Double-quoted string
166- switch (ch) {
167- case ' "' :
168- state = STATE_ARGUMENT;
169- break ;
170- case ' \\ ' :
171- state = STATE_ESCAPE_DOUBLEQUOTED;
172- break ;
173- default :
174- curarg += ch;
175- }
263+ switch (ch)
264+ {
265+ case ' "' : state = STATE_ARGUMENT; break ;
266+ case ' \\ ' : state = STATE_ESCAPE_DOUBLEQUOTED; break ;
267+ default : curarg += ch;
268+ }
176269 break ;
177270 case STATE_ESCAPE_OUTER: // '\' outside quotes
178- curarg += ch;
179- state = STATE_ARGUMENT;
271+ curarg += ch; state = STATE_ARGUMENT;
180272 break ;
181- case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
182- if (ch != ' "' && ch != ' \\ ' ) curarg += ' \\ ' ; // keep '\' for everything but the quote and '\' itself
183- curarg += ch;
184- state = STATE_DOUBLEQUOTED;
273+ case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
274+ if (ch != ' "' && ch != ' \\ ' ) curarg += ' \\ ' ; // keep '\' for everything but the quote and '\' itself
275+ curarg += ch; state = STATE_DOUBLEQUOTED;
185276 break ;
186277 }
187278 }
188279 switch (state) // final state
189280 {
190- case STATE_EATING_SPACES:
191- return true ;
281+ case STATE_COMMAND_EXECUTED:
282+ if (lastResult.isStr ())
283+ strResult = lastResult.get_str ();
284+ else
285+ strResult = lastResult.write (2 );
192286 case STATE_ARGUMENT:
193- args. push_back (curarg);
287+ case STATE_EATING_SPACES:
194288 return true ;
195289 default : // ERROR to end in one of the other states
196290 return false ;
@@ -199,43 +293,26 @@ bool parseCommandLineSettings(std::vector<std::string>& args, const std::string&
199293
200294void RPCExecutor::requestCommand (const QString& command)
201295{
202- std::vector<std::string> args;
203- if (!parseCommandLineSettings (args, command.toStdString ())) {
204- Q_EMIT reply (SettingsConsoleWidget::CMD_ERROR, QString (" Parse error: unbalanced ' or \" " ));
205- return ;
206- }
207- if (args.empty ())
208- return ; // Nothing to do
209296 try {
210- std::string strPrint;
211- // Convert argument list to JSON objects in method-dependent way,
212- // and pass it along with the method name to the dispatcher.
213- JSONRPCRequest req;
214- req.params = RPCConvertValues (args[0 ], std::vector<std::string>(args.begin () + 1 , args.end ()));
215- req.strMethod = args[0 ];
216- UniValue result = tableRPC.execute (req);
217-
218- // Format result reply
219- if (result.isNull ())
220- strPrint = " " ;
221- else if (result.isStr ())
222- strPrint = result.get_str ();
223- else
224- strPrint = result.write (2 );
225-
226- Q_EMIT reply (SettingsConsoleWidget::CMD_REPLY, QString::fromStdString (strPrint));
297+ std::string result;
298+ std::string executableCommand = command.toStdString () + " \n " ;
299+ if (!RPCExecuteCommandLine (result, executableCommand)) {
300+ Q_EMIT reply (SettingsConsoleWidget::CMD_ERROR, QString (" Parse error: unbalanced ' or \" " ));
301+ return ;
302+ }
303+ Q_EMIT reply (SettingsConsoleWidget::CMD_REPLY, QString::fromStdString (result));
227304 } catch (UniValue& objError) {
228305 try // Nice formatting for standard-format error
229306 {
230307 int code = find_value (objError, " code" ).get_int ();
231308 std::string message = find_value (objError, " message" ).get_str ();
232- Q_EMIT reply (SettingsConsoleWidget ::CMD_ERROR, QString::fromStdString (message) + " (code " + QString::number (code) + " )" );
233- } catch (std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
309+ Q_EMIT reply (RPCConsole ::CMD_ERROR, QString::fromStdString (message) + " (code " + QString::number (code) + " )" );
310+ } catch (const std::runtime_error&) // raised when converting to invalid type, i.e. missing code or message
234311 { // Show raw JSON object
235- Q_EMIT reply (SettingsConsoleWidget ::CMD_ERROR, QString::fromStdString (objError.write ()));
312+ Q_EMIT reply (RPCConsole ::CMD_ERROR, QString::fromStdString (objError.write ()));
236313 }
237- } catch (std::exception& e) {
238- Q_EMIT reply (SettingsConsoleWidget ::CMD_ERROR, QString (" Error: " ) + QString::fromStdString (e.what ()));
314+ } catch (const std::exception& e) {
315+ Q_EMIT reply (RPCConsole ::CMD_ERROR, QString (" Error: " ) + QString::fromStdString (e.what ()));
239316 }
240317}
241318
@@ -457,18 +534,11 @@ static bool PotentiallyDangerousCommand(const QString& cmd)
457534 return true ;
458535 }
459536 if (cmd.size () >= 13 && cmd.leftRef (11 ) == " dumpprivkey" ) {
460- // valid PIVX Transparent Address
461- std::vector<std::string> args;
462- parseCommandLineSettings (args, cmd.toStdString ());
463- return (args.size () == 2 && IsValidDestinationString (args[1 ], false ));
537+ return true ;
464538 }
465539 if (cmd.size () >= 18 && cmd.leftRef (16 ) == " exportsaplingkey" ) {
466- // valid PIVX Shield Address
467- std::vector<std::string> args;
468- parseCommandLineSettings (args, cmd.toStdString ());
469- return (args.size () == 2 && KeyIO::IsValidPaymentAddressString (args[1 ]));
540+ return true ;
470541 }
471-
472542 return false ;
473543}
474544
0 commit comments