Mercurial > malikania
changeset 46:b0593a3e2ca8
Server: use Boost.Asio and add basic authentication support
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 04 Dec 2016 21:26:18 +0100 |
parents | 719b5457ea92 |
children | 7097a91b08a8 |
files | database/sqlite/CMakeLists.txt database/sqlite/src/account.cpp database/sqlite/src/driver.cpp database/sqlite/src/sqlite3ext.h libcommon/malikania/util.cpp libcommon/malikania/util.hpp libserver/CMakeLists.txt libserver/malikania/account_dao.cpp libserver/malikania/account_dao.hpp libserver/malikania/client.cpp libserver/malikania/client.hpp libserver/malikania/connection.cpp libserver/malikania/connection.hpp libserver/malikania/connection_service.cpp libserver/malikania/connection_service.hpp libserver/malikania/connection_state.cpp libserver/malikania/connection_state.hpp libserver/malikania/connection_state_authenticating.cpp libserver/malikania/connection_state_authenticating.hpp libserver/malikania/connection_state_closing.cpp libserver/malikania/connection_state_closing.hpp libserver/malikania/connection_state_disconnected.cpp libserver/malikania/connection_state_disconnected.hpp libserver/malikania/connection_state_greeting.cpp libserver/malikania/connection_state_greeting.hpp libserver/malikania/connection_state_ready.cpp libserver/malikania/connection_state_ready.hpp libserver/malikania/database.cpp libserver/malikania/database.hpp libserver/malikania/hash.hpp libserver/malikania/server.cpp libserver/malikania/server.hpp server/CMakeLists.txt server/main.cpp tests/libcommon/util/main.cpp |
diffstat | 35 files changed, 1108 insertions(+), 1804 deletions(-) [+] |
line wrap: on
line diff
--- a/database/sqlite/CMakeLists.txt Wed Nov 30 21:16:34 2016 +0100 +++ b/database/sqlite/CMakeLists.txt Sun Dec 04 21:26:18 2016 +0100 @@ -27,14 +27,12 @@ ${db-sqlite_SOURCE_DIR}/src/account.cpp ${db-sqlite_SOURCE_DIR}/src/driver.cpp ${db-sqlite_SOURCE_DIR}/src/sqlite3.c - ${db-sqlite_SOURCE_DIR}/src/sqlite3ext.h ${db-sqlite_SOURCE_DIR}/src/sqlite3.h ) set_target_properties( mlk-driver-sqlite PROPERTIES - PREFIX "" OUTPUT_NAME sqlite LIBRARY_OUTPUT_DIRECTORY ${malikania_BINARY_DIR}/fakeroot/${WITH_DRIVERDIR} )
--- a/database/sqlite/src/account.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/database/sqlite/src/account.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -17,6 +17,7 @@ */ #include <boost/dll.hpp> +#include <boost/optional.hpp> #include <vector> @@ -54,6 +55,12 @@ "where ac_id = ?" ); +const std::string find_by_name_query( + "select ac_id, ac_name, ac_email, ac_firstname, ac_lastname, ac_password " + "from mk_account " + "where ac_name = ?" +); + const std::string list_query( "select ac_id, ac_name, ac_email, ac_firstname, ac_lastname, ac_password " "from mk_account" @@ -131,17 +138,30 @@ } } -BOOST_SYMBOL_EXPORT account malikania_account_get(std::uint64_t id) +BOOST_SYMBOL_EXPORT boost::optional<account> malikania_account_get(std::uint64_t id) { auto stmt = sqlite::prepare(get_query); sqlite3_bind_int64(stmt.get(), 1, id); if (sqlite3_step(stmt.get()) != SQLITE_ROW) { - throw std::runtime_error(sqlite3_errmsg(sqlite::database.get())); + return boost::optional<account>(); } - return to_account(stmt); + return boost::make_optional(to_account(stmt)); +} + +BOOST_SYMBOL_EXPORT boost::optional<account> malikania_account_find_by_name(const std::string& name) +{ + auto stmt = sqlite::prepare(find_by_name_query); + + sqlite3_bind_text(stmt.get(), 1, name.c_str(), -1, nullptr); + + if (sqlite3_step(stmt.get()) != SQLITE_ROW) { + return boost::optional<account>(); + } + + return boost::make_optional(to_account(stmt)); } BOOST_SYMBOL_EXPORT std::vector<account> malikania_account_list()
--- a/database/sqlite/src/driver.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/database/sqlite/src/driver.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -5,6 +5,11 @@ #include "driver.hpp" +/* + * Global shared stuff for the driver + * ------------------------------------------------------------------ + */ + namespace sqlite { std::unique_ptr<sqlite3, int (*)(sqlite3 *)> database{nullptr, nullptr}; @@ -22,6 +27,38 @@ } // !sqlite +/* + * Local function to this file. + * ------------------------------------------------------------------ + */ + +namespace { + +const std::string verify_query( + "select name " + "from sqlite_master " + "where type='table' " + "and name='mk_info'" +); + +bool is_initialized() +{ + auto stmt = sqlite::prepare(verify_query); + + if (sqlite3_step(stmt.get()) != SQLITE_ROW) { + return false; + } + + return strcmp((char*)sqlite3_column_text(stmt.get(), 0), "mk_info") == 0; +} + +} // !namespace + +/* + * Driver API + * ------------------------------------------------------------------ + */ + extern "C" { BOOST_SYMBOL_EXPORT void malikania_driver_load(const std::unordered_map<std::string, std::string>& params) @@ -40,12 +77,9 @@ sqlite::database = {db, &sqlite3_close}; -#if 0 - // Not needed with SQLITE_DEFAULT_FOREIGN_KEYS - if (sqlite3_exec(db, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr) != SQLITE_OK) { - throw std::runtime_error(sqlite3_errmsg(db)); + if (!is_initialized()) { + throw std::runtime_error("database not initialized"); } -#endif } BOOST_SYMBOL_EXPORT void malikania_driver_unload()
--- a/database/sqlite/src/sqlite3ext.h Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,560 +0,0 @@ -/* -** 2006 June 7 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This header file defines the SQLite interface for use by -** shared libraries that want to be imported as extensions into -** an SQLite instance. Shared libraries that intend to be loaded -** as extensions by SQLite should #include this file instead of -** sqlite3.h. -*/ -#ifndef SQLITE3EXT_H -#define SQLITE3EXT_H -#include "sqlite3.h" - -/* -** The following structure holds pointers to all of the SQLite API -** routines. -** -** WARNING: In order to maintain backwards compatibility, add new -** interfaces to the end of this structure only. If you insert new -** interfaces in the middle of this structure, then older different -** versions of SQLite will not be able to load each other's shared -** libraries! -*/ -struct sqlite3_api_routines { - void * (*aggregate_context)(sqlite3_context*,int nBytes); - int (*aggregate_count)(sqlite3_context*); - int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); - int (*bind_double)(sqlite3_stmt*,int,double); - int (*bind_int)(sqlite3_stmt*,int,int); - int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); - int (*bind_null)(sqlite3_stmt*,int); - int (*bind_parameter_count)(sqlite3_stmt*); - int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); - const char * (*bind_parameter_name)(sqlite3_stmt*,int); - int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); - int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); - int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); - int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); - int (*busy_timeout)(sqlite3*,int ms); - int (*changes)(sqlite3*); - int (*close)(sqlite3*); - int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, - int eTextRep,const char*)); - int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, - int eTextRep,const void*)); - const void * (*column_blob)(sqlite3_stmt*,int iCol); - int (*column_bytes)(sqlite3_stmt*,int iCol); - int (*column_bytes16)(sqlite3_stmt*,int iCol); - int (*column_count)(sqlite3_stmt*pStmt); - const char * (*column_database_name)(sqlite3_stmt*,int); - const void * (*column_database_name16)(sqlite3_stmt*,int); - const char * (*column_decltype)(sqlite3_stmt*,int i); - const void * (*column_decltype16)(sqlite3_stmt*,int); - double (*column_double)(sqlite3_stmt*,int iCol); - int (*column_int)(sqlite3_stmt*,int iCol); - sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); - const char * (*column_name)(sqlite3_stmt*,int); - const void * (*column_name16)(sqlite3_stmt*,int); - const char * (*column_origin_name)(sqlite3_stmt*,int); - const void * (*column_origin_name16)(sqlite3_stmt*,int); - const char * (*column_table_name)(sqlite3_stmt*,int); - const void * (*column_table_name16)(sqlite3_stmt*,int); - const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); - const void * (*column_text16)(sqlite3_stmt*,int iCol); - int (*column_type)(sqlite3_stmt*,int iCol); - sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); - void * (*commit_hook)(sqlite3*,int(*)(void*),void*); - int (*complete)(const char*sql); - int (*complete16)(const void*sql); - int (*create_collation)(sqlite3*,const char*,int,void*, - int(*)(void*,int,const void*,int,const void*)); - int (*create_collation16)(sqlite3*,const void*,int,void*, - int(*)(void*,int,const void*,int,const void*)); - int (*create_function)(sqlite3*,const char*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*)); - int (*create_function16)(sqlite3*,const void*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*)); - int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); - int (*data_count)(sqlite3_stmt*pStmt); - sqlite3 * (*db_handle)(sqlite3_stmt*); - int (*declare_vtab)(sqlite3*,const char*); - int (*enable_shared_cache)(int); - int (*errcode)(sqlite3*db); - const char * (*errmsg)(sqlite3*); - const void * (*errmsg16)(sqlite3*); - int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); - int (*expired)(sqlite3_stmt*); - int (*finalize)(sqlite3_stmt*pStmt); - void (*free)(void*); - void (*free_table)(char**result); - int (*get_autocommit)(sqlite3*); - void * (*get_auxdata)(sqlite3_context*,int); - int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); - int (*global_recover)(void); - void (*interruptx)(sqlite3*); - sqlite_int64 (*last_insert_rowid)(sqlite3*); - const char * (*libversion)(void); - int (*libversion_number)(void); - void *(*malloc)(int); - char * (*mprintf)(const char*,...); - int (*open)(const char*,sqlite3**); - int (*open16)(const void*,sqlite3**); - int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); - int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); - void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); - void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); - void *(*realloc)(void*,int); - int (*reset)(sqlite3_stmt*pStmt); - void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_double)(sqlite3_context*,double); - void (*result_error)(sqlite3_context*,const char*,int); - void (*result_error16)(sqlite3_context*,const void*,int); - void (*result_int)(sqlite3_context*,int); - void (*result_int64)(sqlite3_context*,sqlite_int64); - void (*result_null)(sqlite3_context*); - void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); - void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); - void (*result_value)(sqlite3_context*,sqlite3_value*); - void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); - int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, - const char*,const char*),void*); - void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); - char * (*snprintf)(int,char*,const char*,...); - int (*step)(sqlite3_stmt*); - int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, - char const**,char const**,int*,int*,int*); - void (*thread_cleanup)(void); - int (*total_changes)(sqlite3*); - void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); - int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); - void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, - sqlite_int64),void*); - void * (*user_data)(sqlite3_context*); - const void * (*value_blob)(sqlite3_value*); - int (*value_bytes)(sqlite3_value*); - int (*value_bytes16)(sqlite3_value*); - double (*value_double)(sqlite3_value*); - int (*value_int)(sqlite3_value*); - sqlite_int64 (*value_int64)(sqlite3_value*); - int (*value_numeric_type)(sqlite3_value*); - const unsigned char * (*value_text)(sqlite3_value*); - const void * (*value_text16)(sqlite3_value*); - const void * (*value_text16be)(sqlite3_value*); - const void * (*value_text16le)(sqlite3_value*); - int (*value_type)(sqlite3_value*); - char *(*vmprintf)(const char*,va_list); - /* Added ??? */ - int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); - /* Added by 3.3.13 */ - int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); - int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); - int (*clear_bindings)(sqlite3_stmt*); - /* Added by 3.4.1 */ - int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, - void (*xDestroy)(void *)); - /* Added by 3.5.0 */ - int (*bind_zeroblob)(sqlite3_stmt*,int,int); - int (*blob_bytes)(sqlite3_blob*); - int (*blob_close)(sqlite3_blob*); - int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, - int,sqlite3_blob**); - int (*blob_read)(sqlite3_blob*,void*,int,int); - int (*blob_write)(sqlite3_blob*,const void*,int,int); - int (*create_collation_v2)(sqlite3*,const char*,int,void*, - int(*)(void*,int,const void*,int,const void*), - void(*)(void*)); - int (*file_control)(sqlite3*,const char*,int,void*); - sqlite3_int64 (*memory_highwater)(int); - sqlite3_int64 (*memory_used)(void); - sqlite3_mutex *(*mutex_alloc)(int); - void (*mutex_enter)(sqlite3_mutex*); - void (*mutex_free)(sqlite3_mutex*); - void (*mutex_leave)(sqlite3_mutex*); - int (*mutex_try)(sqlite3_mutex*); - int (*open_v2)(const char*,sqlite3**,int,const char*); - int (*release_memory)(int); - void (*result_error_nomem)(sqlite3_context*); - void (*result_error_toobig)(sqlite3_context*); - int (*sleep)(int); - void (*soft_heap_limit)(int); - sqlite3_vfs *(*vfs_find)(const char*); - int (*vfs_register)(sqlite3_vfs*,int); - int (*vfs_unregister)(sqlite3_vfs*); - int (*xthreadsafe)(void); - void (*result_zeroblob)(sqlite3_context*,int); - void (*result_error_code)(sqlite3_context*,int); - int (*test_control)(int, ...); - void (*randomness)(int,void*); - sqlite3 *(*context_db_handle)(sqlite3_context*); - int (*extended_result_codes)(sqlite3*,int); - int (*limit)(sqlite3*,int,int); - sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); - const char *(*sql)(sqlite3_stmt*); - int (*status)(int,int*,int*,int); - int (*backup_finish)(sqlite3_backup*); - sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); - int (*backup_pagecount)(sqlite3_backup*); - int (*backup_remaining)(sqlite3_backup*); - int (*backup_step)(sqlite3_backup*,int); - const char *(*compileoption_get)(int); - int (*compileoption_used)(const char*); - int (*create_function_v2)(sqlite3*,const char*,int,int,void*, - void (*xFunc)(sqlite3_context*,int,sqlite3_value**), - void (*xStep)(sqlite3_context*,int,sqlite3_value**), - void (*xFinal)(sqlite3_context*), - void(*xDestroy)(void*)); - int (*db_config)(sqlite3*,int,...); - sqlite3_mutex *(*db_mutex)(sqlite3*); - int (*db_status)(sqlite3*,int,int*,int*,int); - int (*extended_errcode)(sqlite3*); - void (*log)(int,const char*,...); - sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); - const char *(*sourceid)(void); - int (*stmt_status)(sqlite3_stmt*,int,int); - int (*strnicmp)(const char*,const char*,int); - int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); - int (*wal_autocheckpoint)(sqlite3*,int); - int (*wal_checkpoint)(sqlite3*,const char*); - void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); - int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); - int (*vtab_config)(sqlite3*,int op,...); - int (*vtab_on_conflict)(sqlite3*); - /* Version 3.7.16 and later */ - int (*close_v2)(sqlite3*); - const char *(*db_filename)(sqlite3*,const char*); - int (*db_readonly)(sqlite3*,const char*); - int (*db_release_memory)(sqlite3*); - const char *(*errstr)(int); - int (*stmt_busy)(sqlite3_stmt*); - int (*stmt_readonly)(sqlite3_stmt*); - int (*stricmp)(const char*,const char*); - int (*uri_boolean)(const char*,const char*,int); - sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); - const char *(*uri_parameter)(const char*,const char*); - char *(*vsnprintf)(int,char*,const char*,va_list); - int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); - /* Version 3.8.7 and later */ - int (*auto_extension)(void(*)(void)); - int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, - void(*)(void*)); - int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, - void(*)(void*),unsigned char); - int (*cancel_auto_extension)(void(*)(void)); - int (*load_extension)(sqlite3*,const char*,const char*,char**); - void *(*malloc64)(sqlite3_uint64); - sqlite3_uint64 (*msize)(void*); - void *(*realloc64)(void*,sqlite3_uint64); - void (*reset_auto_extension)(void); - void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, - void(*)(void*)); - void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, - void(*)(void*), unsigned char); - int (*strglob)(const char*,const char*); - /* Version 3.8.11 and later */ - sqlite3_value *(*value_dup)(const sqlite3_value*); - void (*value_free)(sqlite3_value*); - int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64); - int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64); - /* Version 3.9.0 and later */ - unsigned int (*value_subtype)(sqlite3_value*); - void (*result_subtype)(sqlite3_context*,unsigned int); - /* Version 3.10.0 and later */ - int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int); - int (*strlike)(const char*,const char*,unsigned int); - int (*db_cacheflush)(sqlite3*); - /* Version 3.12.0 and later */ - int (*system_errno)(sqlite3*); - /* Version 3.14.0 and later */ - int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*); - char *(*expanded_sql)(sqlite3_stmt*); -}; - -/* -** This is the function signature used for all extension entry points. It -** is also defined in the file "loadext.c". -*/ -typedef int (*sqlite3_loadext_entry)( - sqlite3 *db, /* Handle to the database. */ - char **pzErrMsg, /* Used to set error string on failure. */ - const sqlite3_api_routines *pThunk /* Extension API function pointers. */ -); - -/* -** The following macros redefine the API routines so that they are -** redirected through the global sqlite3_api structure. -** -** This header file is also used by the loadext.c source file -** (part of the main SQLite library - not an extension) so that -** it can get access to the sqlite3_api_routines structure -** definition. But the main library does not want to redefine -** the API. So the redefinition macros are only valid if the -** SQLITE_CORE macros is undefined. -*/ -#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) -#define sqlite3_aggregate_context sqlite3_api->aggregate_context -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_aggregate_count sqlite3_api->aggregate_count -#endif -#define sqlite3_bind_blob sqlite3_api->bind_blob -#define sqlite3_bind_double sqlite3_api->bind_double -#define sqlite3_bind_int sqlite3_api->bind_int -#define sqlite3_bind_int64 sqlite3_api->bind_int64 -#define sqlite3_bind_null sqlite3_api->bind_null -#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count -#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index -#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name -#define sqlite3_bind_text sqlite3_api->bind_text -#define sqlite3_bind_text16 sqlite3_api->bind_text16 -#define sqlite3_bind_value sqlite3_api->bind_value -#define sqlite3_busy_handler sqlite3_api->busy_handler -#define sqlite3_busy_timeout sqlite3_api->busy_timeout -#define sqlite3_changes sqlite3_api->changes -#define sqlite3_close sqlite3_api->close -#define sqlite3_collation_needed sqlite3_api->collation_needed -#define sqlite3_collation_needed16 sqlite3_api->collation_needed16 -#define sqlite3_column_blob sqlite3_api->column_blob -#define sqlite3_column_bytes sqlite3_api->column_bytes -#define sqlite3_column_bytes16 sqlite3_api->column_bytes16 -#define sqlite3_column_count sqlite3_api->column_count -#define sqlite3_column_database_name sqlite3_api->column_database_name -#define sqlite3_column_database_name16 sqlite3_api->column_database_name16 -#define sqlite3_column_decltype sqlite3_api->column_decltype -#define sqlite3_column_decltype16 sqlite3_api->column_decltype16 -#define sqlite3_column_double sqlite3_api->column_double -#define sqlite3_column_int sqlite3_api->column_int -#define sqlite3_column_int64 sqlite3_api->column_int64 -#define sqlite3_column_name sqlite3_api->column_name -#define sqlite3_column_name16 sqlite3_api->column_name16 -#define sqlite3_column_origin_name sqlite3_api->column_origin_name -#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 -#define sqlite3_column_table_name sqlite3_api->column_table_name -#define sqlite3_column_table_name16 sqlite3_api->column_table_name16 -#define sqlite3_column_text sqlite3_api->column_text -#define sqlite3_column_text16 sqlite3_api->column_text16 -#define sqlite3_column_type sqlite3_api->column_type -#define sqlite3_column_value sqlite3_api->column_value -#define sqlite3_commit_hook sqlite3_api->commit_hook -#define sqlite3_complete sqlite3_api->complete -#define sqlite3_complete16 sqlite3_api->complete16 -#define sqlite3_create_collation sqlite3_api->create_collation -#define sqlite3_create_collation16 sqlite3_api->create_collation16 -#define sqlite3_create_function sqlite3_api->create_function -#define sqlite3_create_function16 sqlite3_api->create_function16 -#define sqlite3_create_module sqlite3_api->create_module -#define sqlite3_create_module_v2 sqlite3_api->create_module_v2 -#define sqlite3_data_count sqlite3_api->data_count -#define sqlite3_db_handle sqlite3_api->db_handle -#define sqlite3_declare_vtab sqlite3_api->declare_vtab -#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache -#define sqlite3_errcode sqlite3_api->errcode -#define sqlite3_errmsg sqlite3_api->errmsg -#define sqlite3_errmsg16 sqlite3_api->errmsg16 -#define sqlite3_exec sqlite3_api->exec -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_expired sqlite3_api->expired -#endif -#define sqlite3_finalize sqlite3_api->finalize -#define sqlite3_free sqlite3_api->free -#define sqlite3_free_table sqlite3_api->free_table -#define sqlite3_get_autocommit sqlite3_api->get_autocommit -#define sqlite3_get_auxdata sqlite3_api->get_auxdata -#define sqlite3_get_table sqlite3_api->get_table -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_global_recover sqlite3_api->global_recover -#endif -#define sqlite3_interrupt sqlite3_api->interruptx -#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid -#define sqlite3_libversion sqlite3_api->libversion -#define sqlite3_libversion_number sqlite3_api->libversion_number -#define sqlite3_malloc sqlite3_api->malloc -#define sqlite3_mprintf sqlite3_api->mprintf -#define sqlite3_open sqlite3_api->open -#define sqlite3_open16 sqlite3_api->open16 -#define sqlite3_prepare sqlite3_api->prepare -#define sqlite3_prepare16 sqlite3_api->prepare16 -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_profile sqlite3_api->profile -#define sqlite3_progress_handler sqlite3_api->progress_handler -#define sqlite3_realloc sqlite3_api->realloc -#define sqlite3_reset sqlite3_api->reset -#define sqlite3_result_blob sqlite3_api->result_blob -#define sqlite3_result_double sqlite3_api->result_double -#define sqlite3_result_error sqlite3_api->result_error -#define sqlite3_result_error16 sqlite3_api->result_error16 -#define sqlite3_result_int sqlite3_api->result_int -#define sqlite3_result_int64 sqlite3_api->result_int64 -#define sqlite3_result_null sqlite3_api->result_null -#define sqlite3_result_text sqlite3_api->result_text -#define sqlite3_result_text16 sqlite3_api->result_text16 -#define sqlite3_result_text16be sqlite3_api->result_text16be -#define sqlite3_result_text16le sqlite3_api->result_text16le -#define sqlite3_result_value sqlite3_api->result_value -#define sqlite3_rollback_hook sqlite3_api->rollback_hook -#define sqlite3_set_authorizer sqlite3_api->set_authorizer -#define sqlite3_set_auxdata sqlite3_api->set_auxdata -#define sqlite3_snprintf sqlite3_api->snprintf -#define sqlite3_step sqlite3_api->step -#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata -#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup -#define sqlite3_total_changes sqlite3_api->total_changes -#define sqlite3_trace sqlite3_api->trace -#ifndef SQLITE_OMIT_DEPRECATED -#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings -#endif -#define sqlite3_update_hook sqlite3_api->update_hook -#define sqlite3_user_data sqlite3_api->user_data -#define sqlite3_value_blob sqlite3_api->value_blob -#define sqlite3_value_bytes sqlite3_api->value_bytes -#define sqlite3_value_bytes16 sqlite3_api->value_bytes16 -#define sqlite3_value_double sqlite3_api->value_double -#define sqlite3_value_int sqlite3_api->value_int -#define sqlite3_value_int64 sqlite3_api->value_int64 -#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type -#define sqlite3_value_text sqlite3_api->value_text -#define sqlite3_value_text16 sqlite3_api->value_text16 -#define sqlite3_value_text16be sqlite3_api->value_text16be -#define sqlite3_value_text16le sqlite3_api->value_text16le -#define sqlite3_value_type sqlite3_api->value_type -#define sqlite3_vmprintf sqlite3_api->vmprintf -#define sqlite3_vsnprintf sqlite3_api->vsnprintf -#define sqlite3_overload_function sqlite3_api->overload_function -#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 -#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 -#define sqlite3_clear_bindings sqlite3_api->clear_bindings -#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob -#define sqlite3_blob_bytes sqlite3_api->blob_bytes -#define sqlite3_blob_close sqlite3_api->blob_close -#define sqlite3_blob_open sqlite3_api->blob_open -#define sqlite3_blob_read sqlite3_api->blob_read -#define sqlite3_blob_write sqlite3_api->blob_write -#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 -#define sqlite3_file_control sqlite3_api->file_control -#define sqlite3_memory_highwater sqlite3_api->memory_highwater -#define sqlite3_memory_used sqlite3_api->memory_used -#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc -#define sqlite3_mutex_enter sqlite3_api->mutex_enter -#define sqlite3_mutex_free sqlite3_api->mutex_free -#define sqlite3_mutex_leave sqlite3_api->mutex_leave -#define sqlite3_mutex_try sqlite3_api->mutex_try -#define sqlite3_open_v2 sqlite3_api->open_v2 -#define sqlite3_release_memory sqlite3_api->release_memory -#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem -#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig -#define sqlite3_sleep sqlite3_api->sleep -#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit -#define sqlite3_vfs_find sqlite3_api->vfs_find -#define sqlite3_vfs_register sqlite3_api->vfs_register -#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister -#define sqlite3_threadsafe sqlite3_api->xthreadsafe -#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob -#define sqlite3_result_error_code sqlite3_api->result_error_code -#define sqlite3_test_control sqlite3_api->test_control -#define sqlite3_randomness sqlite3_api->randomness -#define sqlite3_context_db_handle sqlite3_api->context_db_handle -#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes -#define sqlite3_limit sqlite3_api->limit -#define sqlite3_next_stmt sqlite3_api->next_stmt -#define sqlite3_sql sqlite3_api->sql -#define sqlite3_status sqlite3_api->status -#define sqlite3_backup_finish sqlite3_api->backup_finish -#define sqlite3_backup_init sqlite3_api->backup_init -#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount -#define sqlite3_backup_remaining sqlite3_api->backup_remaining -#define sqlite3_backup_step sqlite3_api->backup_step -#define sqlite3_compileoption_get sqlite3_api->compileoption_get -#define sqlite3_compileoption_used sqlite3_api->compileoption_used -#define sqlite3_create_function_v2 sqlite3_api->create_function_v2 -#define sqlite3_db_config sqlite3_api->db_config -#define sqlite3_db_mutex sqlite3_api->db_mutex -#define sqlite3_db_status sqlite3_api->db_status -#define sqlite3_extended_errcode sqlite3_api->extended_errcode -#define sqlite3_log sqlite3_api->log -#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 -#define sqlite3_sourceid sqlite3_api->sourceid -#define sqlite3_stmt_status sqlite3_api->stmt_status -#define sqlite3_strnicmp sqlite3_api->strnicmp -#define sqlite3_unlock_notify sqlite3_api->unlock_notify -#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint -#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint -#define sqlite3_wal_hook sqlite3_api->wal_hook -#define sqlite3_blob_reopen sqlite3_api->blob_reopen -#define sqlite3_vtab_config sqlite3_api->vtab_config -#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict -/* Version 3.7.16 and later */ -#define sqlite3_close_v2 sqlite3_api->close_v2 -#define sqlite3_db_filename sqlite3_api->db_filename -#define sqlite3_db_readonly sqlite3_api->db_readonly -#define sqlite3_db_release_memory sqlite3_api->db_release_memory -#define sqlite3_errstr sqlite3_api->errstr -#define sqlite3_stmt_busy sqlite3_api->stmt_busy -#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly -#define sqlite3_stricmp sqlite3_api->stricmp -#define sqlite3_uri_boolean sqlite3_api->uri_boolean -#define sqlite3_uri_int64 sqlite3_api->uri_int64 -#define sqlite3_uri_parameter sqlite3_api->uri_parameter -#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf -#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 -/* Version 3.8.7 and later */ -#define sqlite3_auto_extension sqlite3_api->auto_extension -#define sqlite3_bind_blob64 sqlite3_api->bind_blob64 -#define sqlite3_bind_text64 sqlite3_api->bind_text64 -#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension -#define sqlite3_load_extension sqlite3_api->load_extension -#define sqlite3_malloc64 sqlite3_api->malloc64 -#define sqlite3_msize sqlite3_api->msize -#define sqlite3_realloc64 sqlite3_api->realloc64 -#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension -#define sqlite3_result_blob64 sqlite3_api->result_blob64 -#define sqlite3_result_text64 sqlite3_api->result_text64 -#define sqlite3_strglob sqlite3_api->strglob -/* Version 3.8.11 and later */ -#define sqlite3_value_dup sqlite3_api->value_dup -#define sqlite3_value_free sqlite3_api->value_free -#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64 -#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64 -/* Version 3.9.0 and later */ -#define sqlite3_value_subtype sqlite3_api->value_subtype -#define sqlite3_result_subtype sqlite3_api->result_subtype -/* Version 3.10.0 and later */ -#define sqlite3_status64 sqlite3_api->status64 -#define sqlite3_strlike sqlite3_api->strlike -#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush -/* Version 3.12.0 and later */ -#define sqlite3_system_errno sqlite3_api->system_errno -/* Version 3.14.0 and later */ -#define sqlite3_trace_v2 sqlite3_api->trace_v2 -#define sqlite3_expanded_sql sqlite3_api->expanded_sql -#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ - -#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) - /* This case when the file really is being compiled as a loadable - ** extension */ -# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; -# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; -# define SQLITE_EXTENSION_INIT3 \ - extern const sqlite3_api_routines *sqlite3_api; -#else - /* This case when the file is being statically linked into the - ** application */ -# define SQLITE_EXTENSION_INIT1 /*no-op*/ -# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ -# define SQLITE_EXTENSION_INIT3 /*no-op*/ -#endif - -#endif /* SQLITE3EXT_H */
--- a/libcommon/malikania/util.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libcommon/malikania/util.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -16,23 +16,243 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <string> +#include <stdexcept> + +#if defined(__linux__) +# include <unistd.h> + +# include <cerrno> +# include <cstring> +# include <stdexcept> +#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) +# if defined(__NetBSD__) +# include <sys/param.h> +# else +# include <sys/types.h> +# endif + +# if defined(__OpenBSD__) +# include <unistd.h> +# endif + +# include <sys/sysctl.h> + +# include <array> +# include <cerrno> +# include <climits> +# include <cstddef> +# include <cstdlib> +# include <cstring> +# include <stdexcept> +#elif defined(__APPLE__) +# include <cerrno> +# include <cstring> +# include <libproc.h> +# include <unistd.h> +#elif defined(_WIN32) +# include <Windows.h> +#endif + +#include <boost/filesystem.hpp> + #include "util.hpp" +namespace { + +#if defined(__linux__) + +std::string executable_path() +{ + std::string result; + + result.resize(2048, '\0'); + + auto size = readlink("/proc/self/exe", &result[0], 2048); + + if (size < 0) + throw std::runtime_error(std::strerror(errno)); + + result.resize(size, '\0'); + + return result; +} + +#elif defined(__FreeBSD__) || defined(__DragonFly__) + +std::string executable_path() +{ + std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } }; + std::string result; + std::size_t size = PATH_MAX + 1; + + result.resize(size, '\0'); + + if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) + throw std::runtime_error(std::strerror(errno)); + + result.resize(size, '\0'); + + return result; +} + +#elif defined(__APPLE__) + +std::string executable_path() +{ + std::string result; + std::size_t size = PROC_PIDPATHINFO_MAXSIZE; + + result.resize(size, '\0'); + + if ((size = proc_pidpath(getpid(), &result[0], size)) == 0) + throw std::runtime_error(std::strerror(errno)); + + result.resize(size, '\0'); + + return result; +} + +#elif defined(_WIN32) + +std::string executable_path() +{ + std::string result; + std::size_t size = PATH_MAX; + + result.resize(size, '\0'); + + if (!(size = GetModuleFileNameA(nullptr, &result[0], size))) + throw std::runtime_error("GetModuleFileName error"); + + result.resize(size, '\0'); + + return result; +} + +#elif defined(__NetBSD__) + +std::string executable_path() +{ + std::string result; + +#if defined(KERN_PROC_PATHNAME) + std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; + std::size_t size = MAXPATHLEN; + + result.resize(size, '\0'); + + if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) + throw std::runtime_error(std::strerror(errno)); + + result.resize(size, '\0'); +#else + result.resize(2048, '\0'); + + auto size = readlink("/proc/curproc/exe", &result[0], 2048); + + if (size < 0) + throw std::runtime_error(std::strerror(errno)); + + result.resize(size, '\0'); +#endif + + return result; +} + +#elif defined(__OpenBSD__) + +std::string executable_path() +{ + char **paths, *path; + std::string result; + std::size_t len; + std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + + if (sysctl(mib.data(), 4, nullptr, &len, nullptr, 0) == -1) + throw std::runtime_error(std::strerror(errno)); + if ((paths = static_cast<char **>(malloc(len))) == nullptr) + throw std::runtime_error(std::strerror(errno)); + + sysctl(mib.data(), 4, paths, &len, nullptr, 0); + + if ((path = static_cast<char *>(std::malloc(PATH_MAX + 1))) == nullptr) { + std::free(paths); + throw std::runtime_error(std::strerror(errno)); + } + + realpath(paths[0], path); + result = path; + + std::free(paths); + std::free(path); + + return result; +} + +#else + +std::string executable_path() +{ + /* Not supported */ + return ""; +} + +#endif + +} // !namespace + namespace mlk { namespace util { -std::vector<std::string> netsplit(std::string& input) +std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max) { - std::vector<std::string> ret; - std::string::size_type pos; + std::vector<std::string> result; + std::size_t next = -1, current; + int count = 1; + bool finished = false; - while ((pos = input.find("\r\n\r\n")) != std::string::npos) { - ret.push_back(input.substr(0U, pos)); - input.erase(0U, pos + 4); + if (list.empty()) { + return result; } - return ret; + do { + std::string val; + + current = next + 1; + next = list.find_first_of(delimiters, current); + + // split max, get until the end + if (max >= 0 && count++ >= max) { + val = list.substr(current, std::string::npos); + finished = true; + } else { + val = list.substr(current, next - current); + finished = next == std::string::npos; + } + + result.push_back(val); + } while (!finished); + + return result; +} + +std::string basedir() noexcept +{ + std::string result = "./"; + + try { + boost::filesystem::path tmp = executable_path(); + + tmp = tmp.parent_path(); // remove executable name + tmp = tmp.parent_path(); // remove "bin" + result = tmp.string(); + } catch (...) { + // TODO: add log. + } + + return result; } } // !util
--- a/libcommon/malikania/util.hpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libcommon/malikania/util.hpp Sun Dec 04 21:26:18 2016 +0100 @@ -30,16 +30,30 @@ namespace mlk { +/** + * \brief Utilities. + */ namespace util { /** - * Split the network message buffer by \r\n\r\n and update the - * buffer in-place. + * Split a line into a list of tokens. + * + * If max is less than 0, split as much as possible, otherwise split at most + * the number of specified tokens. * - * \param input the buffer to split and update - * \return the list of received message or empty if not ready + * \param line the line to split + * \param delim the list of delimiters allowed + * \param max the maximum number of tokens + * \return the list */ -std::vector<std::string> netsplit(std::string& input); +std::vector<std::string> split(const std::string& line, const std::string& delim, int max = -1); + +/** + * Get the base directory of the current executable. + * + * \return the base directory or empty if impossible to get + */ +std::string basedir() noexcept; /** * Clamp the value between low and high. @@ -55,6 +69,25 @@ return (value < high) ? std::max(value, low) : std::min(value, high); } +/** + * \brief Utilities for networking. + */ +namespace net { + +/** + * Shortcut for util::split(line, " ", max); + * + * \param line the line + * \param max the maximum of tokens to split + * \return the list + */ +inline std::vector<std::string> split(const std::string& line, int max = -1) +{ + return util::split(line, " ", max); +} + +} // !net + } // !util } // !mlk
--- a/libserver/CMakeLists.txt Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/CMakeLists.txt Sun Dec 04 21:26:18 2016 +0100 @@ -22,13 +22,17 @@ 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 ) 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 ) malikania_create_library( @@ -36,6 +40,8 @@ SOURCES ${HEADERS} ${SOURCES} LIBRARIES ${Boost_LIBRARIES} + OpenSSL::Crypto + OpenSSL::SSL libcommon PUBLIC_INCLUDES ${Boost_INCLUDE_DIRS}
--- a/libserver/malikania/account_dao.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/account_dao.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -19,17 +19,27 @@ #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 = account (std::uint64_t); +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); @@ -45,11 +55,16 @@ m_database.handle().get<update_func>("malikania_account_update")(account); } -account account_dao::get(uint64_t id) +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")();
--- a/libserver/malikania/account_dao.hpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/account_dao.hpp Sun Dec 04 21:26:18 2016 +0100 @@ -27,6 +27,8 @@ #include <cstdint> #include <vector> +#include <boost/optional.hpp> + namespace mlk { class database; @@ -51,6 +53,15 @@ } /** + * 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. @@ -85,7 +96,16 @@ * \return the account * \throw std::exception if not found */ - account get(std::uint64_t id); + 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/client.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -0,0 +1,157 @@ +#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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/client.hpp Sun Dec 04 21:26:18 2016 +0100 @@ -0,0 +1,121 @@ +#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 std::ostringstream& 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/connection.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/* - * connection.cpp -- client connection - * - * Copyright (c) 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. - */ - -#include <json.hpp> - -#include "connection.hpp" -#include "connection-state-closing.hpp" -#include "connection-state-greeting.hpp" - -namespace malikania { - -void Connection::handshake() noexcept -{ - try { - m_socket.handshake(); - m_handshake = Handshake::Ready; - } catch (const net::WantReadError &) { - m_handshake = Handshake::Reading; - } catch (const net::WantWriteError &) { - m_handshake = Handshake::Writing; - } catch (const std::exception &) { - } -} - -Connection::Connection(net::TlsSocket socket) - : m_socket(std::move(socket)) - , m_state(std::make_unique<GreetingState>()) -{ - m_socket.set(net::option::SockBlockMode(false)); - - handshake(); -} - -Connection::Connection(Connection &&) = default; - -Connection::~Connection() = default; - -void Connection::prepare(net::Listener<> &listener) -{ - assert(m_state); - - switch (m_handshake) { - case Handshake::Reading: - listener.reset(m_socket.handle(), net::Condition::Readable); - break; - case Handshake::Writing: - listener.reset(m_socket.handle(), net::Condition::Writable); - break; - default: - m_state->prepare(*this, listener); - break; - } -} - -void Connection::sync(net::Listener<> &listener, net::Condition condition) -{ - assert(m_state); - - switch (m_handshake) { - case Handshake::Reading: - case Handshake::Writing: - handshake(); - break; - default: - if (m_stateNext) { - std::cout << "switching state " << m_state->id() << " -> " - << m_stateNext->id() << std::endl; - m_state.reset(m_stateNext.release()); - } - - m_state->sync(*this, listener, condition); - break; - } -} - -void Connection::setState(std::unique_ptr<State> state) noexcept -{ - m_stateNext = std::move(state); -} - -void Connection::error(std::string message) -{ - m_output += std::move(message); - m_stateNext = std::make_unique<ClosingState>(); -} - -Connection &Connection::operator=(Connection &&) = default; - -} // !malikania
--- a/libserver/malikania/connection.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* - * connection.hpp -- client connection - * - * Copyright (c) 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_CONNECTION_HPP -#define MALIKANIA_CONNECTION_HPP - -/** - * \file connection.hpp - * \brief Client connection. - */ - -#include <memory> - -#include <json.hpp> - -#include "net.hpp" -#include "signals.hpp" - -namespace malikania { - -/** - * \brief Client connection. - */ -class Connection { -public: - Signal<> onDisconnect; - Signal<std::string, std::string> onAuthentication; - Signal<const nlohmann::json &> onMessage; - - class State; - class GreetingState; - class AuthenticatingState; - class ReadyState; - class ClosingState; - class DisconnectedState; - -private: - enum class Handshake { - None, - Writing, - Reading, - Ready - } m_handshake{Handshake::None}; - - net::TlsSocket m_socket; - - std::unique_ptr<State> m_state; - std::unique_ptr<State> m_stateNext; - - std::string m_input; - std::string m_output; - - void handshake() noexcept; - -public: - Connection(net::TlsSocket socket); - - Connection(Connection &&); - - virtual ~Connection(); - - inline net::Handle handle() const noexcept - { - return m_socket.handle(); - } - - virtual void prepare(net::Listener<> &listener); - - virtual void sync(net::Listener<> &listener, net::Condition condition); - - inline const State &state() const noexcept - { - return *m_state; - } - - inline State &state() noexcept - { - return *m_state; - } - - void setState(std::unique_ptr<State>) noexcept; - - void error(std::string message); - - Connection &operator=(Connection &&); -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_HPP
--- a/libserver/malikania/connection_service.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * connection-service.cpp -- manage clients server side - * - * Copyright (c) 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. - */ - -#include <iostream> - -#include "dao-account.hpp" -#include "connection-state-ready.hpp" -#include "connection-service.hpp" -#include "server.hpp" - -namespace malikania { - -void ConnectionService::slotAuthentication(Server &server, - Connection &connection, - const std::string &login, - const std::string &password) -{ - AccountDao dao(server.database()); - - try { - if (dao.authenticate(login, password)) { - std::cout << "client successfully authenticated" << std::endl; - server.post([&connection] (auto &) { - connection.setState(std::make_unique<Connection::ReadyState>()); - }); - } else { - std::cout << "client failed to authenticate" << std::endl; - connection.error("authentication failed"); - } - } catch (const std::exception &ex) { - // FATAL ERROR?? - } -} - -void ConnectionService::slotDisconnect(Server &, Connection &) -{ - std::cout << "client disconnected" << std::endl; -} - -void ConnectionService::syncMaster(Server &server) -{ - try { - auto socket = net::TlsSocket(m_master.accept()); - - socket.setCertificate(m_settings.certificate); - socket.setPrivateKey(m_settings.key); - - auto conn = std::make_unique<Connection>(std::move(socket)); - auto &ref = *conn; - - conn->onAuthentication.connect([this, &server, &ref] (auto login, auto password) { - this->slotAuthentication(server, ref, login, password); - }); - conn->onDisconnect.connect([this, &server, &ref] () { - this->slotDisconnect(server, ref); - }); - - m_clients.push_back(std::move(conn)); - - std::cout << "new client connected" << std::endl; - } catch (const std::exception &ex) { - // LOG ERROR SOMEWHERE - } -} - -void ConnectionService::syncClients(net::Listener<> &listener, const net::ListenerStatus &status) -{ - for (auto it = m_clients.begin(); it != m_clients.end(); ) { - auto &cnt = *it; - - if (cnt->handle() == status.socket) - cnt->sync(listener, status.flags); - - if (cnt->state().id() == "disconnected") - it = m_clients.erase(it); - else - ++ it; - } -} - -ConnectionService::ConnectionService(ConnectionSettings settings) - : m_settings(std::move(settings)) - , m_master(any(m_settings.type & ConnectionSettings::Ipv6) ? AF_INET6 : AF_INET, 0) -{ - if (any(m_settings.type & ConnectionSettings::Ipv6)) { - m_master.set(net::option::Ipv6Only(m_settings.type == ConnectionSettings::Ipv6)); - m_master.set(net::option::SockBlockMode(false)); - - if (m_settings.address == "*") - m_master.bind(net::ipv6::any(m_settings.port)); - else - m_master.bind(net::ipv6::pton(m_settings.address, m_settings.port)); - } else { - if (m_settings.address == "*") - m_master.bind(net::ipv4::any(m_settings.port)); - else - m_master.bind(net::ipv4::pton(m_settings.address, m_settings.port)); - } - - m_master.listen(256); -} - -void ConnectionService::prepare(Server &, net::Listener<> &listener) -{ - listener.reset(m_master.handle(), net::Condition::Readable); - - for (auto &cnt : m_clients) - cnt->prepare(listener); -} - -void ConnectionService::sync(Server &server, - net::Listener<> &listener, - const net::ListenerStatus &status) -{ - if (status.socket == m_master.handle()) - syncMaster(server); - else - syncClients(listener, status); -} - -} // !malikania
--- a/libserver/malikania/connection_service.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/* - * connection-service.hpp -- manage clients server side - * - * Copyright (c) 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_CONNECTION_SERVICE_HPP -#define MALIKANIA_CONNECTION_SERVICE_HPP - -#include <cstdint> -#include <memory> -#include <vector> - -#include "connection.hpp" - -namespace malikania { - -class Server; - -/** - * \brief Connections parameters - */ -class ConnectionSettings { -public: - enum Type { - Ipv4 = (1 << 0), //!< Add IPv4 - Ipv6 = (1 << 1), //!< Add IPv6 - All = (Ipv4 | Ipv6) //!< Use both IPv4 and IPv6 on same port - } type{All}; - - std::string certificate; //!< Certificate file - std::string key; //!< Private key file - std::string address{"*"}; //!< Address to bind - std::uint16_t port{3320}; //!< Port to use -}; - -/** - * \brief Manage connections on the server - */ -class ConnectionService { -private: - ConnectionSettings m_settings; - net::TcpSocket m_master; - std::vector<std::unique_ptr<Connection>> m_clients; - - void slotAuthentication(Server &, Connection &, const std::string &, const std::string &); - void slotDisconnect(Server &, Connection &); - - void syncMaster(Server &server); - void syncClients(net::Listener<> &listener, const net::ListenerStatus &status); - -public: - ConnectionService(ConnectionSettings settings); - - inline const std::vector<std::unique_ptr<Connection>> &clients() const noexcept - { - return m_clients; - } - - inline std::vector<std::unique_ptr<Connection>> &clients() noexcept - { - return m_clients; - } - - void prepare(Server &server, net::Listener<> &listener); - - void sync(Server &server, net::Listener<> &listener, const net::ListenerStatus &status); -}; - -/** - * \cond ENUM_HIDDEN_SYMBOLS - */ - -inline ConnectionSettings::Type operator^(ConnectionSettings::Type v1, ConnectionSettings::Type v2) noexcept -{ - return static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2)); -} - -inline ConnectionSettings::Type operator&(ConnectionSettings::Type v1, ConnectionSettings::Type v2) noexcept -{ - return static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) & static_cast<unsigned>(v2)); -} - -inline ConnectionSettings::Type operator|(ConnectionSettings::Type v1, ConnectionSettings::Type v2) noexcept -{ - return static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2)); -} - -inline ConnectionSettings::Type operator~(ConnectionSettings::Type v) noexcept -{ - return static_cast<ConnectionSettings::Type>(~static_cast<unsigned>(v)); -} - -inline ConnectionSettings::Type &operator|=(ConnectionSettings::Type &v1, ConnectionSettings::Type v2) noexcept -{ - v1 = static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2)); - - return v1; -} - -inline ConnectionSettings::Type &operator&=(ConnectionSettings::Type &v1, ConnectionSettings::Type v2) noexcept -{ - v1 = static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) & static_cast<unsigned>(v2)); - - return v1; -} - -inline ConnectionSettings::Type &operator^=(ConnectionSettings::Type &v1, ConnectionSettings::Type v2) noexcept -{ - v1 = static_cast<ConnectionSettings::Type>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2)); - - return v1; -} - -/** - * \endcond - */ - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_SERVICE_HPP
--- a/libserver/malikania/connection_state.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * connection-state.cpp -- base class for connection state - * - * Copyright (c) 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. - */ - -#include <iostream> - -#include "connection-state-disconnected.hpp" -#include "connection-state.hpp" -#include "util.hpp" - -namespace malikania { - -void Connection::State::close(Connection &conn) noexcept -{ - conn.onDisconnect(); - conn.m_state = std::make_unique<DisconnectedState>(); -} - -void Connection::State::recv(Connection &conn, std::string &input) noexcept -{ - try { - std::string buffer; - std::size_t size = 512; - - buffer.resize(size); - buffer.resize(conn.m_socket.recv(&buffer[0], size)); - - if (buffer.size() == 0) - close(conn); - - input += buffer; - } catch (const net::WantReadError &) { - conn.m_handshake = Handshake::Reading; - } catch (const net::WantWriteError &) { - conn.m_handshake = Handshake::Writing; - } catch (const std::exception &) { - close(conn); - } -} - -void Connection::State::send(Connection &conn, std::string &output) noexcept -{ - assert(output.length() != 0); - - try { - auto nsent = conn.m_socket.send(&output[0], output.length()); - - if (nsent == 0) - close(conn); - - output.erase(0, nsent); - } catch (const net::WantReadError &) { - conn.m_handshake = Handshake::Reading; - } catch (const net::WantWriteError &) { - conn.m_handshake = Handshake::Writing; - } catch (const std::exception &) { - close(conn); - } -} - -nlohmann::json Connection::State::next(Connection &conn, std::string &input) noexcept -{ - nlohmann::json object; - - try { - object = util::json::next(input); - } catch (const std::exception &) { - close(conn); - } - - return object; -} - -} // !malikania
--- a/libserver/malikania/connection_state.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * connection-state.hpp -- base class for connection state - * - * Copyright (c) 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_CONNECTION_STATE_HPP -#define MALIKANIA_CONNECTION_STATE_HPP - -/** - * \file connection-state.hpp - * \brief Base class for connection state. - */ - -#include <json.hpp> - -#include "connection.hpp" - -namespace malikania { - -/** - * \brief Base class for connection state. - */ -class Connection::State { -protected: - void close(Connection &conn) noexcept; - - void recv(Connection &conn, std::string &input) noexcept; - - void send(Connection &conn, std::string &output) noexcept; - - nlohmann::json next(Connection &conn, std::string &input) noexcept; - -public: - /** - * Default constructor. - */ - State() = default; - - /** - * Virtual destructor defaulted. - */ - virtual ~State() = default; - - /** - * Prepare the listener about that connection. - * - * This function should not care about handshake status because the - * connection object will never call this function if a handshake is - * taking place. - * - * \param connection the connection - * \param listener the listener - */ - virtual void prepare(Connection &connection, net::Listener<> &listener) = 0; - - virtual void sync(Connection &, net::Listener<> &, net::Condition) = 0; - - virtual std::string id() const noexcept = 0; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_HPP
--- a/libserver/malikania/connection_state_authenticating.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * connection-state-authenticating.cpp -- user is authenticating - * - * Copyright (c) 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. - */ - -#include "connection-state-authenticating.hpp" - -namespace malikania { - -void Connection::AuthenticatingState::prepare(Connection &conn, net::Listener<> &listener) -{ - listener.reset(conn.m_socket.handle(), net::Condition::Readable); -} - -void Connection::AuthenticatingState::sync(Connection &conn, net::Listener<> &, net::Condition) -{ - recv(conn, m_input); - - auto value = next(conn, m_input); - - if (!value.is_object()) - return; - - auto login = value.find("login"); - auto password = value.find("password"); - - if (login != value.end() && login->is_string() && - password != value.end() && password->is_string()) - conn.onAuthentication(*login, *password); -} - -std::string Connection::AuthenticatingState::id() const noexcept -{ - return "authenticating"; -} - -} // !malikania
--- a/libserver/malikania/connection_state_authenticating.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * connection-state-authenticating.hpp -- user is authenticating - * - * Copyright (c) 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_CONNECTION_STATE_AUTHENTICATING_HPP -#define MALIKANIA_CONNECTION_STATE_AUTHENTICATING_HPP - -/** - * \file connection-state-authenticating.hpp - * \brief User is authenticating. - */ - -#include "connection-state.hpp" - -namespace malikania { - -/** - * \brief User is authenticating. - */ -class Connection::AuthenticatingState : public Connection::State { -private: - std::string m_input; - -public: - void prepare(Connection &conn, net::Listener<> &listener) override; - - void sync(Connection &conn, net::Listener<> &, net::Condition) override; - - std::string id() const noexcept override; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_AUTHENTICATING_HPP
--- a/libserver/malikania/connection_state_closing.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * connection-state-closing.hpp -- closing connection to the user - * - * Copyright (c) 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. - */ - -#include "connection-state-disconnected.hpp" -#include "connection-state-closing.hpp" - -namespace malikania { - -void Connection::ClosingState::prepare(Connection &conn, net::Listener<> &listener) -{ - if (conn.m_output.empty()) - listener.remove(conn.handle()); - else - listener.reset(conn.handle(), net::Condition::Writable); -} - -void Connection::ClosingState::sync(Connection &conn, net::Listener<> &, net::Condition) -{ - send(conn, conn.m_output); - - if (conn.m_output.empty()) - conn.m_stateNext = std::make_unique<DisconnectedState>(); -} - -std::string Connection::ClosingState::id() const noexcept -{ - return "closing"; -} - -} // !malikania
--- a/libserver/malikania/connection_state_closing.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * connection-state-closing.hpp -- closing connection to the user - * - * Copyright (c) 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_CONNECTION_STATE_CLOSING_HPP -#define MALIKANIA_CONNECTION_STATE_CLOSING_HPP - -/** - * \file connection-state-closing.hpp - * \brief Closing connection to the user. - */ - -#include "connection-state.hpp" - -namespace malikania { - -/** - * \brief Closing connection to the user. - */ -class Connection::ClosingState : public Connection::State { -public: - void prepare(Connection &conn, net::Listener<> &listener) override; - - void sync(Connection &conn, net::Listener<> &, net::Condition) override; - - std::string id() const noexcept override; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_CLOSING_HPP
--- a/libserver/malikania/connection_state_disconnected.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * connection-state-disconnected.cpp -- client is down and will be removed - * - * Copyright (c) 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. - */ - -#include "connection-state-disconnected.hpp" - -namespace malikania { - -void Connection::DisconnectedState::prepare(Connection &, net::Listener<> &) -{ -} - -void Connection::DisconnectedState::sync(Connection &, net::Listener<> &, net::Condition) -{ -} - -std::string Connection::DisconnectedState::id() const noexcept -{ - return "disconnected"; -} - -} // !malikania
--- a/libserver/malikania/connection_state_disconnected.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * connection-state-disconnected.hpp -- client is down and will be removed - * - * Copyright (c) 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_CONNECTION_STATE_DISCONNECTED_HPP -#define MALIKANIA_CONNECTION_STATE_DISCONNECTED_HPP - -/** - * \file connection-state-disconected.hpp - * \brief Client is down and will be removed. - */ - -#include "connection-state.hpp" - -namespace malikania { - -/** - * \brief Client is down and will be removed. - */ -class Connection::DisconnectedState : public Connection::State { -public: - void prepare(Connection &conn, net::Listener<> &listener) override; - - void sync(Connection &conn, net::Listener<> &, net::Condition) override; - - std::string id() const noexcept override; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_DISCONNECTED_HPP
--- a/libserver/malikania/connection_state_greeting.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * connection-state-greeting.cpp -- greeting the user - * - * Copyright (c) 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. - */ - -#include <json.hpp> - -#include "connection-state-authenticating.hpp" -#include "connection-state-greeting.hpp" - -namespace malikania { - -Connection::GreetingState::GreetingState() -{ - // TODO: this is just an example. - m_output += nlohmann::json::object({ - { "malikania", "0.1.0" } - }).dump(); - m_output += "\r\n\r\n"; -} - -void Connection::GreetingState::prepare(Connection &conn, net::Listener<> &listener) -{ - listener.reset(conn.m_socket.handle(), net::Condition::Writable); -} - -void Connection::GreetingState::sync(Connection &conn, net::Listener<> &listener, net::Condition condition) -{ - assert(condition == net::Condition::Writable); - - if (m_output.length() > 0) - send(conn, m_output); - else { - listener.unset(conn.handle(), net::Condition::Writable); - conn.m_stateNext = std::make_unique<AuthenticatingState>(); - } -} - -std::string Connection::GreetingState::id() const noexcept -{ - return "greeting"; -} - -} // !malikania
--- a/libserver/malikania/connection_state_greeting.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * connection-state-greeting.hpp -- greeting the user - * - * Copyright (c) 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_CONNECTION_STATE_GREETING_HPP -#define MALIKANIA_CONNECTION_STATE_GREETING_HPP - -/** - * \file connection-state-greeting.hpp - * \brief Greeting the user. - */ - -#include "connection-state.hpp" - -namespace malikania { - -/** - * \brief Greeting the user. - */ -class Connection::GreetingState : public Connection::State { -private: - std::string m_output; - -public: - GreetingState(); - - void prepare(Connection &conn, net::Listener<> &listener) override; - - void sync(Connection &conn, net::Listener<> &listener, net::Condition condition) override; - - std::string id() const noexcept override; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_GREETING_HPP
--- a/libserver/malikania/connection_state_ready.cpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - * connection-state-ready.cpp -- client is ready - * - * Copyright (c) 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. - */ - -#include "connection-state-ready.hpp" - -namespace malikania { - -void Connection::ReadyState::prepare(Connection &conn, net::Listener<> &listener) -{ - if (!conn.m_output.empty()) - listener.reset(conn.handle(), net::Condition::Writable); - - listener.reset(conn.handle(), net::Condition::Readable); -} - -void Connection::ReadyState::sync(Connection &conn, net::Listener<> &lst, net::Condition cond) -{ - if (bool(cond & net::Condition::Readable)) - recv(conn, conn.m_input); - - if (bool(cond & net::Condition::Writable)) { - send(conn, conn.m_output); - - if (conn.m_output.empty()) - lst.unset(conn.handle(), net::Condition::Writable); - } -} - -std::string Connection::ReadyState::id() const noexcept -{ - return "ready"; -} - -} // !malikania
--- a/libserver/malikania/connection_state_ready.hpp Wed Nov 30 21:16:34 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * connection-state-ready.hpp -- client is ready - * - * Copyright (c) 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_CONNECTION_STATE_READY_HPP -#define MALIKANIA_CONNECTION_STATE_READY_HPP - -/** - * \file connection-state-ready.hpp - * \brief Client is ready. - */ - -#include "connection-state.hpp" - -namespace malikania { - -/** - * \brief Client is ready. - */ -class Connection::ReadyState : public Connection::State { -public: - void prepare(Connection &conn, net::Listener<> &listener) override; - - void sync(Connection &conn, net::Listener<> &listener, net::Condition condition) override; - - std::string id() const noexcept override; -}; - -} // !malikania - -#endif // !MALIKANIA_CONNECTION_STATE_READY_HPP
--- a/libserver/malikania/database.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/database.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -16,15 +16,42 @@ * 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 { -using load_func = void (const std::unordered_map<std::string, std::string>&); +namespace { + +using load_func = void (const database_settings&); using unload_func = void (); -database::database(const std::string& path, const std::unordered_map<std::string, std::string>& params) - : m_dso(path) +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); }
--- a/libserver/malikania/database.hpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/database.hpp Sun Dec 04 21:26:18 2016 +0100 @@ -32,6 +32,11 @@ namespace mlk { /** + * Generic settings for database. + */ +using database_settings = std::unordered_map<std::string, std::string>; + +/** * \brief Generic database. */ class database { @@ -42,13 +47,10 @@ /** * Load the database driver dynamically. * - * The type is the canonical file name (e.g. "pgsql"). - * - * \param type the database name * \param params the parameters to pass to the driver * \throw std::exception on errors */ - database(const std::string& type, const std::unordered_map<std::string, std::string>& params); + database(const database_settings& params); /** * Close the database.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libserver/malikania/hash.hpp Sun Dec 04 21:26:18 2016 +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
--- a/libserver/malikania/server.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/server.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -18,34 +18,99 @@ #include <iostream> +#include "account_dao.hpp" +#include "client.hpp" #include "server.hpp" - -namespace malikania { +#include "util.hpp" -Server::Server(ConnectionSettings settings, DatabaseSettings dbsettings) - : m_connections(settings) - , m_database(dbsettings) +namespace mlk { + +void server::handle_auth(std::shared_ptr<client> clt, std::string args) { -} + std::cout << "== auth ==" << std::endl; -void Server::run() noexcept -{ - for (;;) { - m_connections.prepare(*this, m_listener); - auto status = m_listener.wait(); - m_connections.sync(*this, m_listener, status); + auto list = util::net::split(args); - // Enqueue events. - for (auto &ev : m_events) - ev(*this); + if (list.size() != 2) { + clt->error("auth", "2 arguments required"); + } else { + mlk::account_dao dao(m_database); - m_events.clear(); + if (!dao.authenticate(list[0], list[1])) { + clt->error("auth", "invalid credential or inexistant account"); + } else { + clt->ok("auth"); + } } } -void Server::post(Event ev) noexcept +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 { - m_events.push_back(std::move(ev)); + // 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(); } -} // !malikania +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 Wed Nov 30 21:16:34 2016 +0100 +++ b/libserver/malikania/server.hpp Sun Dec 04 21:26:18 2016 +0100 @@ -19,54 +19,61 @@ #ifndef MALIKANIA_SERVER_HPP #define MALIKANIA_SERVER_HPP +#include <cstdint> #include <functional> -#include <vector> +#include <memory> +#include <string> +#include <unordered_map> +#include <unordered_set> -#include "connection-service.hpp" +#include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> + #include "database.hpp" -#include "net.hpp" -#include "signals.hpp" -namespace malikania { +namespace mlk { -class Server { +class client; + +class server_settings { public: - using Event = std::function<void (Server &)>; + std::uint16_t port; + std::string certificate; + std::string key; +}; - Signal<const Connection &> onConnection; - Signal<const Connection &> onDisconnection; +class server { +public: + using handler = std::function<void (std::shared_ptr<client>, std::string)>; private: - std::vector<Event> m_events; + 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; - net::Listener<> m_listener; + database m_database; - ConnectionService m_connections; - 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(ConnectionSettings settings, DatabaseSettings dbsettings); + server(boost::asio::io_service& service, + const server_settings& sv_params, + const database_settings& db_params); - inline const ConnectionService &connections() const noexcept - { - return m_connections; - } + void add_handler(std::string cmd, handler func); - inline ConnectionService &connections() noexcept - { - return m_connections; - } + void handle_disconnect(std::shared_ptr<client>); - inline Database &database() - { - return m_database; - } + void handle_message(std::shared_ptr<client>, std::string, std::string); - void run() noexcept; - - void post(Event func) noexcept; + void handle_accept(std::shared_ptr<client>, boost::system::error_code); }; -} // !malikania +} // !mlk #endif // !MALIKANIA_SERVER_HPP
--- a/server/CMakeLists.txt Wed Nov 30 21:16:34 2016 +0100 +++ b/server/CMakeLists.txt Sun Dec 04 21:26:18 2016 +0100 @@ -20,3 +20,17 @@ add_executable(mlk-server main.cpp) target_link_libraries(mlk-server libmlk-server) +set_target_properties( + mlk-server + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${malikania_BINARY_DIR}/fakeroot/bin +) + +foreach (c ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${c} c) + set_target_properties( + mlk-server + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_${c} ${malikania_BINARY_DIR}/fakeroot/bin + ) +endforeach()
--- a/server/main.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/server/main.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -16,6 +16,32 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <iostream> + +#include <malikania/server.hpp> + int main() { + mlk::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; + + db_params["type"] = "sqlite"; + db_params["path"] = "/home/markand/kingdom.db"; + + boost::asio::io_service service; + + try { + mlk::server server(service, sv_params, db_params); + + for (;;) { + service.run(); + } + } catch (const std::exception& ex) { + std::cerr << "fatal: " << ex.what() << std::endl; + } }
--- a/tests/libcommon/util/main.cpp Wed Nov 30 21:16:34 2016 +0100 +++ b/tests/libcommon/util/main.cpp Sun Dec 04 21:26:18 2016 +0100 @@ -56,54 +56,3 @@ } BOOST_AUTO_TEST_SUITE_END() - -/* - * util::netsplit - * ------------------------------------------------------------------ - */ - -BOOST_AUTO_TEST_SUITE(netsplit) - -BOOST_AUTO_TEST_CASE(simple) -{ - std::string input = "hello world\r\n\r\n"; - std::vector<std::string> messages = util::netsplit(input); - - BOOST_REQUIRE_EQUAL(1U, messages.size()); - BOOST_REQUIRE_EQUAL("hello world", messages[0]); - BOOST_REQUIRE(input.empty()); -} - -BOOST_AUTO_TEST_CASE(two) -{ - std::string input = "hello world\r\n\r\nhow are you?\r\n\r\n"; - std::vector<std::string> messages = util::netsplit(input); - - BOOST_REQUIRE_EQUAL(2U, messages.size()); - BOOST_REQUIRE_EQUAL("hello world", messages[0]); - BOOST_REQUIRE_EQUAL("how are you?", messages[1]); - BOOST_REQUIRE(input.empty()); -} - -BOOST_AUTO_TEST_CASE(imcomplete) -{ - std::string input = "hello world\r\n"; - std::vector<std::string> messages = util::netsplit(input); - - BOOST_REQUIRE_EQUAL(0U, messages.size()); - BOOST_REQUIRE_EQUAL("hello world\r\n", input); -} - -BOOST_AUTO_TEST_CASE(empty) -{ - std::string input = "hello world\r\n\r\n\r\n\r\nhow are you?\r\n\r\n"; - std::vector<std::string> messages = util::netsplit(input); - - BOOST_REQUIRE_EQUAL(3U, messages.size()); - BOOST_REQUIRE_EQUAL("hello world", messages[0]); - BOOST_REQUIRE(messages[1].empty()); - BOOST_REQUIRE_EQUAL("how are you?", messages[2]); - BOOST_REQUIRE(input.empty()); -} - -BOOST_AUTO_TEST_SUITE_END()