264 lines
6.6 KiB
C++
264 lines
6.6 KiB
C++
/*
|
|
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "adchpp.h"
|
|
|
|
#include "AdcCommand.h"
|
|
|
|
#include "Text.h"
|
|
|
|
namespace adchpp {
|
|
|
|
#ifdef __GNUC__ /// @todo go figure why some GCCs need these...
|
|
const char AdcCommand::TYPE_BROADCAST;
|
|
const char AdcCommand::TYPE_CLIENT;
|
|
const char AdcCommand::TYPE_DIRECT;
|
|
const char AdcCommand::TYPE_ECHO;
|
|
const char AdcCommand::TYPE_FEATURE;
|
|
const char AdcCommand::TYPE_INFO;
|
|
const char AdcCommand::TYPE_HUB;
|
|
const char AdcCommand::TYPE_UDP;
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
AdcCommand::AdcCommand() : cmdInt(0), priority(PRIORITY_NORMAL), from(INVALID_SID), to(INVALID_SID), type(TYPE_INFO) { }
|
|
|
|
AdcCommand::AdcCommand(Severity sev, Error err, const string& desc, char aType /* = TYPE_INFO */) : cmdInt(CMD_STA), priority(PRIORITY_NORMAL), from(HUB_SID), to(INVALID_SID), type(aType) {
|
|
addParam(Util::toString(sev * 100 + err));
|
|
addParam(desc);
|
|
}
|
|
|
|
void AdcCommand::escape(const string& s, string& out) {
|
|
out.reserve(out.length() + static_cast<size_t>(s.length() * 1.1));
|
|
|
|
for(string::const_iterator i = s.begin(), iend = s.end(); i != iend; ++i) {
|
|
switch(*i) {
|
|
case ' ': out += '\\'; out += 's'; break;
|
|
case '\n': out += '\\'; out += 'n'; break;
|
|
case '\\': out += '\\'; out += '\\'; break;
|
|
default: out += *i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdcCommand::parse(const char* buf, size_t len) throw(ParseException) {
|
|
if(len < 5) {
|
|
throw ParseException("Command too short");
|
|
}
|
|
|
|
type = buf[0];
|
|
|
|
if(type != TYPE_BROADCAST && type != TYPE_CLIENT && type != TYPE_DIRECT && type != TYPE_ECHO && type != TYPE_FEATURE && type != TYPE_INFO && type != TYPE_HUB && type != TYPE_UDP) {
|
|
throw ParseException("Invalid type");
|
|
}
|
|
|
|
if(type == TYPE_HUB) {
|
|
to = HUB_SID;
|
|
}
|
|
|
|
cmd[0] = buf[1];
|
|
cmd[1] = buf[2];
|
|
cmd[2] = buf[3];
|
|
|
|
if(buf[4] != ' ') {
|
|
throw ParseException("Missing space after command");
|
|
}
|
|
|
|
// Skip trailing LF
|
|
len--;
|
|
|
|
parameters.reserve(8);
|
|
|
|
string cur;
|
|
cur.reserve(64);
|
|
|
|
bool toSet = false;
|
|
bool featureSet = false;
|
|
bool fromSet = false;
|
|
|
|
string::size_type i = 5;
|
|
while(i < len) {
|
|
switch(buf[i]) {
|
|
case '\\':
|
|
++i;
|
|
if(i == len)
|
|
throw ParseException("Escape at eol");
|
|
if(buf[i] == 's')
|
|
cur += ' ';
|
|
else if(buf[i] == 'n')
|
|
cur += '\n';
|
|
else if(buf[i] == '\\')
|
|
cur += '\\';
|
|
else
|
|
throw ParseException("Unknown escape");
|
|
break;
|
|
case ' ':
|
|
// New parameter...
|
|
{
|
|
if((type == TYPE_BROADCAST || type == TYPE_DIRECT || type == TYPE_ECHO || type == TYPE_FEATURE) && !fromSet) {
|
|
if(cur.length() != 4) {
|
|
throw ParseException("Invalid SID length");
|
|
}
|
|
from = toSID(cur);
|
|
fromSet = true;
|
|
} else if((type == TYPE_DIRECT || type == TYPE_ECHO) && !toSet) {
|
|
if(cur.length() != 4) {
|
|
throw ParseException("Invalid SID length");
|
|
}
|
|
to = toSID(cur);
|
|
toSet = true;
|
|
} else if(type == TYPE_FEATURE && !featureSet) {
|
|
if(cur.length() % 5 != 0) {
|
|
throw ParseException("Invalid feature length");
|
|
}
|
|
features = cur;
|
|
featureSet = true;
|
|
} else {
|
|
if(!Text::validateUtf8(cur)) {
|
|
throw ParseException("Invalid UTF-8 sequence");
|
|
}
|
|
parameters.push_back(cur);
|
|
}
|
|
cur.clear();
|
|
}
|
|
break;
|
|
default:
|
|
cur += buf[i];
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if(!cur.empty()) {
|
|
if((type == TYPE_BROADCAST || type == TYPE_DIRECT || type == TYPE_ECHO || type == TYPE_FEATURE) && !fromSet) {
|
|
if(cur.length() != 4) {
|
|
throw ParseException("Invalid SID length");
|
|
}
|
|
from = toSID(cur);
|
|
fromSet = true;
|
|
} else if((type == TYPE_DIRECT || type == TYPE_ECHO) && !toSet) {
|
|
if(cur.length() != 4) {
|
|
throw ParseException("Invalid SID length");
|
|
}
|
|
to = toSID(cur);
|
|
toSet = true;
|
|
} else if(type == TYPE_FEATURE && !featureSet) {
|
|
if(cur.length() % 5 != 0) {
|
|
throw ParseException("Invalid feature length");
|
|
}
|
|
features = cur;
|
|
featureSet = true;
|
|
} else {
|
|
if(!Text::validateUtf8(cur)) {
|
|
throw ParseException("Invalid UTF-8 sequence");
|
|
}
|
|
parameters.push_back(cur);
|
|
}
|
|
}
|
|
|
|
if((type == TYPE_BROADCAST || type == TYPE_DIRECT || type == TYPE_ECHO || type == TYPE_FEATURE) && !fromSet) {
|
|
throw ParseException("Missing from_sid");
|
|
}
|
|
|
|
if(type == TYPE_FEATURE && !featureSet) {
|
|
throw ParseException("Missing feature");
|
|
}
|
|
|
|
if((type == TYPE_DIRECT || type == TYPE_ECHO) && !toSet) {
|
|
throw ParseException("Missing to_sid");
|
|
}
|
|
}
|
|
|
|
const BufferPtr& AdcCommand::getBuffer() const {
|
|
if(!buffer) {
|
|
buffer = make_shared<Buffer>(toString());
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
string AdcCommand::toString() const {
|
|
if(buffer) {
|
|
return string((char*)buffer->data(), buffer->size());
|
|
}
|
|
string tmp;
|
|
|
|
tmp.reserve(128);
|
|
|
|
tmp += type;
|
|
tmp += cmdChar;
|
|
|
|
if(type == TYPE_BROADCAST || type == TYPE_DIRECT || type == TYPE_ECHO || type == TYPE_FEATURE) {
|
|
tmp += ' ';
|
|
appendSID(tmp, from);
|
|
}
|
|
|
|
if(type == TYPE_DIRECT || type == TYPE_ECHO) {
|
|
tmp += ' ';
|
|
appendSID(tmp, to);
|
|
}
|
|
|
|
if(type == TYPE_FEATURE) {
|
|
tmp += ' ';
|
|
tmp += features;
|
|
}
|
|
|
|
for(StringIterC i = getParameters().begin(); i != getParameters().end(); ++i) {
|
|
tmp += ' ';
|
|
escape(*i, tmp);
|
|
}
|
|
|
|
tmp += '\n';
|
|
|
|
return tmp;
|
|
}
|
|
|
|
bool AdcCommand::getParam(const char* name, size_t start, string& ret) const {
|
|
for(string::size_type i = start; i < getParameters().size(); ++i) {
|
|
if(toField(name) == toField(getParameters()[i].c_str())) {
|
|
ret = getParameters()[i].substr(2);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AdcCommand::delParam(const char* name, size_t start) {
|
|
for(string::size_type i = start; i < getParameters().size(); ++i) {
|
|
if(toField(name) == toField(getParameters()[i].c_str())) {
|
|
getParameters().erase(getParameters().begin() + i);
|
|
resetBuffer();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AdcCommand::hasFlag(const char* name, size_t start) const {
|
|
for(string::size_type i = start; i < getParameters().size(); ++i) {
|
|
if(toField(name) == toField(getParameters()[i].c_str()) &&
|
|
getParameters()[i].size() == 3 &&
|
|
getParameters()[i][2] == '1')
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|