1
0
mirror of https://github.com/binary-kitchen/doorlockd synced 2024-10-31 22:47:05 +01:00

Big rewrite of several things

- Data type Token changed from uint64_t to std::string
- Added new class "Request" that describes a JSON TCP request
- Classes may now throw Responses for proper error handling
- Removed JSON parsing from Logic
- proper Error handling everywhere
- Many small fixes
- removed unnecessary includes
- removed using namespace std everywhere
This commit is contained in:
Ralf Ramsauer 2015-10-01 22:09:55 +02:00
parent abf69d70fa
commit 49a5b88f6c
16 changed files with 370 additions and 268 deletions

View File

@ -67,6 +67,8 @@ set(LIBDOORLOCK_SRCS
lib/logger.h
lib/logic.cpp
lib/logic.h
lib/request.cpp
lib/request.h
lib/response.cpp
lib/response.h
lib/util.cpp

View File

@ -71,7 +71,6 @@ static int doorlock_client(const std::string &hostname,
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = ba::error::host_not_found;
size_t length;
std::vector<char> data;
while (error && endpoint_iterator != end) {
@ -97,7 +96,8 @@ static int doorlock_client(const std::string &hostname,
throw boost::system::system_error(ec);
}
const auto message = Clientmessage::fromJson(std::string(data.begin(), data.begin()+length));
const auto message = Clientmessage::fromString(
std::string(data.begin(), data.begin()+length));
onDoorlockUpdate(message);
receiveMessage();
@ -107,13 +107,15 @@ static int doorlock_client(const std::string &hostname,
receiveMessage();
io_service.run();
}
catch(const std::exception &e)
{
l(LogLevel::error, e.what());
goto out;
catch(const Response &err) {
l(err.message, LogLevel::error);
retval = -1;
}
catch(const std::exception &err) {
l(LogLevel::error, err.what());
retval = -1;
}
out:
return retval;
}

View File

@ -9,24 +9,22 @@
#include "daemon.h"
using namespace std;
void daemonize(const string &dir,
const string &stdinfile,
const string &stdoutfile,
const string &stderrfile)
void daemonize(const std::string &dir,
const std::string &stdinfile,
const std::string &stdoutfile,
const std::string &stderrfile)
{
umask(0);
rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
{
throw runtime_error(strerror(errno));
throw std::runtime_error(strerror(errno));
}
if (!dir.empty() && chdir(dir.c_str()) < 0)
{
throw runtime_error(strerror(errno));
throw std::runtime_error(strerror(errno));
}
if (rl.rlim_max == RLIM_INFINITY)
@ -47,6 +45,6 @@ void daemonize(const string &dir,
if (fd0 != STDIN_FILENO || fd1 != STDOUT_FILENO || fd2 != STDERR_FILENO)
{
throw runtime_error("new standard file descriptors were not opened as expected");
throw std::runtime_error("new standard file descriptors were not opened as expected");
}
}

View File

@ -1,9 +1,5 @@
#include <iostream>
#include <string>
#include <cstdlib>
#include <memory>
#include <utility>
#include <csignal>
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/asio.hpp>
@ -11,13 +7,13 @@
#include <json/json.h>
#include "../lib/logic.h"
#include "../lib/util.h"
#include "config.h"
#include "daemon.h"
namespace po = boost::program_options;
using boost::asio::ip::tcp;
namespace ba = boost::asio;
using ba::ip::tcp;
// Info about doorlockd version
const static std::string version =
@ -31,7 +27,7 @@ const int constexpr SOCKET_BUFFERLENGTH = 2048;
const static Logger &l = Logger::get();
static std::unique_ptr<Logic> logic = nullptr;
static boost::asio::io_service io_service;
static ba::io_service io_service;
static std::mutex mutex;
static std::condition_variable onClientMessage;
@ -46,13 +42,26 @@ static void signal_handler(int signum)
onClientMessage.notify_all();
}
static Response subscribe(tcp::socket &sock)
{
sock.write_some(ba::buffer(logic->getClientMessage().toJson()));
while (run) {
std::unique_lock<std::mutex> lock(mutex);
onClientMessage.wait(lock);
if (run) {
sock.write_some(ba::buffer(logic->getClientMessage().toJson()));
}
};
return Response(Response::Code::Success);
}
static void session(tcp::socket &&sock)
{
boost::asio::ip::address remoteAddress;
ba::ip::address remoteAddress;
unsigned short remotePort = 0;
Response response;
try {
boost::system::error_code error;
std::vector<char> data;
data.resize(SOCKET_BUFFERLENGTH);
@ -63,79 +72,59 @@ static void session(tcp::socket &&sock)
+ std::to_string(remotePort) + ")",
LogLevel::notice);
size_t length = sock.read_some(boost::asio::buffer(data),
error);
if (error == boost::asio::error::eof)
return;
else if (error)
throw boost::system::system_error(error);
size_t length = sock.read_some(ba::buffer(data));
const std::string request(data.begin(), data.begin()+length);
// Get Request
const std::string requestString(data.begin(), data.begin()+length);
l(" Parsing request...", LogLevel::info);
Request request = Request::fromString(requestString);
Json::Reader reader;
Json::Value root;
Response response;
std::string command;
switch (request.command) {
case Request::Command::Lock:
case Request::Command::Unlock:
response = logic->request(request);
break;
if (!reader.parse(request, root, false))
{
response.message = "Request is no valid JSON";
response.code = Response::Code::JsonError;
l(response.message, LogLevel::warning);
goto out;
}
try {
command = getJsonOrFail<std::string>(root, "command");
}
catch (...)
{
response.code = Response::Code::JsonError;
response.message = "Error parsing JSON";
l(response.message, LogLevel::warning);
goto out;
}
l(" Command: " + command, LogLevel::notice);
if (command == "lock" || command == "unlock") {
response = logic->parseRequest(root);
} else if (command == "subscribe") {
if (remoteAddress.is_loopback() == false) {
response.code = Response::Code::AccessDenied;
response.message = "Subscriptions are only allowed from localhost";
l(response.message, LogLevel::warning);
goto out;
}
sock.write_some(boost::asio::buffer(logic->getClientMessage().toJson()));
while (run) {
std::unique_lock<std::mutex> lock(mutex);
onClientMessage.wait(lock);
if (run) {
sock.write_some(boost::asio::buffer(logic->getClientMessage().toJson()));
case Request::Command::Subscribe:
if (remoteAddress.is_loopback() == false) {
response.code = Response::Code::AccessDenied;
response.message = "Subscriptions are only allowed from localhost";
} else {
response = subscribe(sock);
}
};
} else {
response.code = Response::Code::UnknownCommand;
response.message = "Received unknown command " + command;
l(response.message, LogLevel::warning);
break;
case Request::Command::Unknown:
default:
response.code = Response::Code::UnknownCommand;
response.message = "Received unknown command ";
break;
}
out:
sock.write_some(boost::asio::buffer(response.toJson()),
error);
if (error == boost::asio::error::eof)
return;
else if (error)
throw boost::system::system_error(error);
throw response;
}
catch (const std::exception &e) {
std::string message = "Exception in session " + remoteAddress.to_string()
+ ":" + std::to_string(remotePort) + " : " + e.what();
l(message, LogLevel::error);
catch (const Response &err) {
response = err;
}
catch (const std::exception &err) {
response.code = Response::Code::Fail;
response.message = "Exception in session " + remoteAddress.to_string()
+ ":" + std::to_string(remotePort) + " : " + err.what();
}
catch (...) {
response.code = Response::Code::Fail;
response.message = "Unhandled doorlockd error";
}
if (!response) {
l(response.message, LogLevel::warning);
}
if (sock.is_open()) {
boost::system::error_code ec;
sock.write_some(ba::buffer(response.toJson()), ec);
}
l("Closing TCP connection from " + remoteAddress.to_string(), LogLevel::notice);
}
@ -143,7 +132,7 @@ static void server(unsigned short port)
{
l(LogLevel::info, "Starting TCP Server");
const auto endpoint = tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port);
const auto endpoint = tcp::endpoint(ba::ip::address::from_string("127.0.0.1"), port);
tcp::acceptor a(io_service, endpoint);
tcp::socket sock(io_service);

View File

@ -1,7 +1,6 @@
#include <json/json.h>
#include "clientmessage.h"
#include "util.h"
#include "response.h"
const std::string Clientmessage::_tokenKey = "token";
const std::string Clientmessage::_unlockButtonKey = "unlockButton";
@ -40,21 +39,33 @@ const Doormessage& Clientmessage::doormessage() const
return _doormessage;
}
Clientmessage Clientmessage::fromJson(const std::string &json)
Clientmessage Clientmessage::fromJson(const Json::Value &root)
{
Json::Reader reader;
Json::Value root;
std::string token;
Doormessage doormessage;
if (reader.parse(json, root) == false)
throw std::runtime_error("Error parsing JSON");
token = getJsonOrFail<std::string>(root, _tokenKey);
doormessage.isLockButton = getJsonOrFail<bool>(root, _lockButtonKey);
doormessage.isUnlockButton = getJsonOrFail<bool>(root, _unlockButtonKey);
doormessage.isEmergencyUnlock = getJsonOrFail<bool>(root, _emergencyUnlockKey);
doormessage.isOpen = getJsonOrFail<bool>(root, _isOpenKey);
try {
token = getJsonOrFail<std::string>(root, _tokenKey);
doormessage.isLockButton = getJsonOrFail<bool>(root, _lockButtonKey);
doormessage.isUnlockButton = getJsonOrFail<bool>(root, _unlockButtonKey);
doormessage.isEmergencyUnlock = getJsonOrFail<bool>(root, _emergencyUnlockKey);
doormessage.isOpen = getJsonOrFail<bool>(root, _isOpenKey);
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return Clientmessage(token, doormessage);
}
Clientmessage Clientmessage::fromString(const std::string &string)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(string, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}

View File

@ -2,6 +2,7 @@
#define CLIENTMESSAGE_H
#include <string>
#include <json/json.h>
#include "doormessage.h"
@ -13,7 +14,8 @@ public:
Clientmessage(std::string token,
Doormessage doormessage);
static Clientmessage fromJson(const std::string &json);
static Clientmessage fromJson(const Json::Value &root);
static Clientmessage fromString(const std::string &json);
std::string toJson() const;
const std::string& token() const;

View File

@ -133,7 +133,7 @@ void Door::lock()
_heartbeatCondition.notify_one();
_heartbeatThread.join();
_logger(LogLevel::info, "Door closed");
_logger(LogLevel::notice , "Door closed");
out:
_logger(LogLevel::notice, "Executing Post Lock Script");
@ -175,7 +175,7 @@ void Door::unlock()
writeCMD(DOOR_CMD_LOCK);
});
_logger(LogLevel::info, "Door opened");
_logger(LogLevel::notice, "Door opened");
out:
_logger(LogLevel::notice, "Executing Post Unlock Script");

View File

@ -1,11 +1,12 @@
#ifndef DOOR_H
#define DOOR_H
#include <string>
#include <functional>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>

View File

@ -1,26 +1,17 @@
#include <chrono>
#include <functional>
#include <cstdlib>
#include <json/json.h>
#include <errno.h>
#define LDAP_DEPRECATED 1
#include <ldap.h>
#include <errno.h>
#include "util.h"
#include "logic.h"
#include "util.h"
using namespace std;
Logic::Logic(const chrono::seconds tokenTimeout,
const string &ldapUri,
const string &bindDN,
const string &webPrefix,
const string &serDev,
Logic::Logic(const std::chrono::seconds tokenTimeout,
const std::string &ldapUri,
const std::string &bindDN,
const std::string &webPrefix,
const std::string &serDev,
const unsigned int baudrate,
condition_variable &onClientUpdate) :
std::condition_variable &onClientUpdate) :
_logger(Logger::get()),
_door(serDev, baudrate),
_tokenTimeout(tokenTimeout),
@ -36,10 +27,10 @@ Logic::Logic(const chrono::seconds tokenTimeout,
this,
std::placeholders::_1));
_tokenUpdater = thread([this] () {
_tokenUpdater = std::thread([this] () {
while (_run)
{
unique_lock<mutex> l(_mutex);
std::unique_lock<std::mutex> l(_mutex);
_tokenCondition.wait_for(l, _tokenTimeout);
if (_run == false)
{
@ -58,58 +49,60 @@ Logic::~Logic()
_tokenUpdater.join();
}
Response Logic::parseRequest(const Json::Value &root)
Response Logic::processDoor(const DoorCommand &doorCommand)
{
unique_lock<mutex> l(_mutex);
_logger(LogLevel::info, "Incoming request...");
Response response;
string command, user, password, ip, token;
try {
command = getJsonOrFail<string>(root, "command");
ip = getJsonOrFail<string>(root, "ip");
user = getJsonOrFail<string>(root, "user");
password = getJsonOrFail<string>(root, "password");
token = getJsonOrFail<string>(root, "token");
switch (doorCommand) {
case DoorCommand::Lock:
response = _lock();
break;
case DoorCommand::Unlock:
response = _unlock();
break;
default:
response.code = Response::Code::UnknownCommand;
response.message = "Unknown DoorCommand";
break;
}
catch (...)
{
_logger(LogLevel::warning, "Error parsing JSON");
response.code = Response::Code::JsonError;
response.message = "Error parsing JSON";
return response;
}
Response Logic::request(const Request &request)
{
std::unique_lock<std::mutex> l(_mutex);
Response response;
DoorCommand doorCommand;
switch (request.command) {
case Request::Command::Lock:
doorCommand = DoorCommand::Lock;
break;
case Request::Command::Unlock:
doorCommand = DoorCommand::Unlock;
break;
default:
response.code = Response::Code::UnknownCommand;
response.message = "Unknown Command";
goto out;
}
response = _checkToken(request.token);
if (!response) {
goto out;
}
_logger(LogLevel::info, " -> Token check successful");
_logger(" User : " + user, LogLevel::notice);
_logger(" IP : " + ip, LogLevel::notice);
_logger(" Token : " + token, LogLevel::notice);
if (_checkToken(token) == false)
{
_logger(LogLevel::error, "User provided invalid token");
response.code = Response::Code::InvalidToken;
response.message = "User provided invalid token";
response = _checkLDAP(request.user, request.password);
if (!response) {
goto out;
}
_logger(LogLevel::info, " -> LDAP check successful");
response = _checkLDAP(user,password);
if (!response)
{
_logger(LogLevel::error, "Ldap error");
goto out;
}
if (command == "lock")
{
response = _lock();
} else if (command == "unlock") {
response = _unlock();
} else {
response.code = Response::Code::UnknownCommand;
response.message = "Unknown Command: " + command;
_logger(response.message, LogLevel::error);
}
response = processDoor(doorCommand);
_logger(LogLevel::info, " -> Door Command successful");
out:
return response;
@ -122,14 +115,15 @@ Response Logic::_lock()
{
response.code = Response::Code::AlreadyLocked;
response.message = "Unable to lock: already closed";
_logger(response.message, LogLevel::warning);
} else {
_door.lock();
_createNewToken(false);
response.code = Response::Code::Success;
response.message = "Success";
}
_door.lock();
return response;
}
@ -145,32 +139,38 @@ Response Logic::_unlock()
{
response.code = Response::Code::AlreadyUnlocked;
response.message = "Unable to unlock: already unlocked";
_logger(response.message, LogLevel::warning);
_logger(response.message, LogLevel::info);
} else {
response.code = Response::Code::Success;
response.message = "Success";
}
return response;
}
bool Logic::_checkToken(const string &strToken)
Response Logic::_checkToken(std::string token) const
{
try {
uint64_t token = toUint64(strToken);
if (token == _curToken || (_prevValid == true && token == _prevToken))
{
_logger(LogLevel::info, "Token check successful");
return true;
}
}
catch (const char* const &ex)
{
_logger(LogLevel::error, "Check Token failed for token \"%s\" (expected %s): %s", strToken.c_str(), toHexString(_curToken).c_str(), ex);
}
return false;
std::transform(token.begin(),
token.end(),
token.begin(),
::toupper);
if (token == _curToken)
return Response(Response::Code::Success);
if (_prevValid == true && token == _prevToken)
return Response(Response::Code::Success);
_logger("Check Token failed: got \"" + token
+ "\", expected \"" + _curToken +"\"",
LogLevel::error);
return Response(Response::InvalidToken,
"User provided invalid token");
}
Response Logic::_checkLDAP(const string &user, const string &password)
Response Logic::_checkLDAP(const std::string &user,
const std::string &password)
{
constexpr int BUFFERSIZE = 1024;
char buffer[BUFFERSIZE];
@ -180,16 +180,15 @@ Response Logic::_checkLDAP(const string &user, const string &password)
LDAP* ld = nullptr;
unsigned long version = LDAP_VERSION3;
_logger(LogLevel::notice, "Trying to authenticate as user \"%s\"", user.c_str());
_logger(LogLevel::info, " Trying to authenticate as user \"%s\"", user.c_str());
snprintf(buffer, BUFFERSIZE, _bindDN.c_str(), user.c_str());
rc = ldap_initialize(&ld, _ldapUri.c_str());
if(rc != LDAP_SUCCESS)
{
retval.message = (string)"LDAP initialize error: "
+ ldap_err2string(rc);
retval.message = (std::string)"LDAP initialize error: "
+ ldap_err2string(rc);
retval.code = Response::Code::LDAPInit;
_logger(retval.message, LogLevel::error);
goto out2;
}
@ -200,7 +199,6 @@ Response Logic::_checkLDAP(const string &user, const string &password)
{
retval.code = Response::Code::LDAPInit;
retval.message = "LDAP set version failed";
_logger(retval.message, LogLevel::error);
goto out;
}
@ -210,12 +208,11 @@ Response Logic::_checkLDAP(const string &user, const string &password)
retval = Response::Code::InvalidCredentials;
retval.message = "Credential check for user \"" + user
+ "\" failed: " + ldap_err2string(rc);
_logger(retval.message, LogLevel::error);
goto out;
}
_logger(LogLevel::notice, "user \"%s\" successfully authenticated", user.c_str());
retval = Response::Code::Success;
retval.code = Response::Code::Success;
retval.message = "";
out:
ldap_unbind(ld);
@ -231,11 +228,13 @@ void Logic::_createNewToken(const bool stillValid)
_prevToken = _curToken;
_prevValid = stillValid;
_curToken = (((uint64_t)rand())<<32) | ((uint64_t)rand());
_curToken = toHexString((((uint64_t)rand())<<32) | ((uint64_t)rand()));
ostringstream message;
message << "New Token generated: " << toHexString(_curToken) << " old Token: " << toHexString(_prevToken) << " is " << (_prevValid?"still":"not") << " valid";
_logger(message, LogLevel::info);
std::ostringstream message;
message << "New token: " << _curToken
<< " old token: " << _prevToken << " is "
<< (_prevValid?"still":"not") << " valid";
_logger(message, LogLevel::notice);
_onClientUpdate.notify_all();
}
@ -243,7 +242,7 @@ void Logic::_createNewToken(const bool stillValid)
Clientmessage Logic::getClientMessage()
{
std::lock_guard<std::mutex> l(_mutex);
Clientmessage retval(_webPrefix + toHexString(_curToken),
Clientmessage retval(_webPrefix + _curToken,
_doormessage);
// Reset doormessage

View File

@ -1,23 +1,23 @@
#ifndef LOGIC_H
#define LOGIC_H
#include <cstdint>
#include <string>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
#include "config.h"
#include "clientmessage.h"
#include "door.h"
#include "logger.h"
#include "request.h"
#include "response.h"
#include "clientmessage.h"
/* The "Logic" class
*
* This class is initilized by all settings.
*
* It parses incoming JSON-Requests and returns the Response Code.
* It handles incoming requests and allows modifications of the door
*/
class Logic
{
@ -33,7 +33,11 @@ public:
~Logic();
// Parse incoming JSON Requests
Response parseRequest(const Json::Value &root);
Response request(const Request &request);
// Send direct command to door without credential checks
enum class DoorCommand { Lock, Unlock };
Response processDoor(const DoorCommand &doorCommand);
// Returns the current Token
Clientmessage getClientMessage();
@ -46,7 +50,7 @@ private:
Response _unlock();
// Checks if the incoming token is valid
bool _checkToken(const std::string &token);
Response _checkToken(std::string token) const;
// Checks if incoming credentials against LDAP
Response _checkLDAP(const std::string &user,
@ -63,13 +67,10 @@ private:
// The door
Door _door;
// Tokens are 64-bit hexadecimal values
using Token = uint64_t;
// The current token
Token _curToken = { 0x0000000000000000 };
std::string _curToken = { "0000000000000000" };
// The previous token
Token _prevToken = { 0x0000000000000000 };
std::string _prevToken = { "0000000000000000" };
// Indicates whether the previous token is valid
bool _prevValid = { false };

69
doorlockd/lib/request.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "request.h"
#include "util.h"
#include "logger.h"
const std::string Request::_commandKey = "command";
Request::Command Request::_commandFromString(const std::string &command)
{
Command retval = Command::Unknown;
if (command == "lock")
retval = Command::Lock;
else if (command == "unlock")
retval = Command::Unlock;
else if (command == "subscribe")
retval = Command::Subscribe;
else
retval = Command::Unknown;
return retval;
}
Request Request::fromJson(const Json::Value &root)
{
Request retval;
const auto &l = Logger::get();
try {
const auto commandStr =
getJsonOrFail<std::string>(root, _commandKey);
l(" command: " + commandStr, LogLevel::info);
retval.command = _commandFromString(commandStr);
// Stop parsing, if command is unknown
if (retval.command == Command::Unknown)
return retval;
if (retval.command == Command::Lock ||
retval.command == Command::Unlock) {
retval.user = getJsonOrFail<std::string>(root, "user");
l(" user: " + retval.user, LogLevel::info);
retval.password = getJsonOrFail<std::string>(root, "password");
l(" password: XXX", LogLevel::info);
retval.token = getJsonOrFail<std::string>(root, "token");
l(" token: " + retval.token, LogLevel::info);
}
if (retval.command == Command::Subscribe) {
// Nothing to do in this case
}
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return retval;
}
Request Request::fromString(const std::string &string)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(string, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}

29
doorlockd/lib/request.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef REQUEST_H
#define REQUEST_H
#include <string>
#include <json/json.h>
#include "response.h"
class Request
{
public:
static Request fromJson(const Json::Value &root);
static Request fromString(const std::string &string);
enum class Command { Lock, Unlock, Subscribe, Unknown }
command = { Command::Unknown };
std::string user = { };
std::string password = { };
std::string token = { };
private:
static Command _commandFromString(const std::string &command);
const static std::string _commandKey;
};
#endif // REQUEST_H

View File

@ -8,6 +8,18 @@
const std::string Response::_codeKey = "code";
const std::string Response::_messageKey = "message";
Response::Response():
code(Fail),
message("General failure")
{
}
Response::Response(Response::Code code, const std::string &what) :
code(code),
message(what)
{
}
Response::operator bool() const
{
return code == Response::Code::Success;
@ -24,21 +36,34 @@ std::string Response::toJson() const
return writer.write(response);
}
Response Response::fromJson(const std::string &json)
Response Response::fromJson(const Json::Value &root)
{
Json::Reader reader;
Json::Value root;
Response retval;
if (reader.parse(json, root) == false)
throw std::runtime_error("Error parsing JSON");
try {
retval.message = getJsonOrFail<std::string>(root, _messageKey);
retval.message = getJsonOrFail<std::string>(root, _messageKey);
const auto code = getJsonOrFail<int>(root, _codeKey);
if (code > Code::RESPONSE_NUM_ITEMS)
throw std::runtime_error("Error code out of range");
const auto code = getJsonOrFail<int>(root, _codeKey);
if (code > Code::RESPONSE_NUM_ITEMS)
throw std::runtime_error("Error code out of range");
retval.code = static_cast<Code>(code);
retval.code = static_cast<Code>(code);
}
catch (const std::exception &ex) {
throw Response(Response::Code::JsonError, ex.what());
}
return retval;
}
Response Response::fromString(const std::string &json)
{
Json::Reader reader;
Json::Value root;
if (reader.parse(json, root) == false)
throw Response(Response::Code::NotJson,
"No valid JSON");
return fromJson(root);
}

View File

@ -3,8 +3,11 @@
#include <string>
struct Response
#include <json/json.h>
class Response
{
public:
enum Code {
Success = 0, // Request successful
Fail, // General non-specified error
@ -22,19 +25,11 @@ struct Response
} code;
std::string message;
Response() :
code(Fail),
message("General failure")
{
}
Response();
Response(Response::Code code, const std::string &message = "");
Response(Response::Code code, const std::string &message = "") :
code(code),
message(message)
{
}
static Response fromJson(const std::string &json);
static Response fromJson(const Json::Value &root);
static Response fromString(const std::string &json);
std::string toJson() const;
// Returns true if code is success

View File

@ -1,9 +1,7 @@
#include "util.h"
using namespace std;
template <>
int getJson(const Json::Value &root, const string &key)
int getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isInt())
@ -12,7 +10,7 @@ int getJson(const Json::Value &root, const string &key)
}
template <>
string getJson(const Json::Value &root, const string &key)
std::string getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isString())
@ -21,7 +19,7 @@ string getJson(const Json::Value &root, const string &key)
}
template <>
size_t getJson(const Json::Value &root, const string &key)
size_t getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isInt())
@ -30,7 +28,7 @@ size_t getJson(const Json::Value &root, const string &key)
}
template <>
bool getJson(const Json::Value &root, const string &key)
bool getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
if (val.isBool())
@ -39,7 +37,7 @@ bool getJson(const Json::Value &root, const string &key)
}
template <>
Json::Value getJson(const Json::Value &root, const string &key)
Json::Value getJson(const Json::Value &root, const std::string &key)
{
auto val = root.get(key, Json::Value());
return val;
@ -57,9 +55,9 @@ static char nibble2hex(unsigned char input)
return input - 0xA + 'A';
}
string toHexString(const uint64_t c)
std::string toHexString(const uint64_t c)
{
string retval;
std::string retval;
retval = nibble2hex((c>>60) & 0xF);
retval += nibble2hex((c>>56) & 0xF);
@ -92,21 +90,3 @@ unsigned char hex2uchar(const char input)
}
throw std::runtime_error("Malformed hexadecimal input");
}
uint64_t toUint64(const string &s)
{
if (s.length() != (64/4))
{
throw std::runtime_error("Hex string has invalid length");
}
uint64_t retval = 0;
for (int i = 0 ; i < (64/4) ; i++)
{
retval <<= 4;
retval |= hex2uchar(s.at(i))&0xf;
}
return retval;
}

View File

@ -14,13 +14,12 @@ T getJsonOrFail(const Json::Value &root, const std::string &key)
const auto members = root.getMemberNames();
if (std::find(members.begin(), members.end(), key) == members.end())
{
throw std::runtime_error("Json key not existing");
throw std::runtime_error("Json key \"" + key + "\" not existing");
}
return getJson<T>(root, key);
}
std::string toHexString(uint64_t c);
uint64_t toUint64(const std::string &s);
#endif