Mercurial > malikania
changeset 83:d458af0b7748
Server: namespace and hierarchy
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 22 Jan 2017 17:54:56 +0100 |
parents | ee850a6ab89e |
children | bb859ba34ce4 |
files | database/sqlite/src/account.cpp libserver/CMakeLists.txt libserver/malikania/account.hpp libserver/malikania/account_dao.cpp libserver/malikania/account_dao.hpp libserver/malikania/client.cpp libserver/malikania/client.hpp libserver/malikania/database.cpp libserver/malikania/database.hpp libserver/malikania/hash.hpp libserver/malikania/server.cpp libserver/malikania/server.hpp libserver/malikania/server/account.hpp libserver/malikania/server/account_dao.cpp libserver/malikania/server/account_dao.hpp libserver/malikania/server/client.cpp libserver/malikania/server/client.hpp libserver/malikania/server/database.cpp libserver/malikania/server/database.hpp libserver/malikania/server/hash.hpp libserver/malikania/server/server.cpp libserver/malikania/server/server.hpp server/main.cpp |
diffstat | 23 files changed, 1208 insertions(+), 1171 deletions(-) [+] |
line wrap: on
line diff
--- a/database/sqlite/src/account.cpp Sun Jan 22 11:07:36 2017 +0100 +++ b/database/sqlite/src/account.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -21,11 +21,12 @@ #include <vector> -#include <malikania/account.hpp> +#include <malikania/server/account.hpp> #include "driver.hpp" using namespace mlk; +using namespace mlk::server; namespace {
--- a/libserver/CMakeLists.txt Sun Jan 22 11:07:36 2017 +0100 +++ b/libserver/CMakeLists.txt Sun Jan 22 17:54:56 2017 +0100 @@ -20,19 +20,19 @@ set( HEADERS - ${libmlk-server_SOURCE_DIR}/malikania/account.hpp - ${libmlk-server_SOURCE_DIR}/malikania/account_dao.hpp - ${libmlk-server_SOURCE_DIR}/malikania/client.hpp - ${libmlk-server_SOURCE_DIR}/malikania/database.hpp - ${libmlk-server_SOURCE_DIR}/malikania/server.hpp + ${libmlk-server_SOURCE_DIR}/malikania/server/account.hpp + ${libmlk-server_SOURCE_DIR}/malikania/server/account_dao.hpp + ${libmlk-server_SOURCE_DIR}/malikania/server/client.hpp + ${libmlk-server_SOURCE_DIR}/malikania/server/database.hpp + ${libmlk-server_SOURCE_DIR}/malikania/server/server.hpp ) set( SOURCES - ${libmlk-server_SOURCE_DIR}/malikania/account_dao.cpp - ${libmlk-server_SOURCE_DIR}/malikania/client.cpp - ${libmlk-server_SOURCE_DIR}/malikania/database.cpp - ${libmlk-server_SOURCE_DIR}/malikania/server.cpp + ${libmlk-server_SOURCE_DIR}/malikania/server/account_dao.cpp + ${libmlk-server_SOURCE_DIR}/malikania/server/client.cpp + ${libmlk-server_SOURCE_DIR}/malikania/server/database.cpp + ${libmlk-server_SOURCE_DIR}/malikania/server/server.cpp ) malikania_define_library(
--- a/libserver/malikania/account.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * account.hpp -- account model - * - * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_ACCOUNT_HPP -#define MALIKANIA_ACCOUNT_HPP - -/** - * \file account.hpp - * \brief Account model. - */ - -#include <cstdint> -#include <string> - -namespace mlk { - -/** - * \brief Account model - */ -class account { -public: - std::uint64_t id; - std::string name; - std::string email; - std::string first_name; - std::string last_name; - std::string password; -}; - -inline bool operator==(const account &ac1, const account &ac2) noexcept -{ - return ac1.id == ac2.id; -} - -inline bool operator!=(const account &ac1, const account &ac2) noexcept -{ - return !(ac1 == ac2); -} - -} // !mlk - -#endif // !MALIKANIA_ACCOUNT_HPP
--- a/libserver/malikania/account_dao.cpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * dao-account.cpp -- database account management - * - * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "account.hpp" -#include "account_dao.hpp" -#include "database.hpp" -#include "hash.hpp" - -namespace mlk { - -using create_func = void (account&); -using remove_func = void (const account&); -using update_func = void (account&); -using get_func = boost::optional<account> (std::uint64_t); -using find_by_name_func = boost::optional<account> (const std::string&); -using list_func = std::vector<account> (); -using count_func = std::uint64_t (); -using clear_func = void (); - -bool account_dao::authenticate(const std::string& login, const std::string& pass) -{ - auto ac = find_by_name(login); - auto sha512 = hash::sha512(pass); - - return ac && ac->password == sha512; -} - -void account_dao::create(account& account) -{ - m_database.handle().get<create_func>("malikania_account_create")(account); -} - -void account_dao::remove(const account& account) -{ - m_database.handle().get<remove_func>("malikania_account_remove")(account); -} - -void account_dao::update(account& account) -{ - m_database.handle().get<update_func>("malikania_account_update")(account); -} - -boost::optional<account> account_dao::get(uint64_t id) -{ - return m_database.handle().get<get_func>("malikania_account_get")(id); -} - -boost::optional<account> account_dao::find_by_name(const std::string& name) -{ - return m_database.handle().get<find_by_name_func>("malikania_account_find_by_name")(name); -} - -std::vector<account> account_dao::list() -{ - return m_database.handle().get<list_func>("malikania_account_list")(); -} - -std::uint64_t account_dao::count() -{ - return m_database.handle().get<count_func>("malikania_account_count")(); -} - -void account_dao::clear() -{ - m_database.handle().get<clear_func>("malikania_account_clear")(); -} - -} // !mlk
--- a/libserver/malikania/account_dao.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/* - * account_dao.hpp -- database account management - * - * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_SERVER_ACCOUNT_DAO_HPP -#define MALIKANIA_SERVER_ACCOUNT_DAO_HPP - -/** - * \file account_dao.hpp - * \brief Database account management. - */ - -#include <cstdint> -#include <vector> - -#include <boost/optional.hpp> - -namespace mlk { - -class database; -class account; - -/** - * \brief Account DAO. - */ -class account_dao { -private: - database& m_database; - -public: - /** - * Constructor. - * - * \param database the database - */ - inline account_dao(database& database) noexcept - : m_database(database) - { - } - - /** - * Check if the user authentication is valid. - * - * \param login the login name - * \param pass the clear password - * \return true if authentication was successful - */ - bool authenticate(const std::string& login, const std::string& pass); - - /** - * Create the given account. - * - * The object will be modified in place. - * - * \param account the account to add - * \throw std::exception on errors - */ - void create(account& account); - - /** - * Remove the given account, all data that references this account is - * deleted too. - * - * \param account the account - * \throw std::exception on errors - */ - void remove(const account& account); - - /** - * Update account only, does not recurse into objects that references the - * account. - * - * \param account the account - * \throw std::exception on errors - */ - void update(account& account); - - /** - * Get an account. - * - * \param id the account id - * \return the account - * \throw std::exception if not found - */ - boost::optional<account> get(std::uint64_t id); - - /** - * Find an account by name. - * - * \param name the account name - * \return the account or empty one if not found - * \throw std::exception on other errors - */ - boost::optional<account> find_by_name(const std::string& name); - - /** - * Get the list of account. - * - * \throw std::exception on errors - */ - std::vector<account> list(); - - /** - * Get the number of accounts. - * - * \return the number of account. - */ - std::uint64_t count(); - - /** - * Remove all accounts recursively. - * - * \throw std::exception on errors - * \warning use with care - */ - void clear(); -}; - -} // !mlk - -#endif // !MALIKANIA_SERVER_ACCOUNT_DAO_HPP
--- a/libserver/malikania/client.cpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ -#include <cassert> -#include <iostream> - -#include "client.hpp" -#include "server.hpp" - -namespace mlk { - -/* - * client_proxy_writer - * ------------------------------------------------------------------ - */ - -client_proxy_writer::client_proxy_writer(std::shared_ptr<client> client, std::string init) - : m_client(std::move(client)) -{ - assert(m_client); - - m_output << init; -} - -client_proxy_writer::~client_proxy_writer() -{ - m_client->send(m_output.str()); -} - -/* - * client_proxy_writer - * ------------------------------------------------------------------ - */ - -void client::handle_read(boost::system::error_code code, std::size_t xfer) -{ - // TODO: use fixed size stream + verify exceed. - if (code) { - m_server.handle_disconnect(shared_from_this()); - } else { - std::istringstream iss(std::string( - boost::asio::buffers_begin(m_input.data()), - boost::asio::buffers_begin(m_input.data()) + xfer - 4 - )); - - // Remove early in case of errors. - m_input.consume(xfer); - - // Extract command name. - std::string cmd; - iss >> cmd >> std::ws; - - if (!iss) { - return; - } - - // Rest of the data. - std::string data(std::istreambuf_iterator<char>(iss), {}); - - if (!iss) { - return; - } - - m_server.handle_message(shared_from_this(), std::move(cmd), std::move(data)); - do_read(); - } -} - -void client::handle_write(boost::system::error_code code, std::size_t) -{ - if (code) { - m_server.handle_disconnect(shared_from_this()); - } else { - m_output.pop_front(); - - if (!m_output.empty()) { - do_write(); - } - } -} - -void client::do_read() -{ - auto self = shared_from_this(); - - boost::asio::async_read_until(m_socket, m_input, "\r\n\r\n", [self] (auto code, auto xfer) { - self->handle_read(code, xfer); - }); -} - -void client::do_write() -{ - assert(!m_output.empty()); - - auto self = shared_from_this(); - - boost::asio::async_write(m_socket, boost::asio::buffer(m_output[0]), [self] (auto code, auto xfer) { - self->handle_write(code, xfer); - }); -} - -client::client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context) - : m_server(server) - , m_socket(service, context) -{ -} - -void client::handshake() -{ - auto self = shared_from_this(); - - m_socket.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) { - if (code) { - std::cerr << "handshake failure: " << code << std::endl; - } else { - self->read(); - } - }); -} - -void client::read() -{ - do_read(); -} - -void client::send(std::string cmd, std::string data) -{ - send_raw(cmd + " " + data); -} - -void client::ok(std::string cmd) -{ - send_raw(cmd + " ok"); -} - -void client::error(std::string cmd, std::string reason) -{ - send_raw(cmd + " error " + reason); -} - -client_proxy_writer client::send(std::string cmd) -{ - return client_proxy_writer(shared_from_this(), cmd + " "); -} - -void client::send_raw(std::string data) -{ - assert(!data.empty()); - - auto in_progress = !m_output.empty(); - - data += "\r\n\r\n"; - m_output.push_back(std::move(data)); - - if (!in_progress) { - do_write(); - } -} - -} // !mlk
--- a/libserver/malikania/client.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -#ifndef MALIKANIA_SERVER_CLIENT_HPP -#define MALIKANIA_SERVER_CLIENT_HPP - -#include <cstdint> -#include <deque> -#include <memory> -#include <string> -#include <sstream> - -#include <boost/asio.hpp> -#include <boost/asio/ssl.hpp> - -namespace mlk { - -class client; -class server; - -class client_proxy_writer { - std::shared_ptr<client> m_client; - std::ostringstream m_output; - -public: - client_proxy_writer(std::shared_ptr<client> client, std::string init); - - client_proxy_writer(client_proxy_writer&&) = default; - - ~client_proxy_writer(); - - template <typename Arg> - inline client_proxy_writer& operator<<(Arg&& arg) - { - m_output << std::forward<Arg>(arg); - - return *this; - } -}; - -class client : public std::enable_shared_from_this<client> { -public: - friend class server; - - enum class state : std::uint8_t { - authenticating, - ready - }; - -private: - server& m_server; - state m_state{state::authenticating}; - boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_socket; - boost::asio::streambuf m_input; - std::deque<std::string> m_output; - - void handle_read(boost::system::error_code, std::size_t); - void handle_write(boost::system::error_code, std::size_t); - - void do_read(); - void do_write(); - - void handshake(); - void read(); - -public: - /** - * Create the client in the associated server. - * - * \param server the server object - * \param service the main loop service - * \param context the ssl context - */ - client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context); - - /** - * Send a command and its arguments. - * - * \pre !cmd.empty() - * \param cmd the command - * \param args the arguments - */ - void send(std::string cmd, std::string args); - - /** - * Send successful command result. - * - * \pre !cmd.empty() - * \param cmd the command name - */ - void ok(std::string cmd); - - /** - * Send a error command result. - * - * \pre !cmd.empty() && !reason.empty() - * \param cmd the command name - * \param reason the reason string - */ - void error(std::string cmd, std::string reason); - - /** - * Convenient function for sending data using operator<<. - * - * The returned have operator<< defined for the user and will append all - * data inserted that way when the appropriate client_proxy_write is - * destroyed. - * - * \param cmd the command name - */ - client_proxy_writer send(std::string cmd); - - /** - * Send a raw data. - * - * \pre !data.empty() - * \param data the data - */ - void send_raw(std::string data); -}; - -} // !mlk - -#endif // !MALIKANIA_SERVER_CLIENT_HPP
--- a/libserver/malikania/database.cpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* - * database.cpp -- generic database loader - * - * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <boost/filesystem.hpp> - -#include <stdexcept> - -#include "database.hpp" -#include "util.hpp" - -namespace mlk { - -namespace { - -using load_func = void (const database_settings&); -using unload_func = void (); - -boost::filesystem::path path(const database_settings& params) -{ - auto it = params.find("type"); - - if (it == params.end()) { - throw std::runtime_error("missing 'type' property"); - } - - boost::filesystem::path ret(util::basedir()); - - ret /= "lib"; - ret /= "malikania"; - ret /= "0.1.0"; // TODO: change this with an appropriate sysconfig.h - ret /= it->second; - - return ret; -} - -} // !namespace - -database::database(const database_settings& params) - : m_dso(path(params), boost::dll::load_mode::append_decorations) -{ - m_dso.get<load_func>("malikania_driver_load")(params); -} - -database::~database() -{ - m_dso.get<unload_func>("malikania_driver_unload")(); -} - -} // !mlk
--- a/libserver/malikania/database.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * database.hpp -- generic database loader - * - * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_DATABASE_HPP -#define MALIKANIA_DATABASE_HPP - -/** - * \file database.hpp - * \brief Generic database loader. - */ - -#include <boost/dll.hpp> - -#include <string> -#include <unordered_map> - -namespace mlk { - -/** - * Generic settings for database. - */ -using database_settings = std::unordered_map<std::string, std::string>; - -/** - * \brief Generic database. - */ -class database { -private: - boost::dll::shared_library m_dso; - -public: - /** - * Load the database driver dynamically. - * - * \param params the parameters to pass to the driver - * \throw std::exception on errors - */ - database(const database_settings& params); - - /** - * Close the database. - */ - ~database(); - - /** - * Get the associated dso handle. - * - * \return the handle - */ - inline const boost::dll::shared_library& handle() const noexcept - { - return m_dso; - } - - /** - * Overloaded function - * - * \return the handle - */ - inline boost::dll::shared_library& handle() noexcept - { - return m_dso; - } -}; - -} // !mlk - -#endif // !MALIKANIA_DATABASE_HPP
--- a/libserver/malikania/hash.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,262 +0,0 @@ -/* - * hash.hpp -- hash functions - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_SERVER_HASH_HPP -#define MALIKANIA_SERVER_HASH_HPP - -/** - * \file hash.hpp - * \brief Hash functions. - * \author David Demelier <markand@malikania.fr> - */ - -/** - * \brief Define buffer size. - */ -#if !defined(HASH_BUFFER_SIZE) -# define HASH_BUFFER_SIZE 2048 -#endif - -#include <cassert> -#include <istream> -#include <iterator> -#include <sstream> -#include <string> - -#include <openssl/sha.h> -#include <openssl/md5.h> - -/** - * \brief Hash namespace. - */ -namespace hash { - -/** - * \cond HASH_HIDDEN_SYMBOLS - */ - -namespace detail { - -template <typename Context> -using init_func = int (*)(Context *); - -template <typename Context> -using update_func = int (*)(Context *, const void *, size_t); - -template <typename Context> -using final_func = int (*)(unsigned char *, Context *); - -template <typename Context, size_t Length, typename InputIt> -inline std::string convert(InputIt it, - InputIt end, - init_func<Context> init, - update_func<Context> update, - final_func<Context> finalize) -{ - unsigned char digest[Length] = { 0 }; - char hash[Length * 2 + 1]; - char buf[HASH_BUFFER_SIZE]; - - Context ctx; - init(&ctx); - - while (it != end) { - unsigned i; - - for (i = 0; it != end && i < HASH_BUFFER_SIZE; ++i) { - buf[i] = *it++; - } - - update(&ctx, buf, i); - } - - finalize(digest, &ctx); - - for (unsigned long i = 0; i < Length; i++) { - std::snprintf(&hash[i * 2], 2 + 1, "%02x", static_cast<unsigned>(digest[i])); - } - - return std::string(hash); -} - -} // !namespace - -/** - * \endcond - */ - -/** - * \brief The scheme to use. - */ -enum class scheme { - md5, //!< MD5 - sha1, //!< SHA-1 - sha256, //!< SHA-256 - sha512 //!< SHA-512 -}; - -/** - * Generic function. - * - * \param scheme the scheme to use - * \param it the first character - * \param end the last character - * \return the string - */ -template <typename InputIt> -inline std::string to_string(scheme scheme, InputIt it, InputIt end) -{ - assert(scheme >= scheme::md5 && scheme <= scheme::sha512); - - std::string result; - - switch (scheme) { - case scheme::md5: - result = detail::convert<MD5_CTX, MD5_DIGEST_LENGTH>(it, end, MD5_Init, MD5_Update, MD5_Final); - break; - case scheme::sha1: - result = detail::convert<SHA_CTX, SHA_DIGEST_LENGTH>(it, end, SHA1_Init, SHA1_Update, SHA1_Final);; - break; - case scheme::sha256: - result = detail::convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(it, end, SHA256_Init, SHA256_Update, SHA256_Final); - break; - case scheme::sha512: - result = detail::convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(it, end, SHA512_Init, SHA512_Update, SHA512_Final); - break; - default: - break; - } - - return result; -} - -/** - * Overload for std::istream. - * - * \param scheme the scheme to use - * \param input the input stream - * \return the string - */ -inline std::string to_string(scheme scheme, std::istream &input) -{ - return to_string(scheme, std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>()); -} - -/** - * Overload for std::string. - * - * \param scheme the scheme to use - * \param input the input stream - * \return the string - */ -inline std::string to_string(scheme scheme, const std::string &input) -{ - return to_string(scheme, input.begin(), input.end()); -} - -/** - * Hash using MD5. - * - * \param input the input string - * \return the hashed string - */ -inline std::string md5(const std::string &input) -{ - return to_string(scheme::md5, input); -} - -/** - * Hash using MD5. - * - * \param input the input stream - * \return the hashed string - */ -inline std::string md5(std::istream &input) -{ - return to_string(scheme::md5, input); -} - -/** - * Hash using SHA1. - * - * \param input the input string - * \return the hashed string - */ -inline std::string sha1(const std::string &input) -{ - return to_string(scheme::sha1, input); -} - -/** - * Hash using SHA1. - * - * \param input the input stream - * \return the hashed string - */ -inline std::string sha1(std::istream &input) -{ - return to_string(scheme::sha1, input); -} - -/** - * Hash using SHA256. - * - * \param input the input string - * \return the hashed string - */ -inline std::string sha256(const std::string &input) -{ - return to_string(scheme::sha256, input); -} - -/** - * Hash using SHA256. - * - * \param input the input stream - * \return the hashed string - */ -inline std::string sha256(std::istream &input) -{ - return to_string(scheme::sha256, input); -} - -/** - * Hash using SHA512. - * - * \param input the input string - * \return the hashed string - */ -inline std::string sha512(const std::string &input) -{ - return to_string(scheme::sha512, input); -} - -/** - * Hash using SHA512. - * - * \param input the input stream - * \return the hashed string - */ -inline std::string sha512(std::istream &input) -{ - return to_string(scheme::sha512, input); -} - -} // !hash - -#endif // !MALIKANIA_SERVER_HASH_HPP
--- a/libserver/malikania/server.cpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * server.cpp -- malikania basic server - * - * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <iostream> - -#include "account_dao.hpp" -#include "client.hpp" -#include "server.hpp" -#include "util.hpp" - -namespace mlk { - -void server::handle_auth(std::shared_ptr<client> clt, std::string args) -{ - std::cout << "== auth ==" << std::endl; - - auto list = util::net::split(args); - - if (list.size() != 2) { - clt->error("auth", "2 arguments required"); - } else { - mlk::account_dao dao(m_database); - - if (!dao.authenticate(list[0], list[1])) { - clt->error("auth", "invalid credential or inexistant account"); - } else { - clt->ok("auth"); - } - } -} - -void server::start() -{ - auto clt = std::make_shared<client>(*this, m_service, m_context); - - m_acceptor.async_accept(clt->m_socket.lowest_layer(), [this, clt] (auto code) { - this->handle_accept(std::move(clt), code); - }); -} - -boost::asio::ip::tcp::endpoint server::endpoint(const server_settings ¶ms) const -{ - // TODO: add more settings there. - return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port); -} - -server::server(boost::asio::io_service& service, - const server_settings& sv_params, - const database_settings& db_params) - : m_service(service) - , m_acceptor(service, endpoint(sv_params)) - , m_context(boost::asio::ssl::context::sslv23) - , m_handlers{ - { "auth", std::bind(&server::handle_auth, this, std::placeholders::_1, std::placeholders::_2) } - } - , m_database(db_params) -{ - m_context.use_certificate_chain_file(sv_params.certificate); - m_context.use_private_key_file(sv_params.key, boost::asio::ssl::context::pem); - - start(); -} - -void server::add_handler(std::string cmd, handler func) -{ - m_handlers.emplace(std::move(cmd), std::move(func)); -} - -void server::handle_disconnect(std::shared_ptr<client> clt) -{ - std::cout << "client disconnected" << std::endl; - m_clients.erase(clt); -} - -void server::handle_message(std::shared_ptr<client> clt, std::string cmd, std::string args) -{ - std::cout << "client sent:\n"; - std::cout << " -> cmd [" << cmd << "]\n"; - std::cout << " -> args [" << args << "]\n"; - - auto it = m_handlers.find(cmd); - - if (it != m_handlers.end()) { - it->second(std::move(clt), std::move(args)); - } -} - -void server::handle_accept(std::shared_ptr<client> clt, boost::system::error_code code) -{ - if (code) { - std::cerr << "failed to accept: " << code << std::endl; - } else { - std::cout << "new client connected" << std::endl; - clt->handshake(); - m_clients.insert(std::move(clt)); - } - - start(); -} - -} // !mlk
--- a/libserver/malikania/server.hpp Sun Jan 22 11:07:36 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * server.hpp -- malikania basic server - * - * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef MALIKANIA_SERVER_HPP -#define MALIKANIA_SERVER_HPP - -#include <cstdint> -#include <functional> -#include <memory> -#include <string> -#include <unordered_map> -#include <unordered_set> - -#include <boost/asio.hpp> -#include <boost/asio/ssl.hpp> - -#include "database.hpp" - -namespace mlk { - -class client; - -class server_settings { -public: - std::uint16_t port; - std::string certificate; - std::string key; -}; - -class server { -public: - using handler = std::function<void (std::shared_ptr<client>, std::string)>; - -private: - boost::asio::io_service& m_service; - boost::asio::ip::tcp::acceptor m_acceptor; - boost::asio::ssl::context m_context; - std::unordered_set<std::shared_ptr<client>> m_clients; - std::unordered_map<std::string, handler> m_handlers; - - database m_database; - - void handle_auth(std::shared_ptr<client>, std::string); - void start(); - - boost::asio::ip::tcp::endpoint endpoint(const server_settings& params) const; - -public: - server(boost::asio::io_service& service, - const server_settings& sv_params, - const database_settings& db_params); - - void add_handler(std::string cmd, handler func); - - void handle_disconnect(std::shared_ptr<client>); - - void handle_message(std::shared_ptr<client>, std::string, std::string); - - void handle_accept(std::shared_ptr<client>, boost::system::error_code); -}; - -} // !mlk - -#endif // !MALIKANIA_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/account.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,61 @@ +/* + * account.hpp -- account model + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_ACCOUNT_HPP +#define MALIKANIA_ACCOUNT_HPP + +/** + * \file account.hpp + * \brief Account model. + */ + +#include <cstdint> +#include <string> + +namespace mlk { + +namespace server { + +/** + * \brief Account model + */ +class account { +public: + std::uint64_t id; + std::string name; + std::string email; + std::string first_name; + std::string last_name; + std::string password; +}; + +inline bool operator==(const account &ac1, const account &ac2) noexcept +{ + return ac1.id == ac2.id; +} + +inline bool operator!=(const account &ac1, const account &ac2) noexcept +{ + return !(ac1 == ac2); +} + +} // !server + +} // !mlk + +#endif // !MALIKANIA_ACCOUNT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/account_dao.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,87 @@ +/* + * dao-account.cpp -- database account management + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "account.hpp" +#include "account_dao.hpp" +#include "database.hpp" +#include "hash.hpp" + +namespace mlk { + +namespace server { + +using create_func = void (account&); +using remove_func = void (const account&); +using update_func = void (account&); +using get_func = boost::optional<account> (std::uint64_t); +using find_by_name_func = boost::optional<account> (const std::string&); +using list_func = std::vector<account> (); +using count_func = std::uint64_t (); +using clear_func = void (); + +bool account_dao::authenticate(const std::string& login, const std::string& pass) +{ + auto ac = find_by_name(login); + auto sha512 = hash::sha512(pass); + + return ac && ac->password == sha512; +} + +void account_dao::create(account& account) +{ + m_database.handle().get<create_func>("malikania_account_create")(account); +} + +void account_dao::remove(const account& account) +{ + m_database.handle().get<remove_func>("malikania_account_remove")(account); +} + +void account_dao::update(account& account) +{ + m_database.handle().get<update_func>("malikania_account_update")(account); +} + +boost::optional<account> account_dao::get(uint64_t id) +{ + return m_database.handle().get<get_func>("malikania_account_get")(id); +} + +boost::optional<account> account_dao::find_by_name(const std::string& name) +{ + return m_database.handle().get<find_by_name_func>("malikania_account_find_by_name")(name); +} + +std::vector<account> account_dao::list() +{ + return m_database.handle().get<list_func>("malikania_account_list")(); +} + +std::uint64_t account_dao::count() +{ + return m_database.handle().get<count_func>("malikania_account_count")(); +} + +void account_dao::clear() +{ + m_database.handle().get<clear_func>("malikania_account_clear")(); +} + +} // !server + +} // !mlk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/account_dao.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,139 @@ +/* + * account_dao.hpp -- database account management + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_SERVER_ACCOUNT_DAO_HPP +#define MALIKANIA_SERVER_ACCOUNT_DAO_HPP + +/** + * \file account_dao.hpp + * \brief Database account management. + */ + +#include <cstdint> +#include <vector> + +#include <boost/optional.hpp> + +namespace mlk { + +namespace server { + +class database; +class account; + +/** + * \brief Account DAO. + */ +class account_dao { +private: + database& m_database; + +public: + /** + * Constructor. + * + * \param database the database + */ + inline account_dao(database& database) noexcept + : m_database(database) + { + } + + /** + * Check if the user authentication is valid. + * + * \param login the login name + * \param pass the clear password + * \return true if authentication was successful + */ + bool authenticate(const std::string& login, const std::string& pass); + + /** + * Create the given account. + * + * The object will be modified in place. + * + * \param account the account to add + * \throw std::exception on errors + */ + void create(account& account); + + /** + * Remove the given account, all data that references this account is + * deleted too. + * + * \param account the account + * \throw std::exception on errors + */ + void remove(const account& account); + + /** + * Update account only, does not recurse into objects that references the + * account. + * + * \param account the account + * \throw std::exception on errors + */ + void update(account& account); + + /** + * Get an account. + * + * \param id the account id + * \return the account + * \throw std::exception if not found + */ + boost::optional<account> get(std::uint64_t id); + + /** + * Find an account by name. + * + * \param name the account name + * \return the account or empty one if not found + * \throw std::exception on other errors + */ + boost::optional<account> find_by_name(const std::string& name); + + /** + * Get the list of account. + * + * \throw std::exception on errors + */ + std::vector<account> list(); + + /** + * Get the number of accounts. + * + * \return the number of account. + */ + std::uint64_t count(); + + /** + * Remove all accounts recursively. + * + * \throw std::exception on errors + * \warning use with care + */ + void clear(); +}; + +} // !server + +} // !mlk + +#endif // !MALIKANIA_SERVER_ACCOUNT_DAO_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/client.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,161 @@ +#include <cassert> +#include <iostream> + +#include "client.hpp" +#include "server.hpp" + +namespace mlk { + +namespace server { + +/* + * client_proxy_writer + * ------------------------------------------------------------------ + */ + +client_proxy_writer::client_proxy_writer(std::shared_ptr<client> client, std::string init) + : m_client(std::move(client)) +{ + assert(m_client); + + m_output << init; +} + +client_proxy_writer::~client_proxy_writer() +{ + m_client->send(m_output.str()); +} + +/* + * client_proxy_writer + * ------------------------------------------------------------------ + */ + +void client::handle_read(boost::system::error_code code, std::size_t xfer) +{ + // TODO: use fixed size stream + verify exceed. + if (code) { + m_server.handle_disconnect(shared_from_this()); + } else { + std::istringstream iss(std::string( + boost::asio::buffers_begin(m_input.data()), + boost::asio::buffers_begin(m_input.data()) + xfer - 4 + )); + + // Remove early in case of errors. + m_input.consume(xfer); + + // Extract command name. + std::string cmd; + iss >> cmd >> std::ws; + + if (!iss) { + return; + } + + // Rest of the data. + std::string data(std::istreambuf_iterator<char>(iss), {}); + + if (!iss) { + return; + } + + m_server.handle_message(shared_from_this(), std::move(cmd), std::move(data)); + do_read(); + } +} + +void client::handle_write(boost::system::error_code code, std::size_t) +{ + if (code) { + m_server.handle_disconnect(shared_from_this()); + } else { + m_output.pop_front(); + + if (!m_output.empty()) { + do_write(); + } + } +} + +void client::do_read() +{ + auto self = shared_from_this(); + + boost::asio::async_read_until(m_socket, m_input, "\r\n\r\n", [self] (auto code, auto xfer) { + self->handle_read(code, xfer); + }); +} + +void client::do_write() +{ + assert(!m_output.empty()); + + auto self = shared_from_this(); + + boost::asio::async_write(m_socket, boost::asio::buffer(m_output[0]), [self] (auto code, auto xfer) { + self->handle_write(code, xfer); + }); +} + +client::client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context) + : m_server(server) + , m_socket(service, context) +{ +} + +void client::handshake() +{ + auto self = shared_from_this(); + + m_socket.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) { + if (code) { + std::cerr << "handshake failure: " << code << std::endl; + } else { + self->read(); + } + }); +} + +void client::read() +{ + do_read(); +} + +void client::send(std::string cmd, std::string data) +{ + send_raw(cmd + " " + data); +} + +void client::ok(std::string cmd) +{ + send_raw(cmd + " ok"); +} + +void client::error(std::string cmd, std::string reason) +{ + send_raw(cmd + " error " + reason); +} + +client_proxy_writer client::send(std::string cmd) +{ + return client_proxy_writer(shared_from_this(), cmd + " "); +} + +void client::send_raw(std::string data) +{ + assert(!data.empty()); + + auto in_progress = !m_output.empty(); + + data += "\r\n\r\n"; + m_output.push_back(std::move(data)); + + if (!in_progress) { + do_write(); + } +} + +} // !server + +} // !mlk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/client.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,125 @@ +#ifndef MALIKANIA_SERVER_CLIENT_HPP +#define MALIKANIA_SERVER_CLIENT_HPP + +#include <cstdint> +#include <deque> +#include <memory> +#include <string> +#include <sstream> + +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +namespace mlk { + +namespace server { + +class client; +class server; + +class client_proxy_writer { + std::shared_ptr<client> m_client; + std::ostringstream m_output; + +public: + client_proxy_writer(std::shared_ptr<client> client, std::string init); + + client_proxy_writer(client_proxy_writer&&) = default; + + ~client_proxy_writer(); + + template <typename Arg> + inline client_proxy_writer& operator<<(Arg&& arg) + { + m_output << std::forward<Arg>(arg); + + return *this; + } +}; + +class client : public std::enable_shared_from_this<client> { +public: + friend class server; + + enum class state : std::uint8_t { + authenticating, + ready + }; + +private: + server& m_server; + state m_state{state::authenticating}; + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_socket; + boost::asio::streambuf m_input; + std::deque<std::string> m_output; + + void handle_read(boost::system::error_code, std::size_t); + void handle_write(boost::system::error_code, std::size_t); + + void do_read(); + void do_write(); + + void handshake(); + void read(); + +public: + /** + * Create the client in the associated server. + * + * \param server the server object + * \param service the main loop service + * \param context the ssl context + */ + client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context); + + /** + * Send a command and its arguments. + * + * \pre !cmd.empty() + * \param cmd the command + * \param args the arguments + */ + void send(std::string cmd, std::string args); + + /** + * Send successful command result. + * + * \pre !cmd.empty() + * \param cmd the command name + */ + void ok(std::string cmd); + + /** + * Send a error command result. + * + * \pre !cmd.empty() && !reason.empty() + * \param cmd the command name + * \param reason the reason string + */ + void error(std::string cmd, std::string reason); + + /** + * Convenient function for sending data using operator<<. + * + * The returned have operator<< defined for the user and will append all + * data inserted that way when the appropriate client_proxy_write is + * destroyed. + * + * \param cmd the command name + */ + client_proxy_writer send(std::string cmd); + + /** + * Send a raw data. + * + * \pre !data.empty() + * \param data the data + */ + void send_raw(std::string data); +}; + +} // !server + +} // !mlk + +#endif // !MALIKANIA_SERVER_CLIENT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/database.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,68 @@ +/* + * database.cpp -- generic database loader + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <boost/filesystem.hpp> + +#include <stdexcept> + +#include "database.hpp" +#include "util.hpp" + +namespace mlk { + +namespace server { + +namespace { + +using load_func = void (const database_settings&); +using unload_func = void (); + +boost::filesystem::path path(const database_settings& params) +{ + auto it = params.find("type"); + + if (it == params.end()) { + throw std::runtime_error("missing 'type' property"); + } + + boost::filesystem::path ret(util::basedir()); + + ret /= "lib"; + ret /= "malikania"; + ret /= "0.1.0"; // TODO: change this with an appropriate sysconfig.h + ret /= it->second; + + return ret; +} + +} // !namespace + +database::database(const database_settings& params) + : m_dso(path(params), boost::dll::load_mode::append_decorations) +{ + m_dso.get<load_func>("malikania_driver_load")(params); +} + +database::~database() +{ + m_dso.get<unload_func>("malikania_driver_unload")(); +} + +} // !server + +} // !mlk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/database.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,87 @@ +/* + * database.hpp -- generic database loader + * + * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_DATABASE_HPP +#define MALIKANIA_DATABASE_HPP + +/** + * \file database.hpp + * \brief Generic database loader. + */ + +#include <boost/dll.hpp> + +#include <string> +#include <unordered_map> + +namespace mlk { + +namespace server { + +/** + * Generic settings for database. + */ +using database_settings = std::unordered_map<std::string, std::string>; + +/** + * \brief Generic database. + */ +class database { +private: + boost::dll::shared_library m_dso; + +public: + /** + * Load the database driver dynamically. + * + * \param params the parameters to pass to the driver + * \throw std::exception on errors + */ + database(const database_settings& params); + + /** + * Close the database. + */ + ~database(); + + /** + * Get the associated dso handle. + * + * \return the handle + */ + inline const boost::dll::shared_library& handle() const noexcept + { + return m_dso; + } + + /** + * Overloaded function + * + * \return the handle + */ + inline boost::dll::shared_library& handle() noexcept + { + return m_dso; + } +}; + +} // !server + +} // !mlk + +#endif // !MALIKANIA_DATABASE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/hash.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,262 @@ +/* + * hash.hpp -- hash functions + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_SERVER_HASH_HPP +#define MALIKANIA_SERVER_HASH_HPP + +/** + * \file hash.hpp + * \brief Hash functions. + * \author David Demelier <markand@malikania.fr> + */ + +/** + * \brief Define buffer size. + */ +#if !defined(HASH_BUFFER_SIZE) +# define HASH_BUFFER_SIZE 2048 +#endif + +#include <cassert> +#include <istream> +#include <iterator> +#include <sstream> +#include <string> + +#include <openssl/sha.h> +#include <openssl/md5.h> + +/** + * \brief Hash namespace. + */ +namespace hash { + +/** + * \cond HASH_HIDDEN_SYMBOLS + */ + +namespace detail { + +template <typename Context> +using init_func = int (*)(Context *); + +template <typename Context> +using update_func = int (*)(Context *, const void *, size_t); + +template <typename Context> +using final_func = int (*)(unsigned char *, Context *); + +template <typename Context, size_t Length, typename InputIt> +inline std::string convert(InputIt it, + InputIt end, + init_func<Context> init, + update_func<Context> update, + final_func<Context> finalize) +{ + unsigned char digest[Length] = { 0 }; + char hash[Length * 2 + 1]; + char buf[HASH_BUFFER_SIZE]; + + Context ctx; + init(&ctx); + + while (it != end) { + unsigned i; + + for (i = 0; it != end && i < HASH_BUFFER_SIZE; ++i) { + buf[i] = *it++; + } + + update(&ctx, buf, i); + } + + finalize(digest, &ctx); + + for (unsigned long i = 0; i < Length; i++) { + std::snprintf(&hash[i * 2], 2 + 1, "%02x", static_cast<unsigned>(digest[i])); + } + + return std::string(hash); +} + +} // !namespace + +/** + * \endcond + */ + +/** + * \brief The scheme to use. + */ +enum class scheme { + md5, //!< MD5 + sha1, //!< SHA-1 + sha256, //!< SHA-256 + sha512 //!< SHA-512 +}; + +/** + * Generic function. + * + * \param scheme the scheme to use + * \param it the first character + * \param end the last character + * \return the string + */ +template <typename InputIt> +inline std::string to_string(scheme scheme, InputIt it, InputIt end) +{ + assert(scheme >= scheme::md5 && scheme <= scheme::sha512); + + std::string result; + + switch (scheme) { + case scheme::md5: + result = detail::convert<MD5_CTX, MD5_DIGEST_LENGTH>(it, end, MD5_Init, MD5_Update, MD5_Final); + break; + case scheme::sha1: + result = detail::convert<SHA_CTX, SHA_DIGEST_LENGTH>(it, end, SHA1_Init, SHA1_Update, SHA1_Final);; + break; + case scheme::sha256: + result = detail::convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(it, end, SHA256_Init, SHA256_Update, SHA256_Final); + break; + case scheme::sha512: + result = detail::convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(it, end, SHA512_Init, SHA512_Update, SHA512_Final); + break; + default: + break; + } + + return result; +} + +/** + * Overload for std::istream. + * + * \param scheme the scheme to use + * \param input the input stream + * \return the string + */ +inline std::string to_string(scheme scheme, std::istream &input) +{ + return to_string(scheme, std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>()); +} + +/** + * Overload for std::string. + * + * \param scheme the scheme to use + * \param input the input stream + * \return the string + */ +inline std::string to_string(scheme scheme, const std::string &input) +{ + return to_string(scheme, input.begin(), input.end()); +} + +/** + * Hash using MD5. + * + * \param input the input string + * \return the hashed string + */ +inline std::string md5(const std::string &input) +{ + return to_string(scheme::md5, input); +} + +/** + * Hash using MD5. + * + * \param input the input stream + * \return the hashed string + */ +inline std::string md5(std::istream &input) +{ + return to_string(scheme::md5, input); +} + +/** + * Hash using SHA1. + * + * \param input the input string + * \return the hashed string + */ +inline std::string sha1(const std::string &input) +{ + return to_string(scheme::sha1, input); +} + +/** + * Hash using SHA1. + * + * \param input the input stream + * \return the hashed string + */ +inline std::string sha1(std::istream &input) +{ + return to_string(scheme::sha1, input); +} + +/** + * Hash using SHA256. + * + * \param input the input string + * \return the hashed string + */ +inline std::string sha256(const std::string &input) +{ + return to_string(scheme::sha256, input); +} + +/** + * Hash using SHA256. + * + * \param input the input stream + * \return the hashed string + */ +inline std::string sha256(std::istream &input) +{ + return to_string(scheme::sha256, input); +} + +/** + * Hash using SHA512. + * + * \param input the input string + * \return the hashed string + */ +inline std::string sha512(const std::string &input) +{ + return to_string(scheme::sha512, input); +} + +/** + * Hash using SHA512. + * + * \param input the input stream + * \return the hashed string + */ +inline std::string sha512(std::istream &input) +{ + return to_string(scheme::sha512, input); +} + +} // !hash + +#endif // !MALIKANIA_SERVER_HASH_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/server.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,120 @@ +/* + * server.cpp -- malikania basic server + * + * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> + +#include "account_dao.hpp" +#include "client.hpp" +#include "server.hpp" +#include "util.hpp" + +namespace mlk { + +namespace server { + +void server::handle_auth(std::shared_ptr<client> clt, std::string args) +{ + std::cout << "== auth ==" << std::endl; + + auto list = util::net::split(args); + + if (list.size() != 2) { + clt->error("auth", "2 arguments required"); + } else { + account_dao dao(m_database); + + if (!dao.authenticate(list[0], list[1])) { + clt->error("auth", "invalid credential or inexistant account"); + } else { + clt->ok("auth"); + } + } +} + +void server::start() +{ + auto clt = std::make_shared<client>(*this, m_service, m_context); + + m_acceptor.async_accept(clt->m_socket.lowest_layer(), [this, clt] (auto code) { + this->handle_accept(std::move(clt), code); + }); +} + +boost::asio::ip::tcp::endpoint server::endpoint(const server_settings ¶ms) const +{ + // TODO: add more settings there. + return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port); +} + +server::server(boost::asio::io_service& service, + const server_settings& sv_params, + const database_settings& db_params) + : m_service(service) + , m_acceptor(service, endpoint(sv_params)) + , m_context(boost::asio::ssl::context::sslv23) + , m_handlers{ + { "auth", std::bind(&server::handle_auth, this, std::placeholders::_1, std::placeholders::_2) } + } + , m_database(db_params) +{ + m_context.use_certificate_chain_file(sv_params.certificate); + m_context.use_private_key_file(sv_params.key, boost::asio::ssl::context::pem); + + start(); +} + +void server::add_handler(std::string cmd, handler func) +{ + m_handlers.emplace(std::move(cmd), std::move(func)); +} + +void server::handle_disconnect(std::shared_ptr<client> clt) +{ + std::cout << "client disconnected" << std::endl; + m_clients.erase(clt); +} + +void server::handle_message(std::shared_ptr<client> clt, std::string cmd, std::string args) +{ + std::cout << "client sent:\n"; + std::cout << " -> cmd [" << cmd << "]\n"; + std::cout << " -> args [" << args << "]\n"; + + auto it = m_handlers.find(cmd); + + if (it != m_handlers.end()) { + it->second(std::move(clt), std::move(args)); + } +} + +void server::handle_accept(std::shared_ptr<client> clt, boost::system::error_code code) +{ + if (code) { + std::cerr << "failed to accept: " << code << std::endl; + } else { + std::cout << "new client connected" << std::endl; + clt->handshake(); + m_clients.insert(std::move(clt)); + } + + start(); +} + +} // !server + +} // !mlk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/server/server.hpp Sun Jan 22 17:54:56 2017 +0100 @@ -0,0 +1,83 @@ +/* + * server.hpp -- malikania basic server + * + * Copyright (c) 2016-2017 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MALIKANIA_SERVER_HPP +#define MALIKANIA_SERVER_HPP + +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <unordered_map> +#include <unordered_set> + +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + +#include "database.hpp" + +namespace mlk { + +namespace server { + +class client; + +class server_settings { +public: + std::uint16_t port; + std::string certificate; + std::string key; +}; + +class server { +public: + using handler = std::function<void (std::shared_ptr<client>, std::string)>; + +private: + boost::asio::io_service& m_service; + boost::asio::ip::tcp::acceptor m_acceptor; + boost::asio::ssl::context m_context; + std::unordered_set<std::shared_ptr<client>> m_clients; + std::unordered_map<std::string, handler> m_handlers; + + database m_database; + + void handle_auth(std::shared_ptr<client>, std::string); + void start(); + + boost::asio::ip::tcp::endpoint endpoint(const server_settings& params) const; + +public: + server(boost::asio::io_service& service, + const server_settings& sv_params, + const database_settings& db_params); + + void add_handler(std::string cmd, handler func); + + void handle_disconnect(std::shared_ptr<client>); + + void handle_message(std::shared_ptr<client>, std::string, std::string); + + void handle_accept(std::shared_ptr<client>, boost::system::error_code); +}; + +} // !server + +} // !mlk + +#endif // !MALIKANIA_SERVER_HPP
--- a/server/main.cpp Sun Jan 22 11:07:36 2017 +0100 +++ b/server/main.cpp Sun Jan 22 17:54:56 2017 +0100 @@ -18,17 +18,17 @@ #include <iostream> -#include <malikania/server.hpp> +#include <malikania/server/server.hpp> int main() { - mlk::server_settings sv_params; + mlk::server::server_settings sv_params; sv_params.port = 3320; sv_params.certificate = "/home/markand/null/server.crt"; sv_params.key = "/home/markand/null/server.key"; - mlk::database_settings db_params; + mlk::server::database_settings db_params; db_params["type"] = "sqlite"; db_params["path"] = "/home/markand/kingdom.db"; @@ -36,7 +36,7 @@ boost::asio::io_service service; try { - mlk::server server(service, sv_params, db_params); + mlk::server::server server(service, sv_params, db_params); for (;;) { service.run();