From 49a5b88f6c768c6ca9bafcc368ed215581dcb503 Mon Sep 17 00:00:00 2001 From: Ralf Ramsauer Date: Thu, 1 Oct 2015 22:09:55 +0200 Subject: [PATCH] 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 --- doorlockd/CMakeLists.txt | 2 + doorlockd/client/doorlock-client.cpp | 16 ++- doorlockd/daemon/daemon.cpp | 16 +-- doorlockd/daemon/doorlockd.cpp | 141 ++++++++++----------- doorlockd/lib/clientmessage.cpp | 37 ++++-- doorlockd/lib/clientmessage.h | 4 +- doorlockd/lib/door.cpp | 4 +- doorlockd/lib/door.h | 9 +- doorlockd/lib/logic.cpp | 183 +++++++++++++-------------- doorlockd/lib/logic.h | 25 ++-- doorlockd/lib/request.cpp | 69 ++++++++++ doorlockd/lib/request.h | 29 +++++ doorlockd/lib/response.cpp | 45 +++++-- doorlockd/lib/response.h | 21 ++- doorlockd/lib/util.cpp | 34 +---- doorlockd/lib/util.h | 3 +- 16 files changed, 370 insertions(+), 268 deletions(-) create mode 100644 doorlockd/lib/request.cpp create mode 100644 doorlockd/lib/request.h diff --git a/doorlockd/CMakeLists.txt b/doorlockd/CMakeLists.txt index 168d2a6..710f555 100644 --- a/doorlockd/CMakeLists.txt +++ b/doorlockd/CMakeLists.txt @@ -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 diff --git a/doorlockd/client/doorlock-client.cpp b/doorlockd/client/doorlock-client.cpp index 3ae6b2c..cb2c599 100644 --- a/doorlockd/client/doorlock-client.cpp +++ b/doorlockd/client/doorlock-client.cpp @@ -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 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; } diff --git a/doorlockd/daemon/daemon.cpp b/doorlockd/daemon/daemon.cpp index 2cea094..8f43ca9 100644 --- a/doorlockd/daemon/daemon.cpp +++ b/doorlockd/daemon/daemon.cpp @@ -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"); } } diff --git a/doorlockd/daemon/doorlockd.cpp b/doorlockd/daemon/doorlockd.cpp index f59d68a..c8abadb 100644 --- a/doorlockd/daemon/doorlockd.cpp +++ b/doorlockd/daemon/doorlockd.cpp @@ -1,9 +1,5 @@ -#include -#include -#include -#include -#include #include +#include #include #include @@ -11,13 +7,13 @@ #include #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 = 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 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 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(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 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); diff --git a/doorlockd/lib/clientmessage.cpp b/doorlockd/lib/clientmessage.cpp index 7266b6b..ccf1d4a 100644 --- a/doorlockd/lib/clientmessage.cpp +++ b/doorlockd/lib/clientmessage.cpp @@ -1,7 +1,6 @@ -#include - #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(root, _tokenKey); - doormessage.isLockButton = getJsonOrFail(root, _lockButtonKey); - doormessage.isUnlockButton = getJsonOrFail(root, _unlockButtonKey); - doormessage.isEmergencyUnlock = getJsonOrFail(root, _emergencyUnlockKey); - doormessage.isOpen = getJsonOrFail(root, _isOpenKey); + try { + token = getJsonOrFail(root, _tokenKey); + doormessage.isLockButton = getJsonOrFail(root, _lockButtonKey); + doormessage.isUnlockButton = getJsonOrFail(root, _unlockButtonKey); + doormessage.isEmergencyUnlock = getJsonOrFail(root, _emergencyUnlockKey); + doormessage.isOpen = getJsonOrFail(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); +} diff --git a/doorlockd/lib/clientmessage.h b/doorlockd/lib/clientmessage.h index 5531442..8cccb32 100644 --- a/doorlockd/lib/clientmessage.h +++ b/doorlockd/lib/clientmessage.h @@ -2,6 +2,7 @@ #define CLIENTMESSAGE_H #include +#include #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; diff --git a/doorlockd/lib/door.cpp b/doorlockd/lib/door.cpp index 43cd15e..804e66c 100644 --- a/doorlockd/lib/door.cpp +++ b/doorlockd/lib/door.cpp @@ -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"); diff --git a/doorlockd/lib/door.h b/doorlockd/lib/door.h index d307389..45a3e85 100644 --- a/doorlockd/lib/door.h +++ b/doorlockd/lib/door.h @@ -1,11 +1,12 @@ #ifndef DOOR_H #define DOOR_H -#include -#include -#include -#include +#include #include +#include +#include +#include +#include #include #include diff --git a/doorlockd/lib/logic.cpp b/doorlockd/lib/logic.cpp index ea569dd..56feb1b 100644 --- a/doorlockd/lib/logic.cpp +++ b/doorlockd/lib/logic.cpp @@ -1,26 +1,17 @@ -#include -#include - -#include -#include - +#include #define LDAP_DEPRECATED 1 #include -#include - -#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 l(_mutex); + std::unique_lock 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 l(_mutex); - - _logger(LogLevel::info, "Incoming request..."); Response response; - string command, user, password, ip, token; - try { - command = getJsonOrFail(root, "command"); - ip = getJsonOrFail(root, "ip"); - user = getJsonOrFail(root, "user"); - password = getJsonOrFail(root, "password"); - token = getJsonOrFail(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 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 l(_mutex); - Clientmessage retval(_webPrefix + toHexString(_curToken), + Clientmessage retval(_webPrefix + _curToken, _doormessage); // Reset doormessage diff --git a/doorlockd/lib/logic.h b/doorlockd/lib/logic.h index 821c4d4..d8f369b 100644 --- a/doorlockd/lib/logic.h +++ b/doorlockd/lib/logic.h @@ -1,23 +1,23 @@ #ifndef LOGIC_H #define LOGIC_H -#include -#include -#include #include #include +#include +#include #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 }; diff --git a/doorlockd/lib/request.cpp b/doorlockd/lib/request.cpp new file mode 100644 index 0000000..bc670b9 --- /dev/null +++ b/doorlockd/lib/request.cpp @@ -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(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(root, "user"); + l(" user: " + retval.user, LogLevel::info); + retval.password = getJsonOrFail(root, "password"); + l(" password: XXX", LogLevel::info); + retval.token = getJsonOrFail(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); +} diff --git a/doorlockd/lib/request.h b/doorlockd/lib/request.h new file mode 100644 index 0000000..1120ac6 --- /dev/null +++ b/doorlockd/lib/request.h @@ -0,0 +1,29 @@ +#ifndef REQUEST_H +#define REQUEST_H + +#include + +#include + +#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 + diff --git a/doorlockd/lib/response.cpp b/doorlockd/lib/response.cpp index e747a93..6e84830 100644 --- a/doorlockd/lib/response.cpp +++ b/doorlockd/lib/response.cpp @@ -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(root, _messageKey); - retval.message = getJsonOrFail(root, _messageKey); + const auto code = getJsonOrFail(root, _codeKey); + if (code > Code::RESPONSE_NUM_ITEMS) + throw std::runtime_error("Error code out of range"); - const auto code = getJsonOrFail(root, _codeKey); - if (code > Code::RESPONSE_NUM_ITEMS) - throw std::runtime_error("Error code out of range"); - retval.code = static_cast(code); + retval.code = static_cast(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); +} diff --git a/doorlockd/lib/response.h b/doorlockd/lib/response.h index 46d1505..036c4cf 100644 --- a/doorlockd/lib/response.h +++ b/doorlockd/lib/response.h @@ -3,8 +3,11 @@ #include -struct Response +#include + +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 diff --git a/doorlockd/lib/util.cpp b/doorlockd/lib/util.cpp index 7e8bab9..d9b25ce 100644 --- a/doorlockd/lib/util.cpp +++ b/doorlockd/lib/util.cpp @@ -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; -} diff --git a/doorlockd/lib/util.h b/doorlockd/lib/util.h index 9779ff5..3750718 100644 --- a/doorlockd/lib/util.h +++ b/doorlockd/lib/util.h @@ -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(root, key); } std::string toHexString(uint64_t c); -uint64_t toUint64(const std::string &s); #endif