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 &params) 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()