246 lines
9.3 KiB
C++
246 lines
9.3 KiB
C++
#include "Command.h"
|
|
#include "command_exception.h"
|
|
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <pipes/buffer.h>
|
|
#include "escape.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace ts {
|
|
/*
|
|
Command Command::parse(const pipes::buffer_view& _buffer, bool expect_command) { //FIXME improve!
|
|
string buffer = _buffer.string();
|
|
Command result;
|
|
|
|
size_t nextSpace;
|
|
if(expect_command) {
|
|
nextSpace = buffer.find(' ');
|
|
if(nextSpace == -1){
|
|
result._command = buffer;
|
|
return result;
|
|
}
|
|
|
|
result._command = buffer.substr(0, nextSpace);
|
|
buffer = buffer.substr(nextSpace + 1);
|
|
}
|
|
|
|
int splitIndex = 0;
|
|
do {
|
|
auto nextSplit = buffer.find('|');
|
|
auto splitBuffer = buffer.substr(0, nextSplit);
|
|
if(nextSplit != std::string::npos)
|
|
buffer = buffer.substr(nextSplit + 1);
|
|
else
|
|
buffer = "";
|
|
if(splitBuffer.empty()) continue;
|
|
|
|
|
|
std::string element;
|
|
ssize_t elementEnd;
|
|
do {
|
|
elementEnd = splitBuffer.find(' ');
|
|
element = splitBuffer.substr(0, elementEnd);
|
|
if(element.empty()) goto nextElement;
|
|
|
|
if(element[0] == '-'){
|
|
result.paramethers.push_back(element.substr(1));
|
|
} else {
|
|
auto key = element.substr(0, element.find('='));
|
|
key.erase(std::remove_if(key.begin(), key.end(), [](const char c){ return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-'); }), key.end());
|
|
if(key.length() > 0 && (key.front() == ' ' || key.back() == ' ')) {
|
|
auto first = key.find_first_not_of(' ');
|
|
if(first == std::string::npos) continue;
|
|
auto last = key.find_last_not_of(' ');
|
|
key = key.substr(first, last - first);
|
|
}
|
|
std::string value;
|
|
if(element.find('=') != std::string::npos)
|
|
value = element.substr(element.find('=') + 1);
|
|
if(key.empty() && value.empty()) goto nextElement;
|
|
if(result[splitIndex].has(key)) goto nextElement; //No double insert. Sometimes malformed input 'banadd ip name=WolverinDEV time=3600 banreason=Test\sban! return_code=1:38 return_code=__1:38_1:38'
|
|
result[splitIndex][key] = query::unescape(value, true);
|
|
}
|
|
|
|
nextElement:
|
|
if(elementEnd != std::string::npos && elementEnd + 1 < splitBuffer.size())
|
|
splitBuffer = splitBuffer.substr(elementEnd + 1);
|
|
else
|
|
splitBuffer = "";
|
|
} while(!splitBuffer.empty());
|
|
splitIndex++;
|
|
} while(!buffer.empty());
|
|
|
|
|
|
return result;
|
|
}
|
|
*/
|
|
|
|
Command Command::parse(const pipes::buffer_view &buffer, bool expect_type, bool drop_non_utf8) {
|
|
string_view data{buffer.data_ptr<const char>(), buffer.length()};
|
|
|
|
Command result;
|
|
|
|
size_t current_index = std::string::npos, end_index;
|
|
if(expect_type) {
|
|
current_index = data.find(' ', 0);
|
|
if(current_index == std::string::npos){
|
|
result._command = std::string(data);
|
|
return result;
|
|
} else {
|
|
result._command = std::string(data.substr(0, current_index));
|
|
}
|
|
}
|
|
|
|
size_t bulk_index = 0;
|
|
while(++current_index > 0 || (current_index == 0 && !expect_type && (expect_type = true))) {
|
|
end_index = data.find_first_of(" |", current_index);
|
|
|
|
if(end_index != current_index && current_index < data.length()) { /* else we've found another space or a pipe */
|
|
if(data[current_index] == '-') {
|
|
string trigger(data.substr(current_index + 1, end_index - current_index - 1));
|
|
result.paramethers.push_back(trigger);
|
|
} else {
|
|
auto index_assign = data.find_first_of('=', current_index);
|
|
string key, value;
|
|
if(index_assign == string::npos || index_assign > end_index) {
|
|
key = data.substr(current_index, end_index - current_index);
|
|
} else {
|
|
key = data.substr(current_index, index_assign - current_index);
|
|
try {
|
|
value = query::unescape(string(data.substr(index_assign + 1, end_index - index_assign - 1)), true);
|
|
} catch(const std::invalid_argument& ex) {
|
|
(void) ex;
|
|
|
|
/* invalid character at index X */
|
|
if(!drop_non_utf8)
|
|
throw;
|
|
goto skip_assign;
|
|
}
|
|
}
|
|
|
|
{
|
|
const static auto key_validator = [](char c){ return !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-'); };
|
|
auto invalid_index = find_if(key.begin(), key.end(), key_validator);
|
|
if(invalid_index != key.end())
|
|
throw command_malformed_exception(current_index + distance(key.begin(), invalid_index));
|
|
|
|
if(!key.empty() && !result[bulk_index].has(key)) //No double insert. Sometimes malformed input 'banadd ip name=WolverinDEV time=3600 banreason=Test\sban! return_code=1:38 return_code=__1:38_1:38'
|
|
result[bulk_index][key] = value;
|
|
}
|
|
skip_assign:;
|
|
}
|
|
}
|
|
|
|
if(end_index < data.length() && data[end_index] == '|')
|
|
bulk_index++;
|
|
current_index = end_index;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Command::Command(const std::string& command) {
|
|
this->_command = command;
|
|
}
|
|
|
|
Command::Command(const std::string& command, std::initializer_list<variable> parms) : Command(command, {parms}) { }
|
|
|
|
|
|
Command::Command(const std::string& command, std::initializer_list<std::initializer_list<variable>> bulks) : Command(command) {
|
|
int blkIndex = 0;
|
|
for(auto blk = bulks.begin(); blk != bulks.end(); blk++, blkIndex++)
|
|
for(auto it = blk->begin(); it != blk->end(); it++)
|
|
operator[](blkIndex).parms.push_back(*it);
|
|
}
|
|
|
|
Command::Command(const Command& other) {
|
|
this->_command = other._command;
|
|
this->bulks = other.bulks;
|
|
this->paramethers = other.paramethers;
|
|
}
|
|
|
|
Command::Command() = default;
|
|
|
|
Command::~Command() = default;
|
|
|
|
std::string Command::build(bool escaped) const {
|
|
std::stringstream out;
|
|
out << this->_command;
|
|
|
|
bool bulkBegin = false;
|
|
for(auto it = this->bulks.begin(); it != this->bulks.end(); it++){
|
|
for(const auto& elm : it->parms) {
|
|
if(!bulkBegin) out << " ";
|
|
else bulkBegin = false;
|
|
if(elm.key().empty()) {
|
|
out << elm.value(); /* special case used for permission list */
|
|
} else {
|
|
out << elm.key();
|
|
if(!elm.value().empty()){
|
|
out << "=" << (escaped ? query::escape(elm.value()) : ("'" + elm.value() + "'"));
|
|
}
|
|
}
|
|
}
|
|
if(it + 1 != this->bulks.end()){
|
|
out << "|";
|
|
bulkBegin = true;
|
|
}
|
|
}
|
|
for(const auto& parm : this->paramethers) out << " -" << parm;
|
|
|
|
auto str = out.str();
|
|
if(str.length() > 1) if(str[0] == ' ') str = str.substr(1);
|
|
return str;
|
|
}
|
|
|
|
#ifdef HAVE_JSON
|
|
Json::Value Command::buildJson() const {
|
|
Json::Value result;
|
|
result["command"] = this->_command;
|
|
|
|
int index = 0;
|
|
for(auto it = this->bulks.begin(); it != this->bulks.end(); it++){
|
|
Json::Value& node = result["data"][index++];
|
|
for(const auto& elm : it->parms)
|
|
node[elm.key()] = elm.value();
|
|
}
|
|
|
|
Json::Value& triggers = result["triggers"];
|
|
index = 0;
|
|
for(const auto& parm : this->paramethers)
|
|
triggers[index++] = parm;
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
std::deque<std::string> Command::parms() { return this->paramethers; }
|
|
bool Command::hasParm(std::string parm) { return std::find(this->paramethers.begin(), this->paramethers.end(), parm) != this->paramethers.end(); }
|
|
|
|
void Command::toggleParm(const std::string& key, bool flag) {
|
|
if(flag){
|
|
if(!hasParm(key)) this->paramethers.push_back(key);
|
|
} else {
|
|
auto found = std::find(this->paramethers.begin(), this->paramethers.end(), key);
|
|
if(found != this->paramethers.end())
|
|
this->paramethers.erase(found);
|
|
};
|
|
}
|
|
|
|
void Command::reverseBulks() {
|
|
std::reverse(this->bulks.begin(), this->bulks.end());
|
|
}
|
|
|
|
void Command::pop_bulk() {
|
|
if(!this->bulks.empty())
|
|
this->bulks.pop_front();
|
|
}
|
|
|
|
void Command::push_bulk_front() {
|
|
this->bulks.push_front(ParameterBulk{});
|
|
}
|
|
}
|