diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2012-08-30 21:42:18 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2012-08-31 17:23:48 +0200 |
commit | 576b5efe93cdcd544c205f4c4f8f1696e6e907ee (patch) | |
tree | 64742f77c908b479fa21a6ebb9d7d923926e3377 | |
parent | a65b53bf06e4fb1988e054041f30cbaecb11cc23 (diff) |
Fix RPC console parser to handle escaped arguments more like bash
- Fix issue #1750
-rw-r--r-- | src/qt/rpcconsole.cpp | 118 |
1 files changed, 98 insertions, 20 deletions
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 08f936e719..470eba732c 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -13,7 +13,6 @@ #include <QUrl> #include <QScrollBar> -#include <boost/tokenizer.hpp> #include <openssl/crypto.h> // TODO: make it possible to filter out categories (esp debug messages when implemented) @@ -54,34 +53,113 @@ void RPCExecutor::start() // Nothing to do } -void RPCExecutor::request(const QString &command) +/** + * Split shell command line into a list of arguments. Aims to emulate \c bash and friends. + * + * - Arguments are delimited with whitespace + * - Extra whitespace at the beginning and end and between arguments will be ignored + * - Arguments can be "double" or 'single' quoted. Those are treated the same. + * - The backslash '\' is used as escape character + * - Outside quotes, any character can be escaped + * - Within double quotes, only escape double quotes with \" and backslashes with \\ + * - Within single quotes, only escape single quotes with \' and backslashes with \\ + * + * @param[out] args Parsed arguments will be appended to this list + * @param[in] strCommand Command line to split + */ +bool parseCommandLine(std::vector<std::string> &args, const std::string &strCommand) { - // Parse shell-like command line into separate arguments - std::string strMethod; - std::vector<std::string> strParams; - try { - boost::escaped_list_separator<char> els('\\',' ','\"'); - std::string strCommand = command.toStdString(); - boost::tokenizer<boost::escaped_list_separator<char> > tok(strCommand, els); - - int n = 0; - for(boost::tokenizer<boost::escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg,++n) + enum CmdParseState + { + STATE_EATING_SPACES, + STATE_ARGUMENT, + STATE_SINGLEQUOTED, + STATE_DOUBLEQUOTED, + STATE_ESCAPE_OUTER, + STATE_ESCAPE_SINGLEQUOTED, + STATE_ESCAPE_DOUBLEQUOTED + } state = STATE_EATING_SPACES; + std::string curarg; + foreach(char ch, strCommand) + { + switch(state) { - if(n == 0) // First parameter is the command - strMethod = *beg; - else - strParams.push_back(*beg); + case STATE_ARGUMENT: // After argument + case STATE_EATING_SPACES: // Handle runs of spaces + switch(ch) + { + case '"': state = STATE_DOUBLEQUOTED; break; + case '\'': state = STATE_SINGLEQUOTED; break; + case '\\': state = STATE_ESCAPE_OUTER; break; + case ' ': case '\n': case '\t': + if(state == STATE_ARGUMENT) // Space ends argument + { + args.push_back(curarg); + curarg.clear(); + } + state = STATE_EATING_SPACES; + break; + default: curarg += ch; state = STATE_ARGUMENT; + } + break; + case STATE_SINGLEQUOTED: // Single-quoted string + switch(ch) + { + case '\'': state = STATE_ARGUMENT; break; + case '\\': state = STATE_ESCAPE_SINGLEQUOTED; break; + default: curarg += ch; + } + break; + case STATE_DOUBLEQUOTED: // Double-quoted string + switch(ch) + { + case '"': state = STATE_ARGUMENT; break; + case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break; + default: curarg += ch; + } + break; + case STATE_ESCAPE_OUTER: // '\' outside quotes + curarg += ch; state = STATE_ARGUMENT; + break; + case STATE_ESCAPE_SINGLEQUOTED: // '\' in single-quoted text + if(ch != '\'') curarg += '\\'; // keep '\' for everything but the quote + curarg += ch; state = STATE_SINGLEQUOTED; + break; + case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text + if(ch != '"') curarg += '\\'; // keep '\' for everything but the quote + curarg += ch; state = STATE_DOUBLEQUOTED; + break; } } - catch(boost::escaped_list_error &e) + switch(state) // final state { - emit reply(RPCConsole::CMD_ERROR, QString("Parse error")); - return; + case STATE_EATING_SPACES: + return true; + case STATE_ARGUMENT: + args.push_back(curarg); + return true; + default: // ERROR to end in one of the other states + return false; } +} +void RPCExecutor::request(const QString &command) +{ + std::vector<std::string> args; + if(!parseCommandLine(args, command.toStdString())) + { + emit reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); + return; + } + if(args.empty()) + return; // Nothing to do try { std::string strPrint; - json_spirit::Value result = tableRPC.execute(strMethod, RPCConvertValues(strMethod, strParams)); + // Convert argument list to JSON objects in method-dependent way, + // and pass it along with the method name to the dispatcher. + json_spirit::Value result = tableRPC.execute( + args[0], + RPCConvertValues(args[0], std::vector<std::string>(args.begin() + 1, args.end()))); // Format result reply if (result.type() == json_spirit::null_type) |