From 6c2f79eeca64b07da121345c74d6a9e678d69c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 24 May 2017 16:38:20 +0200 Subject: Fix the date format in the RPM spec file --- packaging/biboumi.spec.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/biboumi.spec.cmake b/packaging/biboumi.spec.cmake index dccd2f9..158e289 100644 --- a/packaging/biboumi.spec.cmake +++ b/packaging/biboumi.spec.cmake @@ -59,7 +59,7 @@ make check %{?_smp_mflags} %changelog -* Wed May 24 Le Coz Florent - 5.0-1 +* Wed May 24 2017 Le Coz Florent - 5.0-1 - Update to version 5.0 * Wed May 2 2017 Le Coz Florent - 4.3-1 -- cgit v1.2.3 From ca9db6f933978051807caa2f4e147bde7c5578db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 28 May 2017 01:01:24 +0200 Subject: Move biboumi.h.cmake inside the src/ directory --- CMakeLists.txt | 2 +- biboumi.h.cmake | 12 ------------ src/biboumi.h.cmake | 12 ++++++++++++ 3 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 biboumi.h.cmake create mode 100644 src/biboumi.h.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 139f85a..6b081bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,4 +432,4 @@ mark_as_advanced(HAS_PUT_TIME) configure_file(unit/biboumi.service.cmake biboumi.service) configure_file(packaging/biboumi.spec.cmake biboumi.spec) -configure_file(biboumi.h.cmake src/biboumi.h) +configure_file(src/biboumi.h.cmake src/biboumi.h) diff --git a/biboumi.h.cmake b/biboumi.h.cmake deleted file mode 100644 index 1ad9a40..0000000 --- a/biboumi.h.cmake +++ /dev/null @@ -1,12 +0,0 @@ -#cmakedefine USE_DATABASE -#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST -#cmakedefine LIBIDN_FOUND -#cmakedefine SYSTEMD_FOUND -#cmakedefine POLLER ${POLLER} -#cmakedefine BOTAN_FOUND -#cmakedefine GCRYPT_FOUND -#cmakedefine UDNS_FOUND -#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" -#cmakedefine PROJECT_NAME "${PROJECT_NAME}" -#cmakedefine HAS_GET_TIME -#cmakedefine HAS_PUT_TIME diff --git a/src/biboumi.h.cmake b/src/biboumi.h.cmake new file mode 100644 index 0000000..1ad9a40 --- /dev/null +++ b/src/biboumi.h.cmake @@ -0,0 +1,12 @@ +#cmakedefine USE_DATABASE +#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST +#cmakedefine LIBIDN_FOUND +#cmakedefine SYSTEMD_FOUND +#cmakedefine POLLER ${POLLER} +#cmakedefine BOTAN_FOUND +#cmakedefine GCRYPT_FOUND +#cmakedefine UDNS_FOUND +#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" +#cmakedefine PROJECT_NAME "${PROJECT_NAME}" +#cmakedefine HAS_GET_TIME +#cmakedefine HAS_PUT_TIME -- cgit v1.2.3 From a5f73969b0b75e0a4af17d78fc8ce0765796a13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 1 Jun 2017 17:37:47 +0200 Subject: Change the makepkg command used in the archlinux CI test --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 657acef..88062a8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -306,9 +306,9 @@ packaging:archlinux: - sudo pacman -Syuu --noconfirm - git clone https://aur.archlinux.org/litesql-git.git - cd litesql-git - - makepkg --noconfirm -s && makepkg -f && sudo pacman --noconfirm -U *.pkg.* + - makepkg -si --noconfirm - cd .. - git clone https://aur.archlinux.org/biboumi-git.git - cd biboumi-git - - makepkg --noconfirm -s && makepkg -f && sudo pacman --noconfirm -U *.pkg.* + - makepkg -si --noconfirm dependencies: [] -- cgit v1.2.3 From ceb496369f834ffa055eb5b7ffc273b2a21f9b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 1 Jun 2017 17:38:03 +0200 Subject: Test that the archlinux package actually installs at least /usr/bin/biboumi --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 88062a8..6604adb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -311,4 +311,5 @@ packaging:archlinux: - git clone https://aur.archlinux.org/biboumi-git.git - cd biboumi-git - makepkg -si --noconfirm + - test -e /usr/bin/biboumi dependencies: [] -- cgit v1.2.3 From 7ca95a09740297ae9c041c5f8ae4caa0a57a149a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Jun 2017 10:30:51 +0200 Subject: Find sqlite3 instead of litesql Simplifies the CMakeLists.txt a little bit --- CMakeLists.txt | 31 ++++++----------- cmake/Modules/FindLITESQL.cmake | 76 ----------------------------------------- cmake/Modules/FindSQLITE3.cmake | 43 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 96 deletions(-) delete mode 100644 cmake/Modules/FindLITESQL.cmake create mode 100644 cmake/Modules/FindSQLITE3.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b081bc..ed20870 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,10 +124,10 @@ elseif(NOT WITHOUT_UDNS) find_package(UDNS) endif() -if(WITH_LITESQL) - find_package(LITESQL REQUIRED) -elseif(NOT WITHOUT_LITESQL) - find_package(LITESQL) +if(WITH_SQLITE3) + find_package(SQLITE3 REQUIRED) +elseif(NOT WITHOUT_SQLITE3) + find_package(SQLITE3) endif() # @@ -158,17 +158,14 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}/") file(GLOB source_utils src/utils/*.[hc]pp) add_library(utils OBJECT ${source_utils}) -add_dependencies(utils litesql_generated_sources) file(GLOB source_irc src/irc/*.[hc]pp) add_library(irc OBJECT ${source_irc}) -add_dependencies(irc litesql_generated_sources) file(GLOB source_xmpp src/xmpp/*.[hc]pp) add_library(xmpp OBJECT ${source_xmpp}) -add_dependencies(xmpp litesql_generated_sources) file(GLOB source_identd src/identd/*.[hc]pp) @@ -177,7 +174,6 @@ add_library(identd OBJECT ${source_identd}) file(GLOB source_bridge src/bridge/*.[hc]pp) add_library(bridge OBJECT ${source_bridge}) -add_dependencies(bridge litesql_generated_sources) file(GLOB source_config src/config/*.[hc]pp) @@ -191,20 +187,15 @@ file(GLOB source_network src/network/*.[hc]pp) add_library(network OBJECT ${source_network}) -if(LITESQL_FOUND) - LITESQL_GENERATE_CPP("database/database.xml" - "biboudb" - LITESQL_GENERATED_SOURCES) - add_custom_target(litesql_generated_sources SOURCES ${LITESQL_GENERATED_SOURCES}) +if(SQLITE3_FOUND) + file(GLOB source_database + src/database/*.[hc]pp) + add_library(database OBJECT ${source_database}) - add_library(database OBJECT src/database/database.cpp ${LITESQL_GENERATED_SOURCES}) - add_dependencies(database litesql_generated_sources) - - include_directories(database ${LITESQL_INCLUDE_DIRS}) + include_directories(database ${SQLITE3_INCLUDE_DIRS}) set(USE_DATABASE TRUE) else() add_library(database OBJECT "") - add_custom_target(litesql_generated_sources) endif() # @@ -269,8 +260,8 @@ if(LIBIDN_FOUND) target_link_libraries(test_suite ${LIBIDN_LIBRARIES}) endif() if(USE_DATABASE) - target_link_libraries(${PROJECT_NAME} ${LITESQL_LIBRARIES}) - target_link_libraries(test_suite ${LITESQL_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} ${SQLITE3_LIBRARIES}) + target_link_libraries(test_suite ${SQLITE3_LIBRARIES}) endif() # Define a __FILENAME__ macro with the relative path (from the base project directory) diff --git a/cmake/Modules/FindLITESQL.cmake b/cmake/Modules/FindLITESQL.cmake deleted file mode 100644 index 2d3b073..0000000 --- a/cmake/Modules/FindLITESQL.cmake +++ /dev/null @@ -1,76 +0,0 @@ -# - Find LiteSQL -# -# Find the LiteSQL library, and defines a function to generate C++ files -# from the database xml file using litesql-gen fro -# -# This module defines the following variables: -# LITESQL_FOUND - True if library and include directory are found -# If set to TRUE, the following are also defined: -# LITESQL_INCLUDE_DIRS - The directory where to find the header file -# LITESQL_LIBRARIES - Where to find the library file -# LITESQL_GENERATE_CPP - A function, to be used like this: -# LITESQL_GENERATE_CPP("db/database.xml" # The file defining the db schemas -# "database" # The name of the C++ “module” -# # that will be generated -# LITESQL_GENERATED_SOURCES # Variable containing the -# resulting C++ files to compile -# -# For conveniance, these variables are also set. They have the same values -# than the variables above. The user can thus choose his/her prefered way -# to write them. -# LITESQL_INCLUDE_DIR -# LITESQL_LIBRARY -# -# This file is in the public domain - -find_path(LITESQL_INCLUDE_DIRS NAMES litesql.hpp - DOC "The LiteSQL include directory") - -find_library(LITESQL_LIBRARIES NAMES litesql - DOC "The LiteSQL library") - -foreach(DB_TYPE sqlite postgresql mysql ocilib) - string(TOUPPER ${DB_TYPE} DB_TYPE_UPPER) - find_library(LITESQL_${DB_TYPE_UPPER}_LIB_PATH NAMES litesql_${DB_TYPE} - DOC "The ${DB_TYPE} backend for LiteSQL") - if(LITESQL_${DB_TYPE_UPPER}_LIB_PATH) - list(APPEND LITESQL_LIBRARIES ${LITESQL_${DB_TYPE_UPPER}_LIB_PATH}) - endif() - mark_as_advanced(LITESQL_${DB_TYPE_UPPER}_LIB_PATH) -endforeach() - -find_program(LITESQLGEN_EXECUTABLE NAMES litesql-gen - DOC "The utility that creates .h and .cpp files from a xml database description") - -# Use some standard module to handle the QUIETLY and REQUIRED arguments, and -# set LITESQL_FOUND to TRUE if these two variables are set. -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LITESQL REQUIRED_VARS LITESQL_LIBRARIES LITESQL_INCLUDE_DIRS - LITESQLGEN_EXECUTABLE) - -# Compatibility for all the ways of writing these variables -if(LITESQL_FOUND) - set(LITESQL_INCLUDE_DIR ${LITESQL_INCLUDE_DIRS}) - set(LITESQL_LIBRARY ${LITESQL_LIBRARIES}) -endif() - -mark_as_advanced(LITESQL_INCLUDE_DIRS LITESQL_LIBRARIES LITESQLGEN_EXECUTABLE) - - -# LITESQL_GENERATE_CPP function - -function(LITESQL_GENERATE_CPP - SOURCE_FILE OUTPUT_NAME OUTPUT_SOURCES) - set(${OUTPUT_SOURCES}) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}.cpp" - "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}.hpp" - COMMAND ${LITESQLGEN_EXECUTABLE} - ARGS -t c++ --output-dir=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE} - DEPENDS ${SOURCE_FILE} - COMMENT "Running litesql-gen on ${SOURCE_FILE}" - VERBATIM) - list(APPEND ${OUTPUT_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_NAME}.cpp") - set_source_files_properties(${${OUTPUT_SOURCES}} PROPERTIES GENERATED TRUE) - set(${OUTPUT_SOURCES} ${${OUTPUT_SOURCES}} PARENT_SCOPE) -endfunction() diff --git a/cmake/Modules/FindSQLITE3.cmake b/cmake/Modules/FindSQLITE3.cmake new file mode 100644 index 0000000..2861b37 --- /dev/null +++ b/cmake/Modules/FindSQLITE3.cmake @@ -0,0 +1,43 @@ +# - Find sqlite3 +# Find the sqlite3 cryptographic library +# +# This module defines the following variables: +# SQLITE3_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# SQLITE3_INCLUDE_DIRS - The directory where to find the header file +# SQLITE3_LIBRARIES - Where to find the library file +# +# For conveniance, these variables are also set. They have the same values +# than the variables above. The user can thus choose his/her prefered way +# to write them. +# SQLITE3_LIBRARY +# SQLITE3_INCLUDE_DIR +# +# This file is in the public domain + +include(FindPkgConfig) + +if(NOT SQLITE3_FOUND) + pkg_check_modules(SQLITE3 sqlite3) +endif() + +if(NOT SQLITE3_FOUND) + find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h + DOC "The sqlite3 include directory") + + find_library(SQLITE3_LIBRARIES NAMES sqlite3 + DOC "The sqlite3 library") + + # Use some standard module to handle the QUIETLY and REQUIRED arguments, and + # set SQLITE3_FOUND to TRUE if these two variables are set. + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(SQLITE3 REQUIRED_VARS SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS) + + if(SQLITE3_FOUND) + set(SQLITE3_LIBRARY ${SQLITE3_LIBRARIES} CACHE INTERNAL "") + set(SQLITE3_INCLUDE_DIR ${SQLITE3_INCLUDE_DIRS} CACHE INTERNAL "") + set(SQLITE3_FOUND ${SQLITE3_FOUND} CACHE INTERNAL "") + endif() +endif() + +mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES) \ No newline at end of file -- cgit v1.2.3 From 50cadf3dac0d56ef8181d1800cc30f8dcb749141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Jun 2017 10:38:39 +0200 Subject: Implement our own database ORM, and update the whole code to use it Entirely replace LiteSQL fix #3271 --- database/database.xml | 70 ------------- src/bridge/bridge.cpp | 18 ++-- src/database/column.hpp | 13 +++ src/database/count_query.hpp | 35 +++++++ src/database/database.cpp | 204 +++++++++++++++++------------------- src/database/database.hpp | 153 ++++++++++++++++++++------- src/database/insert_query.hpp | 128 ++++++++++++++++++++++ src/database/query.cpp | 11 ++ src/database/query.hpp | 46 ++++++++ src/database/row.hpp | 75 +++++++++++++ src/database/select_query.hpp | 153 +++++++++++++++++++++++++++ src/database/table.hpp | 84 +++++++++++++++ src/database/type_to_sql.cpp | 7 ++ src/database/type_to_sql.hpp | 6 ++ src/irc/irc_client.cpp | 24 ++--- src/main.cpp | 5 +- src/utils/reload.cpp | 2 +- src/xmpp/biboumi_adhoc_commands.cpp | 88 ++++++++-------- src/xmpp/biboumi_component.cpp | 16 ++- src/xmpp/biboumi_component.hpp | 4 +- tests/database.cpp | 46 ++++---- 21 files changed, 867 insertions(+), 321 deletions(-) delete mode 100644 database/database.xml create mode 100644 src/database/column.hpp create mode 100644 src/database/count_query.hpp create mode 100644 src/database/insert_query.hpp create mode 100644 src/database/query.cpp create mode 100644 src/database/query.hpp create mode 100644 src/database/row.hpp create mode 100644 src/database/select_query.hpp create mode 100644 src/database/table.hpp create mode 100644 src/database/type_to_sql.cpp create mode 100644 src/database/type_to_sql.hpp diff --git a/database/database.xml b/database/database.xml deleted file mode 100644 index e641fdf..0000000 --- a/database/database.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 4a41b50..fcfcc24 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -23,7 +23,7 @@ static std::string in_encoding_for(const Bridge& bridge, const Iid& iid) #ifdef USE_DATABASE const auto jid = bridge.get_bare_jid(); auto options = Database::get_irc_channel_options_with_server_default(jid, iid.get_server(), iid.get_local()); - return options.encodingIn.value(); + return options.col(); #else (void)bridge; (void)iid; @@ -32,13 +32,13 @@ static std::string in_encoding_for(const Bridge& bridge, const Iid& iid) } Bridge::Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr& poller): - user_jid(std::move(user_jid)), + user_jid(std::move(user_jid)), xmpp(xmpp), poller(poller) { #ifdef USE_DATABASE const auto options = Database::get_global_options(this->user_jid); - this->set_record_history(options.recordHistory.value()); + this->set_record_history(options.col()); #endif } @@ -258,7 +258,7 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) #ifdef USE_DATABASE const auto xmpp_body = this->make_xmpp_body(line); if (this->record_history) - uuid = Database::store_muc_message(this->get_bare_jid(), iid, std::chrono::system_clock::now(), + uuid = Database::store_muc_message(this->get_bare_jid(), iid.get_local(), iid.get_server(), std::chrono::system_clock::now(), std::get<0>(xmpp_body), irc->get_own_nick()); #endif for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) @@ -436,7 +436,7 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con #ifdef USE_DATABASE const auto coptions = Database::get_irc_channel_options_with_server_default(this->user_jid, iid.get_server(), iid.get_local()); - persistent = coptions.persistent.value(); + persistent = coptions.col(); #endif if (channel->joined && !channel->parting && !persistent) { @@ -837,7 +837,7 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st #ifdef USE_DATABASE const auto xmpp_body = this->make_xmpp_body(body, encoding); if (!nick.empty() && this->record_history) - Database::store_muc_message(this->get_bare_jid(), iid, std::chrono::system_clock::now(), + Database::store_muc_message(this->get_bare_jid(), iid.get_local(), iid.get_server(), std::chrono::system_clock::now(), std::get<0>(xmpp_body), nick); #endif for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) @@ -994,12 +994,12 @@ void Bridge::send_room_history(const std::string& hostname, std::string chan_nam { #ifdef USE_DATABASE const auto coptions = Database::get_irc_channel_options_with_server_and_global_default(this->user_jid, hostname, chan_name); - const auto lines = Database::get_muc_logs(this->user_jid, chan_name, hostname, coptions.maxHistoryLength.value()); + const auto lines = Database::get_muc_logs(this->user_jid, chan_name, hostname, coptions.col()); chan_name.append(utils::empty_if_fixed_server("%" + hostname)); for (const auto& line: lines) { - const auto seconds = line.date.value().timeStamp(); - this->xmpp.send_history_message(chan_name, line.nick.value(), line.body.value(), + const auto seconds = line.col(); + this->xmpp.send_history_message(chan_name, line.col(), line.col(), this->user_jid + "/" + resource, seconds); } #else diff --git a/src/database/column.hpp b/src/database/column.hpp new file mode 100644 index 0000000..e74d426 --- /dev/null +++ b/src/database/column.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +template +struct Column +{ + using real_type = T; + T value; +}; + +struct Id: Column { static constexpr auto name = "id_"; + static constexpr auto options = "PRIMARY KEY AUTOINCREMENT"; }; diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp new file mode 100644 index 0000000..863fad1 --- /dev/null +++ b/src/database/count_query.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +#include + +struct CountQuery: public Query +{ + CountQuery(std::string name): + Query("SELECT count(*) FROM ") + { + this->body += std::move(name); + } + + std::size_t execute(sqlite3* db) + { + auto statement = this->prepare(db); + std::size_t res = 0; + if (sqlite3_step(statement) == SQLITE_ROW) + res = sqlite3_column_int64(statement, 0); + else + { + log_error("Count request didn’t return a result"); + return 0; + } + if (sqlite3_step(statement) != SQLITE_DONE) + log_warning("Count request returned more than one result."); + + log_debug("Returning count: ", res); + return res; + } +}; diff --git a/src/database/database.cpp b/src/database/database.cpp index 9f310da..1738a69 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -2,175 +2,166 @@ #ifdef USE_DATABASE #include -#include -#include #include #include #include -using namespace std::string_literals; +#include -std::unique_ptr Database::db; +sqlite3* Database::db; +Database::MucLogLineTable Database::muc_log_lines("MucLogLine_"); +Database::GlobalOptionsTable Database::global_options("GlobalOptions_"); +Database::IrcServerOptionsTable Database::irc_server_options("IrcServerOptions_"); +Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOptions_"); -void Database::open(const std::string& filename, const std::string& db_type) +void Database::open(const std::string& filename) { - try - { - auto new_db = std::make_unique(db_type, - "database="s + filename); - if (new_db->needsUpgrade()) - new_db->upgrade(); - Database::db = std::move(new_db); - } catch (const litesql::DatabaseError& e) { - log_error("Failed to open database ", filename, ". ", e.what()); - throw; - } + auto res = sqlite3_open_v2(filename.data(), &Database::db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + log_debug("open: ", res); + Database::muc_log_lines.create(Database::db); + Database::global_options.create(Database::db); + Database::irc_server_options.create(Database::db); + Database::irc_channel_options.create(Database::db); } -void Database::set_verbose(const bool val) -{ - Database::db->verbose = val; -} -db::GlobalOptions Database::get_global_options(const std::string& owner) +Database::GlobalOptions Database::get_global_options(const std::string& owner) { - try { - auto options = litesql::select(*Database::db, - db::GlobalOptions::Owner == owner).one(); - return options; - } catch (const litesql::NotFound& e) { - db::GlobalOptions options(*Database::db); - options.owner = owner; - return options; - } + auto request = Database::global_options.select().where() << Owner{} << "=" << owner; + + Database::GlobalOptions options{Database::global_options.get_name()}; + auto result = request.execute(Database::db); + if (result.size() == 1) + options = result.front(); + else + options.col() = owner; + return options; } -db::IrcServerOptions Database::get_irc_server_options(const std::string& owner, - const std::string& server) +Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server) { - try { - auto options = litesql::select(*Database::db, - db::IrcServerOptions::Owner == owner && - db::IrcServerOptions::Server == server).one(); - return options; - } catch (const litesql::NotFound& e) { - db::IrcServerOptions options(*Database::db); - options.owner = owner; - options.server = server; - // options.update(); - return options; - } + auto request = Database::irc_server_options.select().where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server; + + Database::IrcServerOptions options{Database::irc_server_options.get_name()}; + auto result = request.execute(Database::db); + if (result.size() == 1) + options = result.front(); + else + { + options.col() = owner; + options.col() = server; + } + return options; } -db::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, - const std::string& server, - const std::string& channel) +Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel) { - try { - auto options = litesql::select(*Database::db, - db::IrcChannelOptions::Owner == owner && - db::IrcChannelOptions::Server == server && - db::IrcChannelOptions::Channel == channel).one(); - return options; - } catch (const litesql::NotFound& e) { - db::IrcChannelOptions options(*Database::db); - options.owner = owner; - options.server = server; - options.channel = channel; - return options; - } + auto request = Database::irc_channel_options.select().where() << Owner{} << "=" << owner <<\ + " and " << Server{} << "=" << server <<\ + " and " << Channel{} << "=" << channel; + Database::IrcChannelOptions options{Database::irc_channel_options.get_name()}; + auto result = request.execute(Database::db); + if (result.size() == 1) + options = result.front(); + else + { + options.col() = owner; + options.col() = server; + options.col() = channel; + } + return options; } -db::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, - const std::string& server, - const std::string& channel) +Database::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, const std::string& server, + const std::string& channel) { auto coptions = Database::get_irc_channel_options(owner, server, channel); auto soptions = Database::get_irc_server_options(owner, server); - coptions.encodingIn = get_first_non_empty(coptions.encodingIn.value(), - soptions.encodingIn.value()); - coptions.encodingOut = get_first_non_empty(coptions.encodingOut.value(), - soptions.encodingOut.value()); + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col()); + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col()); - coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(), - soptions.maxHistoryLength.value()); + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col()); return coptions; } -db::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, - const std::string& server, - const std::string& channel) +Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, const std::string& server, const std::string& channel) { auto coptions = Database::get_irc_channel_options(owner, server, channel); auto soptions = Database::get_irc_server_options(owner, server); auto goptions = Database::get_global_options(owner); - coptions.encodingIn = get_first_non_empty(coptions.encodingIn.value(), - soptions.encodingIn.value()); - coptions.encodingOut = get_first_non_empty(coptions.encodingOut.value(), - soptions.encodingOut.value()); + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col()); - coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(), - soptions.maxHistoryLength.value(), - goptions.maxHistoryLength.value()); + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col()); + + coptions.col() = get_first_non_empty(coptions.col(), + soptions.col(), + goptions.col()); return coptions; } -std::string Database::store_muc_message(const std::string& owner, const Iid& iid, - Database::time_point date, - const std::string& body, - const std::string& nick) +std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name, + const std::string& server_name, Database::time_point date, + const std::string& body, const std::string& nick) { - db::MucLogLine line(*Database::db); + auto line = Database::muc_log_lines.row(); auto uuid = Database::gen_uuid(); - line.uuid = uuid; - line.owner = owner; - line.ircChanName = iid.get_local(); - line.ircServerName = iid.get_server(); - line.date = std::chrono::duration_cast(date.time_since_epoch()).count(); - line.body = body; - line.nick = nick; + line.col() = uuid; + line.col() = owner; + line.col() = chan_name; + line.col() = server_name; + line.col() = std::chrono::duration_cast(date.time_since_epoch()).count(); + line.col() = body; + line.col() = nick; - line.update(); + line.save(Database::db); return uuid; } -std::vector Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, +std::vector Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, int limit, const std::string& start, const std::string& end) { - auto request = litesql::select(*Database::db, - db::MucLogLine::Owner == owner && - db::MucLogLine::IrcChanName == chan_name && - db::MucLogLine::IrcServerName == server); - request.orderBy(db::MucLogLine::Id, false); + auto request = Database::muc_log_lines.select().where() << Database::Owner{} << "=" << owner << \ + " and " << Database::IrcChanName{} << "=" << chan_name << \ + " and " << Database::IrcServerName{} << "=" << server; - if (limit >= 0) - request.limit(limit); if (!start.empty()) { const auto start_time = utils::parse_datetime(start); if (start_time != -1) - request.where(db::MucLogLine::Date >= start_time); + request << " and " << Database::Date{} << ">=" << start_time; } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) - request.where(db::MucLogLine::Date <= end_time); + request << " and " << Database::Date{} << "<=" << end_time; } - const auto& res = request.all(); - return {res.crbegin(), res.crend()}; + + request.order_by() << Id{} << " DESC "; + + if (limit >= 0) + request.limit() << limit; + + auto result = request.execute(Database::db); + + return {result.crbegin(), result.crend()}; } void Database::close() { - Database::db.reset(nullptr); + sqlite3_close_v2(Database::db); } std::string Database::gen_uuid() @@ -182,5 +173,4 @@ std::string Database::gen_uuid() return uuid_str; } - -#endif +#endif \ No newline at end of file diff --git a/src/database/database.hpp b/src/database/database.hpp index b08a175..ebc4878 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -1,22 +1,101 @@ #pragma once - #include #ifdef USE_DATABASE -#include "biboudb.hpp" - -#include +#include +#include +#include -#include #include +#include + +#include -class Iid; class Database { -public: + public: using time_point = std::chrono::system_clock::time_point; + + struct Uuid: Column { static constexpr auto name = "uuid_"; + static constexpr auto options = ""; }; + + struct Owner: Column { static constexpr auto name = "owner_"; + static constexpr auto options = ""; }; + + struct IrcChanName: Column { static constexpr auto name = "ircChanName_"; + static constexpr auto options = ""; }; + + struct Channel: Column { static constexpr auto name = "channel_"; + static constexpr auto options = ""; }; + + struct IrcServerName: Column { static constexpr auto name = "ircServerName_"; + static constexpr auto options = ""; }; + + struct Server: Column { static constexpr auto name = "server_"; + static constexpr auto options = ""; }; + + struct Date: Column { static constexpr auto name = "date_"; + static constexpr auto options = ""; }; + + struct Body: Column { static constexpr auto name = "body_"; + static constexpr auto options = ""; }; + + struct Nick: Column { static constexpr auto name = "nick_"; + static constexpr auto options = ""; }; + + struct Pass: Column { static constexpr auto name = "pass_"; + static constexpr auto options = ""; }; + + struct Ports: Column { static constexpr auto name = "tlsPorts_"; + static constexpr auto options = ""; }; + + struct TlsPorts: Column { static constexpr auto name = "ports_"; + static constexpr auto options = ""; }; + + struct Username: Column { static constexpr auto name = "username_"; + static constexpr auto options = ""; }; + + struct Realname: Column { static constexpr auto name = "realname_"; + static constexpr auto options = ""; }; + + struct AfterConnectionCommand: Column { static constexpr auto name = "afterConnectionCommand_"; + static constexpr auto options = ""; }; + + struct TrustedFingerprint: Column { static constexpr auto name = "trustedFingerprint_"; + static constexpr auto options = ""; }; + + struct EncodingOut: Column { static constexpr auto name = "encodingOut_"; + static constexpr auto options = ""; }; + + struct EncodingIn: Column { static constexpr auto name = "encodingIn_"; + static constexpr auto options = ""; }; + + struct MaxHistoryLength: Column { static constexpr auto name = "maxHistoryLength_"; + static constexpr auto options = ""; }; + + struct RecordHistory: Column { static constexpr auto name = "recordHistory_"; + static constexpr auto options = ""; }; + + struct VerifyCert: Column { static constexpr auto name = "verifyCert_"; + static constexpr auto options = ""; }; + + struct Persistent: Column { static constexpr auto name = "persistent_"; + static constexpr auto options = ""; }; + + using MucLogLineTable = Table; + using MucLogLine = MucLogLineTable::RowType; + + using GlobalOptionsTable = Table; + using GlobalOptions = GlobalOptionsTable::RowType; + + using IrcServerOptionsTable = Table; + using IrcServerOptions = IrcServerOptionsTable::RowType; + + using IrcChannelOptionsTable = Table; + using IrcChannelOptions = IrcChannelOptionsTable::RowType; + Database() = default; ~Database() = default; @@ -25,42 +104,40 @@ public: Database& operator=(const Database&) = delete; Database& operator=(Database&&) = delete; - static void set_verbose(const bool val); - - template - static size_t count() - { - return litesql::select(*Database::db).count(); - } - /** - * Return the object from the db. Create it beforehand (with all default - * values) if it is not already present. - */ - static db::GlobalOptions get_global_options(const std::string& owner); - static db::IrcServerOptions get_irc_server_options(const std::string& owner, + static GlobalOptions get_global_options(const std::string& owner); + static IrcServerOptions get_irc_server_options(const std::string& owner, const std::string& server); - static db::IrcChannelOptions get_irc_channel_options(const std::string& owner, - const std::string& server, - const std::string& channel); - static db::IrcChannelOptions get_irc_channel_options_with_server_default(const std::string& owner, - const std::string& server, - const std::string& channel); - static db::IrcChannelOptions get_irc_channel_options_with_server_and_global_default(const std::string& owner, - const std::string& server, - const std::string& channel); - static std::vector get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, - int limit=-1, const std::string& start="", const std::string& end=""); - static std::string store_muc_message(const std::string& owner, const Iid& iid, - time_point date, const std::string& body, const std::string& nick); + static IrcChannelOptions get_irc_channel_options(const std::string& owner, + const std::string& server, + const std::string& channel); + static IrcChannelOptions get_irc_channel_options_with_server_default(const std::string& owner, + const std::string& server, + const std::string& channel); + static IrcChannelOptions get_irc_channel_options_with_server_and_global_default(const std::string& owner, + const std::string& server, + const std::string& channel); + static std::vector get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, + int limit=-1, const std::string& start="", const std::string& end=""); + static std::string store_muc_message(const std::string& owner, const std::string& chan_name, const std::string& server_name, + time_point date, const std::string& body, const std::string& nick); static void close(); - static void open(const std::string& filename, const std::string& db_type="sqlite3"); + static void open(const std::string& filename); + template + static std::size_t count(const TableType& table) + { + CountQuery query{table.get_name()}; + return query.execute(Database::db); + } + + static MucLogLineTable muc_log_lines; + static GlobalOptionsTable global_options; + static IrcServerOptionsTable irc_server_options; + static IrcChannelOptionsTable irc_channel_options; + static sqlite3* db; -private: + private: static std::string gen_uuid(); - static std::unique_ptr db; }; #endif /* USE_DATABASE */ - - diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp new file mode 100644 index 0000000..00b77c5 --- /dev/null +++ b/src/database/insert_query.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include + +template +typename std::enable_if, Id>::value, void>::type +actual_bind(sqlite3_stmt* statement, std::vector& params, const std::tuple&) +{ + const auto value = params.front(); + params.erase(params.begin()); + if (sqlite3_bind_text(statement, N + 1, value.data(), static_cast(value.size()), SQLITE_TRANSIENT) != SQLITE_OK) + log_error("Failed to bind ", value, " to param ", N); + else + log_debug("Bound (not id) [", value, "] to ", N); +} + +template +typename std::enable_if, Id>::value, void>::type +actual_bind(sqlite3_stmt* statement, std::vector&, const std::tuple& columns) +{ + auto&& column = std::get(columns); + if (column.value != 0) + { + if (sqlite3_bind_int64(statement, N + 1, column.value) != SQLITE_OK) + log_error("Failed to bind ", column.value, " to id."); + } + else if (sqlite3_bind_null(statement, N + 1) != SQLITE_OK) + log_error("Failed to bind NULL to param ", N); + else + log_debug("Bound NULL to ", N); +} + +struct InsertQuery: public Query +{ + InsertQuery(const std::string& name): + Query("INSERT OR REPLACE INTO ") + { + this->body += name; + } + + template + void execute(const std::tuple& columns, sqlite3* db) + { + auto statement = this->prepare(db); + { + this->bind_param<0>(columns, statement); + if (sqlite3_step(statement) != SQLITE_DONE) + log_error("Failed to execute query: ", sqlite3_errmsg(db)); + } + } + + template + typename std::enable_if::type + bind_param(const std::tuple& columns, sqlite3_stmt* statement) + { + using ColumnType = typename std::remove_reference(columns))>::type; + + actual_bind(statement, this->params, columns); + this->bind_param(columns, statement); + } + + template + typename std::enable_if::type + bind_param(const std::tuple&, sqlite3_stmt*) + {} + + template + void insert_values(const std::tuple& columns) + { + this->body += "VALUES ("; + this->insert_value<0>(columns); + this->body += ")"; + } + + template + typename std::enable_if::type + insert_value(const std::tuple& columns) + { + this->body += "?"; + if (N != sizeof...(T) - 1) + this->body += ","; + this->body += " "; + add_param(*this, std::get(columns)); + this->insert_value(columns); + } + template + typename std::enable_if::type + insert_value(const std::tuple&) + { } + + template + void insert_col_names(const std::tuple& columns) + { + this->body += " ("; + this->insert_col_name<0>(columns); + this->body += ")\n"; + } + + template + typename std::enable_if::type + insert_col_name(const std::tuple& columns) + { + auto value = std::get(columns); + + this->body += value.name; + + if (N < (sizeof...(T) - 1)) + this->body += ", "; + + this->insert_col_name(columns); + } + template + typename std::enable_if::type + insert_col_name(const std::tuple&) + {} + + + private: +}; diff --git a/src/database/query.cpp b/src/database/query.cpp new file mode 100644 index 0000000..fb8c055 --- /dev/null +++ b/src/database/query.cpp @@ -0,0 +1,11 @@ +#include +#include + +template <> +void add_param(Query&, const Id&) +{} + +void actual_add_param(Query& query, const std::string& val) +{ + query.params.push_back(val); +} diff --git a/src/database/query.hpp b/src/database/query.hpp new file mode 100644 index 0000000..92845d0 --- /dev/null +++ b/src/database/query.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include + +#include + +struct Query +{ + std::string body; + std::vector params; + + Query(std::string str): + body(std::move(str)) + {} + + sqlite3_stmt* prepare(sqlite3* db) + { + sqlite3_stmt* statement; + log_debug(this->body); + auto res = sqlite3_prepare(db, this->body.data(), static_cast(this->body.size()) + 1, + &statement, nullptr); + if (res != SQLITE_OK) + { + log_error("Error preparing statement: ", sqlite3_errmsg(db)); + return nullptr; + } + return statement; + } +}; + +template +void add_param(Query& query, const ColumnType& column) +{ + actual_add_param(query, column.value); +} + +template +void actual_add_param(Query& query, const T& val) +{ + query.params.push_back(std::to_string(val)); +} + +void actual_add_param(Query& query, const std::string& val); diff --git a/src/database/row.hpp b/src/database/row.hpp new file mode 100644 index 0000000..ca686c1 --- /dev/null +++ b/src/database/row.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include + +#include + +template +typename std::enable_if, Id>::value, void>::type +update_id(std::tuple&, sqlite3*) +{} + +template +typename std::enable_if, Id>::value, void>::type +update_id(std::tuple& columns, sqlite3* db) +{ + auto&& column = std::get(columns); + log_debug("Found an autoincrement col."); + auto res = sqlite3_last_insert_rowid(db); + log_debug("Value is now: ", res); + column.value = res; +} + +template +typename std::enable_if::type +update_autoincrement_id(std::tuple& columns, sqlite3* db) +{ + using ColumnType = typename std::remove_reference(columns))>::type; + update_id(columns, db); + update_autoincrement_id(columns, db); +} + +template +typename std::enable_if::type +update_autoincrement_id(std::tuple&, sqlite3*) +{} + +template +struct Row +{ + Row(std::string name): + table_name(std::move(name)) + {} + + template + auto& col() + { + auto&& col = std::get(this->columns); + return col.value; + } + + template + const auto& col() const + { + auto&& col = std::get(this->columns); + return col.value; + } + + void save(sqlite3* db) + { + InsertQuery query(this->table_name); + query.insert_col_names(this->columns); + query.insert_values(this->columns); + log_debug(query.body); + + query.execute(this->columns, db); + + update_autoincrement_id<0>(this->columns, db); + } + + std::tuple columns; + std::string table_name; +}; diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp new file mode 100644 index 0000000..b41632e --- /dev/null +++ b/src/database/select_query.hpp @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +using namespace std::string_literals; + +template +typename std::enable_if::value, sqlite3_int64>::type +extract_row_value(sqlite3_stmt* statement, const int i) +{ + return sqlite3_column_int64(statement, i); +} + +template +typename std::enable_if::value, std::string>::type +extract_row_value(sqlite3_stmt* statement, const int i) +{ + const auto size = sqlite3_column_bytes(statement, i); + const unsigned char* str = sqlite3_column_text(statement, i); + std::string result(reinterpret_cast(str), size); + return result; +} + +template +typename std::enable_if::type +extract_row_values(Row& row, sqlite3_stmt* statement) +{ + using ColumnType = typename std::remove_reference(row.columns))>::type; + + auto&& column = std::get(row.columns); + column.value = static_cast(extract_row_value(statement, N)); + + extract_row_values(row, statement); +} + +template +typename std::enable_if::type +extract_row_values(Row&, sqlite3_stmt*) +{} + +template +struct SelectQuery: public Query +{ + SelectQuery(std::string table_name): + Query("SELECT"), + table_name(table_name) + { + this->insert_col_name<0>(); + this->body += " from " + this->table_name; + } + + template + typename std::enable_if::type + insert_col_name() + { + using ColumnsType = std::tuple; + ColumnsType tuple{}; + auto value = std::get(tuple); + + this->body += " "s + value.name; + + if (N < (sizeof...(T) - 1)) + this->body += ", "; + + this->insert_col_name(); + } + template + typename std::enable_if::type + insert_col_name() + {} + + SelectQuery& where() + { + this->body += " WHERE "; + return *this; + }; + + SelectQuery& order_by() + { + this->body += " ORDER BY "; + return *this; + } + + SelectQuery& limit() + { + this->body += " LIMIT "; + return *this; + } + + auto execute(sqlite3* db) + { + auto statement = this->prepare(db); + int i = 1; + for (const std::string& param: this->params) + { + if (sqlite3_bind_text(statement, i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) + log_debug("Failed to bind ", param, " to param ", i); + else + log_debug("Bound ", param, " to ", i); + + i++; + } + std::vector> rows; + while (sqlite3_step(statement) == SQLITE_ROW) + { + Row row(this->table_name); + extract_row_values(row, statement); + rows.push_back(row); + } + return rows; + } + + const std::string table_name; +}; + +template +typename std::enable_if::value, SelectQuery&>::type +operator<<(SelectQuery& query, const T&) +{ + query.body += T::name; + return query; +} + +template +SelectQuery& operator<<(SelectQuery& query, const char* str) +{ + query.body += str; + return query; +} + +template +SelectQuery& operator<<(SelectQuery& query, const std::string& str) +{ + query.body += "?"; + actual_add_param(query, str); + return query; +} + +template +typename std::enable_if::value, SelectQuery&>::type +operator<<(SelectQuery& query, const Integer& i) +{ + query.body += "?"; + actual_add_param(query, i); + return query; +} diff --git a/src/database/table.hpp b/src/database/table.hpp new file mode 100644 index 0000000..163b97f --- /dev/null +++ b/src/database/table.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +template +class Table +{ + static_assert(sizeof...(T) > 0, "Table cannot be empty"); + using ColumnTypes = std::tuple; + + public: + using RowType = Row; + + Table(std::string name): + name(std::move(name)) + {} + + void create(sqlite3* db) + { + std::string res{"CREATE TABLE IF NOT EXISTS "}; + res += this->name; + res += " (\n"; + this->add_column_create<0>(res); + res += ")"; + + log_debug(res); + + char* error; + const auto result = sqlite3_exec(db, res.data(), nullptr, nullptr, &error); + log_debug("result: ", +result); + if (result != SQLITE_OK) + { + log_error("Error executing query: ", error); + sqlite3_free(error); + } + } + + RowType row() + { + return {this->name}; + } + + SelectQuery select() + { + SelectQuery select(this->name); + return select; + } + + const std::string& get_name() const + { + return this->name; + } + + private: + template + typename std::enable_if::type + add_column_create(std::string& str) + { + using ColumnType = typename std::remove_reference(std::declval()))>::type; + using RealType = typename ColumnType::real_type; + str += ColumnType::name; + str += " "; + str += TypeToSQLType::type; + str += " "s + ColumnType::options; + if (N != sizeof...(T) - 1) + str += ","; + str += "\n"; + + add_column_create(str); + } + + template + typename std::enable_if::type + add_column_create(std::string&) + { } + + const std::string name; +}; diff --git a/src/database/type_to_sql.cpp b/src/database/type_to_sql.cpp new file mode 100644 index 0000000..5de012e --- /dev/null +++ b/src/database/type_to_sql.cpp @@ -0,0 +1,7 @@ +#include + +template <> const std::string TypeToSQLType::type = "INTEGER"; +template <> const std::string TypeToSQLType::type = "INTEGER"; +template <> const std::string TypeToSQLType::type = "INTEGER"; +template <> const std::string TypeToSQLType::type = "INTEGER"; +template <> const std::string TypeToSQLType::type = "TEXT"; diff --git a/src/database/type_to_sql.hpp b/src/database/type_to_sql.hpp new file mode 100644 index 0000000..5ae03ad --- /dev/null +++ b/src/database/type_to_sql.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include + +template +struct TypeToSQLType { static const std::string type; }; diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 1d74098..bacb89e 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -156,11 +156,11 @@ IrcClient::IrcClient(std::shared_ptr& poller, std::string hostname, #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->get_hostname()); - std::vector ports = utils::split(options.ports, ';', false); + std::vector ports = utils::split(options.col(), ';', false); for (auto it = ports.rbegin(); it != ports.rend(); ++it) this->ports_to_try.emplace(*it, false); # ifdef BOTAN_FOUND - ports = utils::split(options.tlsPorts, ';', false); + ports = utils::split(options.col(), ';', false); for (auto it = ports.rbegin(); it != ports.rend(); ++it) this->ports_to_try.emplace(*it, true); # endif // BOTAN_FOUND @@ -204,7 +204,7 @@ void IrcClient::start() # ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->get_hostname()); - this->credential_manager.set_trusted_fingerprint(options.trustedFingerprint); + this->credential_manager.set_trusted_fingerprint(options.col()); # endif #endif this->connect(this->hostname, port, tls); @@ -275,8 +275,8 @@ void IrcClient::on_connected() #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->get_hostname()); - if (!options.pass.value().empty()) - this->send_pass_command(options.pass.value()); + if (!options.col().empty()) + this->send_pass_command(options.col()); #endif this->send_nick_command(this->current_nick); @@ -284,10 +284,10 @@ void IrcClient::on_connected() #ifdef USE_DATABASE if (Config::get("realname_customization", "true") == "true") { - if (!options.username.value().empty()) - this->username = options.username.value(); - if (!options.realname.value().empty()) - this->realname = options.realname.value(); + if (!options.col().empty()) + this->username = options.col(); + if (!options.col().empty()) + this->realname = options.col(); this->send_user_command(username, realname); } else @@ -894,8 +894,8 @@ void IrcClient::on_welcome_message(const IrcMessage& message) #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->get_hostname()); - if (!options.afterConnectionCommand.value().empty()) - this->send_raw(options.afterConnectionCommand.value()); + if (!options.col().empty()) + this->send_raw(options.col()); #endif // Install a repeated events to regularly send a PING TimedEventsManager::instance().add_event(TimedEvent(240s, std::bind(&IrcClient::send_ping_command, this), @@ -1260,7 +1260,7 @@ bool IrcClient::abort_on_invalid_cert() const { #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->hostname); - return options.verifyCert.value(); + return options.col(); #endif return true; } diff --git a/src/main.cpp b/src/main.cpp index 1a9b065..5725584 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,6 @@ #include #include -#ifdef USE_DATABASE -# include -#endif #include @@ -91,7 +88,7 @@ int main(int ac, char** av) #ifdef USE_DATABASE try { open_database(); - } catch (const litesql::DatabaseError&) { + } catch (...) { return 1; } #endif diff --git a/src/utils/reload.cpp b/src/utils/reload.cpp index 348c5b5..fdca9bc 100644 --- a/src/utils/reload.cpp +++ b/src/utils/reload.cpp @@ -26,7 +26,7 @@ void reload_process() #ifdef USE_DATABASE try { open_database(); - } catch (const litesql::DatabaseError&) { + } catch (...) { log_warning("Re-using the previous database."); } #endif diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index ab28cfd..a13dbb8 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -130,7 +130,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman { XmlSubNode value(max_histo_length, "value"); - value.set_inner(std::to_string(options.maxHistoryLength.value())); + value.set_inner(std::to_string(options.col())); } XmlSubNode record_history(x, "field"); @@ -142,7 +142,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman { XmlSubNode value(record_history, "value"); value.set_name("value"); - if (options.recordHistory.value()) + if (options.col()) value.set_inner("true"); else value.set_inner("false"); @@ -164,18 +164,18 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, if (field->get_tag("var") == "max_history_length" && value && !value->get_inner().empty()) - options.maxHistoryLength = value->get_inner(); + options.col() = atoi(value->get_inner().data()); else if (field->get_tag("var") == "record_history" && value && !value->get_inner().empty()) { - options.recordHistory = to_bool(value->get_inner()); + options.col() = to_bool(value->get_inner()); Bridge* bridge = biboumi_component.find_user_bridge(owner.bare()); if (bridge) - bridge->set_record_history(options.recordHistory.value()); + bridge->set_record_history(options.col()); } } - options.update(); + options.save(Database::db); command_node.delete_all_children(); XmlSubNode note(command_node, "note"); @@ -211,8 +211,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com ports["type"] = "text-multi"; ports["label"] = "Ports"; ports["desc"] = "List of ports to try, without TLS. Defaults: 6667."; - auto vals = utils::split(options.ports.value(), ';', false); - for (const auto& val: vals) + for (const auto& val: utils::split(options.col(), ';', false)) { XmlSubNode ports_value(ports, "value"); ports_value.set_inner(val); @@ -224,8 +223,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com tls_ports["type"] = "text-multi"; tls_ports["label"] = "TLS ports"; tls_ports["desc"] = "List of ports to try, with TLS. Defaults: 6697, 6670."; - vals = utils::split(options.tlsPorts.value(), ';', false); - for (const auto& val: vals) + for (const auto& val: utils::split(options.col(), ';', false)) { XmlSubNode tls_ports_value(tls_ports, "value"); tls_ports_value.set_inner(val); @@ -237,7 +235,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com verify_cert["label"] = "Verify certificate"; verify_cert["desc"] = "Whether or not to abort the connection if the server’s TLS certificate is invalid"; XmlSubNode verify_cert_value(verify_cert, "value"); - if (options.verifyCert.value()) + if (options.col()) verify_cert_value.set_inner("true"); else verify_cert_value.set_inner("false"); @@ -246,10 +244,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com fingerprint["var"] = "fingerprint"; fingerprint["type"] = "text-single"; fingerprint["label"] = "SHA-1 fingerprint of the TLS certificate to trust."; - if (!options.trustedFingerprint.value().empty()) + if (!options.col().empty()) { XmlSubNode fingerprint_value(fingerprint, "value"); - fingerprint_value.set_inner(options.trustedFingerprint.value()); + fingerprint_value.set_inner(options.col()); } #endif @@ -258,10 +256,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com pass["type"] = "text-private"; pass["label"] = "Server password"; pass["desc"] = "Will be used in a PASS command when connecting"; - if (!options.pass.value().empty()) + if (!options.col().empty()) { XmlSubNode pass_value(pass, "value"); - pass_value.set_inner(options.pass.value()); + pass_value.set_inner(options.col()); } XmlSubNode after_cnt_cmd(x, "field"); @@ -269,10 +267,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com after_cnt_cmd["type"] = "text-single"; after_cnt_cmd["desc"] = "Custom IRC command sent after the connection is established with the server."; after_cnt_cmd["label"] = "After-connection IRC command"; - if (!options.afterConnectionCommand.value().empty()) + if (!options.col().empty()) { XmlSubNode after_cnt_cmd_value(after_cnt_cmd, "value"); - after_cnt_cmd_value.set_inner(options.afterConnectionCommand.value()); + after_cnt_cmd_value.set_inner(options.col()); } if (Config::get("realname_customization", "true") == "true") @@ -281,20 +279,20 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com username["var"] = "username"; username["type"] = "text-single"; username["label"] = "Username"; - if (!options.username.value().empty()) + if (!options.col().empty()) { XmlSubNode username_value(username, "value"); - username_value.set_inner(options.username.value()); + username_value.set_inner(options.col()); } XmlSubNode realname(x, "field"); realname["var"] = "realname"; realname["type"] = "text-single"; realname["label"] = "Realname"; - if (!options.realname.value().empty()) + if (!options.col().empty()) { XmlSubNode realname_value(realname, "value"); - realname_value.set_inner(options.realname.value()); + realname_value.set_inner(options.col()); } } @@ -303,10 +301,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com encoding_out["type"] = "text-single"; encoding_out["desc"] = "The encoding used when sending messages to the IRC server."; encoding_out["label"] = "Out encoding"; - if (!options.encodingOut.value().empty()) + if (!options.col().empty()) { XmlSubNode encoding_out_value(encoding_out, "value"); - encoding_out_value.set_inner(options.encodingOut.value()); + encoding_out_value.set_inner(options.col()); } XmlSubNode encoding_in(x, "field"); @@ -314,10 +312,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com encoding_in["type"] = "text-single"; encoding_in["desc"] = "The encoding used to decode message received from the IRC server."; encoding_in["label"] = "In encoding"; - if (!options.encodingIn.value().empty()) + if (!options.col().empty()) { XmlSubNode encoding_in_value(encoding_in, "value"); - encoding_in_value.set_inner(options.encodingIn.value()); + encoding_in_value.set_inner(options.col()); } } @@ -342,7 +340,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com std::string ports; for (const auto& val: values) ports += val->get_inner() + ";"; - options.ports = ports; + options.col() = ports; } #ifdef BOTAN_FOUND @@ -351,31 +349,31 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com std::string ports; for (const auto& val: values) ports += val->get_inner() + ";"; - options.tlsPorts = ports; + options.col() = ports; } else if (field->get_tag("var") == "verify_cert" && value && !value->get_inner().empty()) { auto val = to_bool(value->get_inner()); - options.verifyCert = val; + options.col() = val; } else if (field->get_tag("var") == "fingerprint" && value && !value->get_inner().empty()) { - options.trustedFingerprint = value->get_inner(); + options.col() = value->get_inner(); } #endif // BOTAN_FOUND else if (field->get_tag("var") == "pass" && value && !value->get_inner().empty()) - options.pass = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "after_connect_command" && value && !value->get_inner().empty()) - options.afterConnectionCommand = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "username" && value && !value->get_inner().empty()) @@ -383,24 +381,24 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com auto username = value->get_inner(); // The username must not contain spaces std::replace(username.begin(), username.end(), ' ', '_'); - options.username = username; + options.col() = username; } else if (field->get_tag("var") == "realname" && value && !value->get_inner().empty()) - options.realname = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "encoding_out" && value && !value->get_inner().empty()) - options.encodingOut = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "encoding_in" && value && !value->get_inner().empty()) - options.encodingIn = value->get_inner(); + options.col() = value->get_inner(); } - options.update(); + options.save(Database::db); command_node.delete_all_children(); XmlSubNode note(command_node, "note"); @@ -441,10 +439,10 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, encoding_out["type"] = "text-single"; encoding_out["desc"] = "The encoding used when sending messages to the IRC server. Defaults to the server's “out encoding” if unset for the channel"; encoding_out["label"] = "Out encoding"; - if (!options.encodingOut.value().empty()) + if (!options.col().empty()) { XmlSubNode encoding_out_value(encoding_out, "value"); - encoding_out_value.set_inner(options.encodingOut.value()); + encoding_out_value.set_inner(options.col()); } XmlSubNode encoding_in(x, "field"); @@ -452,10 +450,10 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, encoding_in["type"] = "text-single"; encoding_in["desc"] = "The encoding used to decode message received from the IRC server. Defaults to the server's “in encoding” if unset for the channel"; encoding_in["label"] = "In encoding"; - if (!options.encodingIn.value().empty()) + if (!options.col().empty()) { XmlSubNode encoding_in_value(encoding_in, "value"); - encoding_in_value.set_inner(options.encodingIn.value()); + encoding_in_value.set_inner(options.col()); } XmlSubNode persistent(x, "field"); @@ -466,7 +464,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, { XmlSubNode value(persistent, "value"); value.set_name("value"); - if (options.persistent.value()) + if (options.col()) value.set_inner("true"); else value.set_inner("false"); @@ -510,18 +508,18 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque if (field->get_tag("var") == "encoding_out" && value && !value->get_inner().empty()) - options.encodingOut = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "encoding_in" && value && !value->get_inner().empty()) - options.encodingIn = value->get_inner(); + options.col() = value->get_inner(); else if (field->get_tag("var") == "persistent" && value) - options.persistent = to_bool(value->get_inner()); + options.col() = to_bool(value->get_inner()); } - options.update(); + options.save(Database::db); } return true; } diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index ca3a887..881e757 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -18,8 +18,6 @@ #include -#include - #ifdef SYSTEMD_FOUND # include #endif @@ -648,9 +646,9 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) limit = 100; } const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); - for (const db::MucLogLine& line: lines) + for (const Database::MucLogLine& line: lines) { - if (!line.nick.value().empty()) + if (!line.col().empty()) this->send_archived_message(line, to.full(), from.full(), query_id); } this->send_iq_result_full_jid(id, from.full(), to.full()); @@ -659,7 +657,7 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) return false; } -void BiboumiComponent::send_archived_message(const db::MucLogLine& log_line, const std::string& from, const std::string& to, +void BiboumiComponent::send_archived_message(const Database::MucLogLine& log_line, const std::string& from, const std::string& to, const std::string& queryid) { Stanza message("message"); @@ -671,22 +669,22 @@ void BiboumiComponent::send_archived_message(const db::MucLogLine& log_line, con result["xmlns"] = MAM_NS; if (!queryid.empty()) result["queryid"] = queryid; - result["id"] = log_line.uuid.value(); + result["id"] = log_line.col(); XmlSubNode forwarded(result, "forwarded"); forwarded["xmlns"] = FORWARD_NS; XmlSubNode delay(forwarded, "delay"); delay["xmlns"] = DELAY_NS; - delay["stamp"] = utils::to_string(log_line.date.value().timeStamp()); + delay["stamp"] = utils::to_string(log_line.col()); XmlSubNode submessage(forwarded, "message"); submessage["xmlns"] = CLIENT_NS; - submessage["from"] = from + "/" + log_line.nick.value(); + submessage["from"] = from + "/" + log_line.col(); submessage["type"] = "groupchat"; XmlSubNode body(submessage, "body"); - body.set_inner(log_line.body.value()); + body.set_inner(log_line.col()); } this->send_stanza(message); } diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp index ac9bde4..87311f9 100644 --- a/src/xmpp/biboumi_component.hpp +++ b/src/xmpp/biboumi_component.hpp @@ -1,6 +1,6 @@ #pragma once - +#include #include #include @@ -96,7 +96,7 @@ public: #ifdef USE_DATABASE bool handle_mam_request(const Stanza& stanza); - void send_archived_message(const db::MucLogLine& log_line, const std::string& from, const std::string& to, + void send_archived_message(const Database::MucLogLine& log_line, const std::string& from, const std::string& to, const std::string& queryid); bool handle_room_configuration_form_request(const std::string& from, const Jid& to, const std::string& id); bool handle_room_configuration_form(const XmlNode& query, const std::string& from, const Jid& to, const std::string& id); diff --git a/tests/database.cpp b/tests/database.cpp index 4e2be14..47dfd7c 100644 --- a/tests/database.cpp +++ b/tests/database.cpp @@ -8,24 +8,22 @@ TEST_CASE("Database") { #ifdef USE_DATABASE Database::open(":memory:"); - Database::set_verbose(false); SECTION("Basic retrieve and update") { auto o = Database::get_irc_server_options("zouzou@example.com", "irc.example.com"); - o.update(); + o.save(Database::db); auto a = Database::get_irc_server_options("zouzou@example.com", "irc.example.com"); auto b = Database::get_irc_server_options("moumou@example.com", "irc.example.com"); // b does not yet exist in the db, the object is created but not yet // inserted - CHECK(1 == Database::count()); + CHECK(1 == Database::count(Database::irc_server_options)); - b.update(); - CHECK(2 == Database::count()); + b.save(Database::db); + CHECK(2 == Database::count(Database::irc_server_options)); - CHECK(b.pass == ""); - CHECK(b.pass.value() == ""); + CHECK(b.col() == ""); } SECTION("channel options") @@ -33,16 +31,16 @@ TEST_CASE("Database") Config::set("db_name", ":memory:"); auto o = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo"); - CHECK(o.encodingIn == ""); - o.encodingIn = "ISO-8859-1"; - o.update(); + CHECK(o.col() == ""); + o.col() = "ISO-8859-1"; + o.save(Database::db); auto b = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo"); - CHECK(o.encodingIn == "ISO-8859-1"); + CHECK(o.col() == "ISO-8859-1"); } SECTION("Channel options with server default") { - const std::string owner{"zouzou@example.com"}; + const std::string owner{"CACA@example.com"}; const std::string server{"irc.example.com"}; const std::string chan1{"#foo"}; @@ -51,43 +49,43 @@ TEST_CASE("Database") GIVEN("An option defined for the channel but not the server") { - c.encodingIn = "channelEncoding"; - c.update(); + c.col() = "channelEncoding"; + c.save(Database::db); WHEN("we fetch that option") { auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); THEN("we get the channel option") - CHECK(r.encodingIn == "channelEncoding"); + CHECK(r.col() == "channelEncoding"); } } GIVEN("An option defined for the server but not the channel") { - s.encodingIn = "serverEncoding"; - s.update(); + s.col() = "serverEncoding"; + s.save(Database::db); WHEN("we fetch that option") { auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); THEN("we get the server option") - CHECK(r.encodingIn == "serverEncoding"); + CHECK(r.col() == "serverEncoding"); } } GIVEN("An option defined for both the server and the channel") { - s.encodingIn = "serverEncoding"; - s.update(); - c.encodingIn = "channelEncoding"; - c.update(); + s.col() = "serverEncoding"; + s.save(Database::db); + c.col() = "channelEncoding"; + c.save(Database::db); WHEN("we fetch that option") { auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); THEN("we get the channel option") - CHECK(r.encodingIn == "channelEncoding"); + CHECK(r.col() == "channelEncoding"); } WHEN("we fetch that option, with no channel specified") { auto r = Database::get_irc_channel_options_with_server_default(owner, server, ""); THEN("we get the server option") - CHECK(r.encodingIn == "serverEncoding"); + CHECK(r.col() == "serverEncoding"); } } } -- cgit v1.2.3 From 369ccb037619871403b14c959bbb359332133810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Jun 2017 11:18:05 +0200 Subject: Add default values for the database columns --- src/bridge/bridge.cpp | 6 ++++-- src/database/column.hpp | 6 +++++- src/database/database.hpp | 18 ++++++++++++------ tests/end_to_end/__main__.py | 2 +- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index fcfcc24..23ecfe9 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -23,12 +23,14 @@ static std::string in_encoding_for(const Bridge& bridge, const Iid& iid) #ifdef USE_DATABASE const auto jid = bridge.get_bare_jid(); auto options = Database::get_irc_channel_options_with_server_default(jid, iid.get_server(), iid.get_local()); - return options.col(); + auto result = options.col(); + if (!result.empty()) + return result; #else (void)bridge; (void)iid; - return {"ISO-8859-1"}; #endif + return {"ISO-8859-1"}; } Bridge::Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr& poller): diff --git a/src/database/column.hpp b/src/database/column.hpp index e74d426..22f4254 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -5,8 +5,12 @@ template struct Column { + Column(T default_value): + value{default_value} {} + Column(): + value{} {} using real_type = T; - T value; + T value{}; }; struct Id: Column { static constexpr auto name = "id_"; diff --git a/src/database/database.hpp b/src/database/database.hpp index ebc4878..a0611c1 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -49,10 +49,12 @@ class Database static constexpr auto options = ""; }; struct Ports: Column { static constexpr auto name = "tlsPorts_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + Ports(): Column("6667") {}}; struct TlsPorts: Column { static constexpr auto name = "ports_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + TlsPorts(): Column("6697;6670") {} }; struct Username: Column { static constexpr auto name = "username_"; static constexpr auto options = ""; }; @@ -73,16 +75,20 @@ class Database static constexpr auto options = ""; }; struct MaxHistoryLength: Column { static constexpr auto name = "maxHistoryLength_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + MaxHistoryLength(): Column(20) {} }; struct RecordHistory: Column { static constexpr auto name = "recordHistory_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + RecordHistory(): Column(true) {}}; struct VerifyCert: Column { static constexpr auto name = "verifyCert_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + VerifyCert(): Column(true) {} }; struct Persistent: Column { static constexpr auto name = "persistent_"; - static constexpr auto options = ""; }; + static constexpr auto options = ""; + Persistent(): Column(false) {} }; using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index f9e04a8..8f108ee 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -2417,7 +2417,7 @@ if __name__ == '__main__': "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='username']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='realname']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", - "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='ISO-8859-1']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", "/iq/commands:command/commands:actions/commands:next", ), after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) -- cgit v1.2.3 From 41c23aab37905a97007d095c3997a0d0a9dfddda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 13 Jun 2017 11:51:10 +0200 Subject: Update the docker images and the gitlab-ci script to use sqlite3 --- .gitlab-ci.yml | 19 ++++------ docker/biboumi-test/alpine/Dockerfile | 50 +++++++++++++++++++++++-- docker/biboumi-test/alpine/Dockerfile.base | 51 -------------------------- docker/biboumi-test/debian/Dockerfile | 56 ++++++++++++++++++++++++++-- docker/biboumi-test/debian/Dockerfile.base | 57 ----------------------------- docker/biboumi-test/fedora/Dockerfile | 58 +++++++++++++++++++++++++++-- docker/biboumi-test/fedora/Dockerfile.base | 59 ------------------------------ docker/biboumi/Dockerfile | 5 +-- 8 files changed, 163 insertions(+), 192 deletions(-) delete mode 100644 docker/biboumi-test/alpine/Dockerfile.base delete mode 100644 docker/biboumi-test/debian/Dockerfile.base delete mode 100644 docker/biboumi-test/fedora/Dockerfile.base diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6604adb..0286ef8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,7 @@ variables: UDNS: "-DWITH_UDNS=1" SYSTEMD: "-DWITH_SYSTEMD=1" LIBIDN: "-DWITH_LIBIDN=1" - LITESQL: "-DWITH_LITESQL=1" + SQLITE3: "-DWITH_SQLITE3=1" # ## Build jobs @@ -33,8 +33,8 @@ variables: tags: - docker script: - - "echo Running cmake with the following parameters: -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${LITESQL}" - - cmake .. -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${LITESQL} + - "echo Running cmake with the following parameters: -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${SQLITE3}" + - cmake .. -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${SQLITE3} - make everything -j$(nproc || echo 1) - make coverage_check -j$(nproc || echo 1) artifacts: @@ -77,19 +77,19 @@ build:2: build:3: variables: - LITESQL: "-DWITHOUT_LITESQL=1" + SQLITE3: "-DWITHOUT_SQLITE3=1" <<: *fedora_build build:4: variables: - LITESQL: "-DWITHOUT_LITESQL=1" + SQLITE3: "-DWITHOUT_SQLITE3=1" BOTAN: "-DWITHOUT_BOTAN=1" LIBIDN: "-DWITHOUT_LIBIDN=1" <<: *fedora_build build:5: variables: - LITESQL: "-DWITHOUT_LITESQL=1" + SQLITE3: "-DWITHOUT_SQLITE3=1" UDNS: "-DWITHOUT_UDNS=1" <<: *fedora_build @@ -163,7 +163,7 @@ test:freebsd: SYSTEMD: "-DWITHOUT_SYSTEMD=1" stage: test script: - - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${LITESQL} + - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${SQLITE3} - make check - make e2e @@ -303,11 +303,6 @@ packaging:archlinux: image: docker.louiz.org/biboumi-test-archlinux:latest before_script: [] script: - - sudo pacman -Syuu --noconfirm - - git clone https://aur.archlinux.org/litesql-git.git - - cd litesql-git - - makepkg -si --noconfirm - - cd .. - git clone https://aur.archlinux.org/biboumi-git.git - cd biboumi-git - makepkg -si --noconfirm diff --git a/docker/biboumi-test/alpine/Dockerfile b/docker/biboumi-test/alpine/Dockerfile index ab288b6..1938a8b 100644 --- a/docker/biboumi-test/alpine/Dockerfile +++ b/docker/biboumi-test/alpine/Dockerfile @@ -1,10 +1,54 @@ # This Dockerfile creates a docker image suitable to run biboumi’s build and # tests. For example, it can be used on with gitlab-ci. -FROM docker.louiz.org/biboumi-test-alpine-base +FROM docker.io/alpine:latest -# Install litesql -RUN git clone git://git.louiz.org/litesql && mkdir /litesql/build && cd /litesql/build && cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && cd /litesql/build && make install && rm -rf /litesql && ldconfig || true +ENV LC_ALL C.UTF-8 + +# Needed to build biboumi +RUN apk add --no-cache g++\ + clang\ + valgrind\ + udns-dev\ + c-ares-dev\ + sqlite-dev\ + libuuid\ + util-linux-dev\ + libgcrypt-dev\ + cmake\ + make\ + expat-dev\ + libidn-dev\ + git\ + py3-lxml\ + libtool\ + py3-pip\ + python2\ + python3-dev\ + automake\ + autoconf\ + flex\ + bison\ + libltdl\ + openssl\ + libressl-dev\ + zlib-dev\ + curl + +# Install botan +RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /botan + +# Install slixmpp, for e2e tests +RUN git clone https://github.com/saghul/aiodns.git && cd aiodns && git checkout 7ee13f9bea25784322~ && python3 setup.py build && python3 setup.py install && git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install + +RUN adduser tester -D -h /home/tester + +# Install charybdis, for e2e tests +RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin && make -j8 && make install && rm -rf /charybdis + +RUN chown -R tester:tester /home/tester/ircd + +RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem WORKDIR /home/tester USER tester diff --git a/docker/biboumi-test/alpine/Dockerfile.base b/docker/biboumi-test/alpine/Dockerfile.base deleted file mode 100644 index dffa1d1..0000000 --- a/docker/biboumi-test/alpine/Dockerfile.base +++ /dev/null @@ -1,51 +0,0 @@ -# This Dockerfile creates a docker image suitable to run biboumi’s build and -# tests. For example, it can be used on with gitlab-ci. - -FROM docker.io/alpine:latest - -ENV LC_ALL C.UTF-8 - -# Needed to build biboumi -RUN apk add --no-cache g++\ - clang\ - valgrind\ - udns-dev\ - c-ares-dev\ - sqlite-dev\ - libuuid\ - util-linux-dev\ - libgcrypt-dev\ - cmake\ - make\ - expat-dev\ - libidn-dev\ - git\ - py3-lxml\ - libtool\ - py3-pip\ - python2\ - python3-dev\ - automake\ - autoconf\ - flex\ - bison\ - libltdl\ - openssl\ - libressl-dev\ - zlib-dev\ - curl - -# Install botan -RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /botan - -# Install slixmpp, for e2e tests -RUN git clone https://github.com/saghul/aiodns.git && cd aiodns && git checkout 7ee13f9bea25784322~ && python3 setup.py build && python3 setup.py install && git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install - -RUN adduser tester -D -h /home/tester - -# Install charybdis, for e2e tests -RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin && make -j8 && make install && rm -rf /charybdis - -RUN chown -R tester:tester /home/tester/ircd - -RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem diff --git a/docker/biboumi-test/debian/Dockerfile b/docker/biboumi-test/debian/Dockerfile index b811ea4..232a585 100644 --- a/docker/biboumi-test/debian/Dockerfile +++ b/docker/biboumi-test/debian/Dockerfile @@ -1,10 +1,60 @@ # This Dockerfile creates a docker image suitable to run biboumi’s build and # tests. For example, it can be used on with gitlab-ci. -FROM docker.louiz.org/biboumi-test-debian-base +FROM docker.io/debian:latest -# Install litesql -RUN git clone git://git.louiz.org/litesql && mkdir /litesql/build && cd /litesql/build && cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && cd /litesql/build && make install && rm -rf /litesql && ldconfig +ENV LC_ALL C.UTF-8 + +RUN apt update + +# Needed to build biboumi +RUN apt install -y g++\ + clang\ + valgrind\ + libudns-dev\ + libc-ares-dev\ + libsqlite3-dev\ + libuuid1\ + libgcrypt20-dev\ + cmake\ + make\ + libexpat1-dev\ + libidn11-dev\ + uuid-dev\ + libsystemd-dev\ + pandoc\ + libasan1\ + libubsan0\ + git\ + python3-lxml\ + lcov\ + libtool\ + python3-pip\ + python3-dev\ + automake\ + autoconf\ + flex\ + bison\ + libltdl-dev\ + openssl\ + zlib1g-dev\ + libssl-dev\ + curl + +# Install botan +RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /botan + +# Install slixmpp, for e2e tests +RUN git clone https://github.com/saghul/aiodns.git && cd aiodns && git checkout 7ee13f9bea25784322~ && python3 setup.py build && python3 setup.py install && git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install + +RUN useradd tester -m + +# Install charybdis, for e2e tests +RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin && make -j8 && make install && rm -rf /charybdis + +RUN chown -R tester:tester /home/tester/ircd + +RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem WORKDIR /home/tester USER tester diff --git a/docker/biboumi-test/debian/Dockerfile.base b/docker/biboumi-test/debian/Dockerfile.base deleted file mode 100644 index f5d061b..0000000 --- a/docker/biboumi-test/debian/Dockerfile.base +++ /dev/null @@ -1,57 +0,0 @@ -# This Dockerfile creates a docker image suitable to run biboumi’s build and -# tests. For example, it can be used on with gitlab-ci. - -FROM docker.io/debian:latest - -ENV LC_ALL C.UTF-8 - -RUN apt update - -# Needed to build biboumi -RUN apt install -y g++\ - clang\ - valgrind\ - libudns-dev\ - libc-ares-dev\ - libsqlite3-dev\ - libuuid1\ - libgcrypt20-dev\ - cmake\ - make\ - libexpat1-dev\ - libidn11-dev\ - uuid-dev\ - libsystemd-dev\ - pandoc\ - libasan1\ - libubsan0\ - git\ - python3-lxml\ - lcov\ - libtool\ - python3-pip\ - python3-dev\ - automake\ - autoconf\ - flex\ - bison\ - libltdl-dev\ - openssl\ - zlib1g-dev\ - libssl-dev\ - curl - -# Install botan -RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /botan - -# Install slixmpp, for e2e tests -RUN git clone https://github.com/saghul/aiodns.git && cd aiodns && git checkout 7ee13f9bea25784322~ && python3 setup.py build && python3 setup.py install && git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install - -RUN useradd tester -m - -# Install charybdis, for e2e tests -RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin && make -j8 && make install && rm -rf /charybdis - -RUN chown -R tester:tester /home/tester/ircd - -RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem diff --git a/docker/biboumi-test/fedora/Dockerfile b/docker/biboumi-test/fedora/Dockerfile index 45dbe76..384fd51 100644 --- a/docker/biboumi-test/fedora/Dockerfile +++ b/docker/biboumi-test/fedora/Dockerfile @@ -1,10 +1,62 @@ # This Dockerfile creates a docker image suitable to run biboumi’s build and # tests. For example, it can be used on with gitlab-ci. -FROM docker.louiz.org/biboumi-test-fedora-base +FROM docker.io/fedora:latest -# Install litesql -RUN git clone git://git.louiz.org/litesql && mkdir /litesql/build && cd /litesql/build && cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && cd /litesql/build && make install && ldconfig && rm -rf /litesql +ENV LC_ALL C.UTF-8 + +RUN dnf --refresh install -y\ + gcc-c++\ + clang\ + valgrind\ + udns-devel\ + c-ares-devel\ + sqlite-devel\ + libuuid-devel\ + libgcrypt-devel\ + cmake\ + make\ + expat-devel\ + libidn-devel\ + uuid-devel\ + systemd-devel\ + pandoc\ + libasan\ + libubsan\ + git\ + fedora-packager\ + python3-lxml\ + lcov\ + rpmdevtools\ + python3-devel\ + automake\ + autoconf\ + flex\ + flex-devel\ + bison\ + libtool-ltdl-devel\ + libtool\ + openssl-devel\ + which\ + java-1.8.0-openjdk\ + && dnf clean all + +# Install botan +RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && ldconfig && rm -rf /botan + +# Install slixmpp, for e2e tests +RUN git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install + +RUN useradd tester + +# Install charybdis, for e2e tests +RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin --with-included-boost && make -j8 && make install && rm -rf /charybdis + +RUN chown -R tester:tester /home/tester/ircd + +RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem + +COPY coverity /home/tester/coverity WORKDIR /home/tester USER tester diff --git a/docker/biboumi-test/fedora/Dockerfile.base b/docker/biboumi-test/fedora/Dockerfile.base deleted file mode 100644 index 20984a2..0000000 --- a/docker/biboumi-test/fedora/Dockerfile.base +++ /dev/null @@ -1,59 +0,0 @@ -# This Dockerfile creates a docker image suitable to run biboumi’s build and -# tests. For example, it can be used on with gitlab-ci. - -FROM docker.io/fedora:latest - -ENV LC_ALL C.UTF-8 - -RUN dnf --refresh install -y\ - gcc-c++\ - clang\ - valgrind\ - udns-devel\ - c-ares-devel\ - sqlite-devel\ - libuuid-devel\ - libgcrypt-devel\ - cmake\ - make\ - expat-devel\ - libidn-devel\ - uuid-devel\ - systemd-devel\ - pandoc\ - libasan\ - libubsan\ - git\ - fedora-packager\ - python3-lxml\ - lcov\ - rpmdevtools\ - python3-devel\ - automake\ - autoconf\ - flex\ - flex-devel\ - bison\ - libtool-ltdl-devel\ - libtool\ - openssl-devel\ - which\ - java-1.8.0-openjdk\ - && dnf clean all - -# Install botan -RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && ldconfig && rm -rf /botan - -# Install slixmpp, for e2e tests -RUN git clone git://git.louiz.org/slixmpp && pip3 install pyasn1 && cd slixmpp && python3 setup.py build && python3 setup.py install - -RUN useradd tester - -# Install charybdis, for e2e tests -RUN git clone https://github.com/charybdis-ircd/charybdis.git && cd charybdis && cd /charybdis && git checkout 4f2b9a4 && sed s/113/1113/ -i /charybdis/authd/providers/ident.c && ./autogen.sh && ./configure --prefix=/home/tester/ircd --bindir=/usr/bin --with-included-boost && make -j8 && make install && rm -rf /charybdis - -RUN chown -R tester:tester /home/tester/ircd - -RUN yes "" | openssl req -nodes -x509 -newkey rsa:4096 -keyout /home/tester/ircd/etc/ssl.key -out /home/tester/ircd/etc/ssl.pem - -COPY coverity /home/tester/coverity diff --git a/docker/biboumi/Dockerfile b/docker/biboumi/Dockerfile index d27421b..67f0f41 100644 --- a/docker/biboumi/Dockerfile +++ b/docker/biboumi/Dockerfile @@ -18,15 +18,12 @@ RUN apk add --no-cache\ # Install botan RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /botan -# Install litesql -RUN git clone git://git.louiz.org/litesql && mkdir /litesql/build && cd /litesql/build && cmake .. -DCMAKE_INSTALL_PREFIX=/usr && make -j8 && cd /litesql/build && make install && rm -rf /litesql - # Install biboumi RUN git clone git://git.louiz.org/biboumi && mkdir ./biboumi/build && cd ./biboumi/build &&\ cmake .. -DCMAKE_INSTALL_PREFIX=/usr\ -DCMAKE_BUILD_TYPE=Release\ -DWITH_BOTAN=1\ - -DWITH_LITESQL=1\ + -DWITH_SQLITE3=1\ -DWITH_LIBIDN=1\ && make -j8 && make install && rm -rf /biboumi -- cgit v1.2.3 From 9defd0ccb75b1905c308ed0437e5ccd479e3a7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 00:04:00 +0200 Subject: Add a Statement class to manage the sqlite3_stmt objects and avoid leaks --- src/database/count_query.hpp | 6 +++--- src/database/insert_query.hpp | 17 +++++++++-------- src/database/query.hpp | 6 ++++-- src/database/select_query.hpp | 19 ++++++++++--------- src/database/statement.hpp | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 src/database/statement.hpp diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp index 863fad1..322ad1b 100644 --- a/src/database/count_query.hpp +++ b/src/database/count_query.hpp @@ -19,14 +19,14 @@ struct CountQuery: public Query { auto statement = this->prepare(db); std::size_t res = 0; - if (sqlite3_step(statement) == SQLITE_ROW) - res = sqlite3_column_int64(statement, 0); + if (sqlite3_step(statement.get()) == SQLITE_ROW) + res = sqlite3_column_int64(statement.get(), 0); else { log_error("Count request didn’t return a result"); return 0; } - if (sqlite3_step(statement) != SQLITE_DONE) + if (sqlite3_step(statement.get()) != SQLITE_DONE) log_warning("Count request returned more than one result."); log_debug("Returning count: ", res); diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 00b77c5..1712916 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -13,11 +14,11 @@ template typename std::enable_if, Id>::value, void>::type -actual_bind(sqlite3_stmt* statement, std::vector& params, const std::tuple&) +actual_bind(Statement& statement, std::vector& params, const std::tuple&) { const auto value = params.front(); params.erase(params.begin()); - if (sqlite3_bind_text(statement, N + 1, value.data(), static_cast(value.size()), SQLITE_TRANSIENT) != SQLITE_OK) + if (sqlite3_bind_text(statement.get(), N + 1, value.data(), static_cast(value.size()), SQLITE_TRANSIENT) != SQLITE_OK) log_error("Failed to bind ", value, " to param ", N); else log_debug("Bound (not id) [", value, "] to ", N); @@ -25,15 +26,15 @@ actual_bind(sqlite3_stmt* statement, std::vector& params, const std template typename std::enable_if, Id>::value, void>::type -actual_bind(sqlite3_stmt* statement, std::vector&, const std::tuple& columns) +actual_bind(Statement& statement, std::vector&, const std::tuple& columns) { auto&& column = std::get(columns); if (column.value != 0) { - if (sqlite3_bind_int64(statement, N + 1, column.value) != SQLITE_OK) + if (sqlite3_bind_int64(statement.get(), N + 1, column.value) != SQLITE_OK) log_error("Failed to bind ", column.value, " to id."); } - else if (sqlite3_bind_null(statement, N + 1) != SQLITE_OK) + else if (sqlite3_bind_null(statement.get(), N + 1) != SQLITE_OK) log_error("Failed to bind NULL to param ", N); else log_debug("Bound NULL to ", N); @@ -53,14 +54,14 @@ struct InsertQuery: public Query auto statement = this->prepare(db); { this->bind_param<0>(columns, statement); - if (sqlite3_step(statement) != SQLITE_DONE) + if (sqlite3_step(statement.get()) != SQLITE_DONE) log_error("Failed to execute query: ", sqlite3_errmsg(db)); } } template typename std::enable_if::type - bind_param(const std::tuple& columns, sqlite3_stmt* statement) + bind_param(const std::tuple& columns, Statement& statement) { using ColumnType = typename std::remove_reference(columns))>::type; @@ -70,7 +71,7 @@ struct InsertQuery: public Query template typename std::enable_if::type - bind_param(const std::tuple&, sqlite3_stmt*) + bind_param(const std::tuple&, Statement&) {} template diff --git a/src/database/query.hpp b/src/database/query.hpp index 92845d0..b77a421 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -16,7 +18,7 @@ struct Query body(std::move(str)) {} - sqlite3_stmt* prepare(sqlite3* db) + Statement prepare(sqlite3* db) { sqlite3_stmt* statement; log_debug(this->body); @@ -27,7 +29,7 @@ struct Query log_error("Error preparing statement: ", sqlite3_errmsg(db)); return nullptr; } - return statement; + return {statement}; } }; diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index b41632e..80d1424 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -13,24 +14,24 @@ using namespace std::string_literals; template typename std::enable_if::value, sqlite3_int64>::type -extract_row_value(sqlite3_stmt* statement, const int i) +extract_row_value(Statement& statement, const int i) { - return sqlite3_column_int64(statement, i); + return sqlite3_column_int64(statement.get(), i); } template typename std::enable_if::value, std::string>::type -extract_row_value(sqlite3_stmt* statement, const int i) +extract_row_value(Statement& statement, const int i) { - const auto size = sqlite3_column_bytes(statement, i); - const unsigned char* str = sqlite3_column_text(statement, i); + const auto size = sqlite3_column_bytes(statement.get(), i); + const unsigned char* str = sqlite3_column_text(statement.get(), i); std::string result(reinterpret_cast(str), size); return result; } template typename std::enable_if::type -extract_row_values(Row& row, sqlite3_stmt* statement) +extract_row_values(Row& row, Statement& statement) { using ColumnType = typename std::remove_reference(row.columns))>::type; @@ -42,7 +43,7 @@ extract_row_values(Row& row, sqlite3_stmt* statement) template typename std::enable_if::type -extract_row_values(Row&, sqlite3_stmt*) +extract_row_values(Row&, Statement&) {} template @@ -100,7 +101,7 @@ struct SelectQuery: public Query int i = 1; for (const std::string& param: this->params) { - if (sqlite3_bind_text(statement, i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) + if (sqlite3_bind_text(statement.get(), i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) log_debug("Failed to bind ", param, " to param ", i); else log_debug("Bound ", param, " to ", i); @@ -108,7 +109,7 @@ struct SelectQuery: public Query i++; } std::vector> rows; - while (sqlite3_step(statement) == SQLITE_ROW) + while (sqlite3_step(statement.get()) == SQLITE_ROW) { Row row(this->table_name); extract_row_values(row, statement); diff --git a/src/database/statement.hpp b/src/database/statement.hpp new file mode 100644 index 0000000..87cd70f --- /dev/null +++ b/src/database/statement.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +class Statement +{ + public: + Statement(sqlite3_stmt* stmt): + stmt(stmt) {} + ~Statement() + { + sqlite3_finalize(this->stmt); + } + + Statement(const Statement&) = delete; + Statement& operator=(const Statement&) = delete; + Statement(Statement&& other): + stmt(other.stmt) + { + other.stmt = nullptr; + } + Statement& operator=(Statement&& other) + { + this->stmt = other.stmt; + other.stmt = nullptr; + return *this; + } + sqlite3_stmt* get() + { + return this->stmt; + } + + private: + sqlite3_stmt* stmt; +}; -- cgit v1.2.3 From 8d1d822edf72e2bf426a1512fb2e19ef65e97b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 00:04:19 +0200 Subject: Explicitely close the Database before re-opening it --- src/utils/reload.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/reload.cpp b/src/utils/reload.cpp index fdca9bc..807a9ab 100644 --- a/src/utils/reload.cpp +++ b/src/utils/reload.cpp @@ -11,6 +11,7 @@ void open_database() #ifdef USE_DATABASE const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite")); log_info("Opening database: ", db_filename); + Database::close(); Database::open(db_filename); log_info("database successfully opened."); #endif -- cgit v1.2.3 From 94dd16cbe6af11bfcaf2f62b6dc91998567fdf95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 00:15:22 +0200 Subject: The packaging:archlinux build only makes sense on the master branch --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0286ef8..83e5e86 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -298,6 +298,8 @@ packaging:deb: packaging:archlinux: stage: packaging + only: + - master@louiz/biboumi tags: - docker image: docker.louiz.org/biboumi-test-archlinux:latest -- cgit v1.2.3 From 3d1e85fd488c265cbeaf07b8fc3a2175ccff10ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 00:18:44 +0200 Subject: Update INSTALL.rst and CHANGELOG.rst for the sqlite dependency --- CHANGELOG.rst | 6 ++++++ CONTRIBUTING.rst | 6 +++--- INSTALL.rst | 13 +++++++------ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5709483..9bfecd6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,9 @@ +Version 6.0 +=========== + + - The LiteSQL dependency was removed. Only libsqlite3 is now necessary + to work with the database. + Version 5.0 - 2017-05-24 ======================== diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 74459d1..8df4899 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -52,8 +52,8 @@ There are two test suites for biboumi: uses a specific IRC server (`charybdis`_), and only tests the most complete biboumi configuration (when all dependencies are used). To run it, you need to install various dependencies: refer to fedora’s `Dockerfile.base`_ and - `Dockerfile`_ to see how to install charybdis, slixmpp, botan, litesql, an - ssl certificate, etc. + `Dockerfile`_ to see how to install charybdis, slixmpp, botan, a ssl + certificate, etc. Once all the dependencies are correctly installed, the tests are run with @@ -94,4 +94,4 @@ Please try to follow the existing style: .. _Dockerfile.base: docker/biboumi-test/fedora/Dockerfile.base .. _Dockerfile: docker/biboumi-test/fedora/Dockerfile .. _charybdis: https://github.com/charybdis-ircd/charybdis -.. _the __main__.py file: tests/end_to_end/__main__.py \ No newline at end of file +.. _the __main__.py file: tests/end_to_end/__main__.py diff --git a/INSTALL.rst b/INSTALL.rst index 6cd85d2..5bb0ca8 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -32,6 +32,12 @@ libiconv_ libuuid_ Generate unique IDs +sqlite3_ (option, but highly recommended) + Provides a way to store various options in a (sqlite3) database. Each user + of the gateway can store their own values (for example their prefered port, + or their IRC password). Without this dependency, many interesting features + are missing. + libidn_ (optional, but recommended) Provides the stringprep functionality. Without it, JIDs for IRC users are not provided. @@ -48,11 +54,6 @@ libbotan_ 1.11 or 2.0 (optional) gcrypt_ (mandatory only if botan is absent) Provides the SHA-1 hash function, for the case where Botan is absent. -litesql_ (optional) - Provides a way to store various options in a (sqlite3) database. Each user - of the gateway can store their own values (for example their prefered port, - or their IRC password). - systemd_ (optional) Provides the support for a systemd service of Type=notify. This is useful only if you are packaging biboumi in a distribution with Systemd. @@ -160,7 +161,7 @@ to use biboumi. .. _libidn: http://www.gnu.org/software/libidn/ .. _libbotan: http://botan.randombit.net/ .. _udns: http://www.corpit.ru/mjt/udns.html -.. _litesql: http://git.louiz.org/litesql +.. _sqlite3: https://sqlite.org .. _systemd: https://www.freedesktop.org/wiki/Software/systemd/ .. _biboumi.1.rst: doc/biboumi.1.rst .. _gcrypt: https://www.gnu.org/software/libgcrypt/ -- cgit v1.2.3 From 922610cdbf0469e01fd99655c46cceff57a825d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 00:24:35 +0200 Subject: Run the coverity and freebsd tests in all louiz/biboumi branches --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 83e5e86..2c39731 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -156,7 +156,7 @@ test:alpine: test:freebsd: only: - - master@louiz/biboumi + - branches@louiz/biboumi tags: - freebsd variables: @@ -240,7 +240,7 @@ codecov:build:7: coverity: stage: external only: - - master@louiz/biboumi + - branches@louiz/biboumi tags: - docker image: docker.louiz.org/biboumi-test-fedora:latest -- cgit v1.2.3 From dac19da4c791a6c16cde09e61841fd7f6b6268d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 10:09:03 +0200 Subject: Fix an inversion of tlsPorts_ and ports_ --- src/database/database.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database/database.hpp b/src/database/database.hpp index a0611c1..3a99867 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -48,11 +48,11 @@ class Database struct Pass: Column { static constexpr auto name = "pass_"; static constexpr auto options = ""; }; - struct Ports: Column { static constexpr auto name = "tlsPorts_"; + struct Ports: Column { static constexpr auto name = "ports_"; static constexpr auto options = ""; - Ports(): Column("6667") {}}; + Ports(): Column("6667") {} }; - struct TlsPorts: Column { static constexpr auto name = "ports_"; + struct TlsPorts: Column { static constexpr auto name = "tlsPorts_"; static constexpr auto options = ""; TlsPorts(): Column("6697;6670") {} }; -- cgit v1.2.3 From 2677ac42e8d2e1cf162fec773a9acb453bef8b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 10:31:45 +0200 Subject: Fix compilation (many warnings, and a linkage error) with clang++ --- src/database/count_query.hpp | 4 ++-- src/database/database.hpp | 2 +- src/database/insert_query.hpp | 6 +++--- src/database/row.hpp | 2 +- src/database/select_query.hpp | 7 +++---- src/database/type_to_sql.cpp | 1 + src/database/type_to_sql.hpp | 7 +++++++ 7 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp index 322ad1b..b7bbf51 100644 --- a/src/database/count_query.hpp +++ b/src/database/count_query.hpp @@ -15,10 +15,10 @@ struct CountQuery: public Query this->body += std::move(name); } - std::size_t execute(sqlite3* db) + int64_t execute(sqlite3* db) { auto statement = this->prepare(db); - std::size_t res = 0; + int64_t res = 0; if (sqlite3_step(statement.get()) == SQLITE_ROW) res = sqlite3_column_int64(statement.get(), 0); else diff --git a/src/database/database.hpp b/src/database/database.hpp index 3a99867..1ad62fc 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -131,7 +131,7 @@ class Database static void open(const std::string& filename); template - static std::size_t count(const TableType& table) + static int64_t count(const TableType& table) { CountQuery query{table.get_name()}; return query.execute(Database::db); diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 1712916..4965fc0 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -31,7 +31,7 @@ actual_bind(Statement& statement, std::vector&, const std::tuple(columns); if (column.value != 0) { - if (sqlite3_bind_int64(statement.get(), N + 1, column.value) != SQLITE_OK) + if (sqlite3_bind_int64(statement.get(), N + 1, static_cast(column.value)) != SQLITE_OK) log_error("Failed to bind ", column.value, " to id."); } else if (sqlite3_bind_null(statement.get(), N + 1) != SQLITE_OK) @@ -110,9 +110,9 @@ struct InsertQuery: public Query typename std::enable_if::type insert_col_name(const std::tuple& columns) { - auto value = std::get(columns); + using ColumnType = typename std::remove_reference(columns))>::type; - this->body += value.name; + this->body += ColumnType::name; if (N < (sizeof...(T) - 1)) this->body += ", "; diff --git a/src/database/row.hpp b/src/database/row.hpp index ca686c1..b6887cb 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -20,7 +20,7 @@ update_id(std::tuple& columns, sqlite3* db) log_debug("Found an autoincrement col."); auto res = sqlite3_last_insert_rowid(db); log_debug("Value is now: ", res); - column.value = res; + column.value = static_cast(res); } template diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 80d1424..d0c1d59 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -25,7 +25,7 @@ extract_row_value(Statement& statement, const int i) { const auto size = sqlite3_column_bytes(statement.get(), i); const unsigned char* str = sqlite3_column_text(statement.get(), i); - std::string result(reinterpret_cast(str), size); + std::string result(reinterpret_cast(str), static_cast(size)); return result; } @@ -62,10 +62,9 @@ struct SelectQuery: public Query insert_col_name() { using ColumnsType = std::tuple; - ColumnsType tuple{}; - auto value = std::get(tuple); + using ColumnType = typename std::remove_reference(std::declval()))>::type; - this->body += " "s + value.name; + this->body += " "s + ColumnType::name; if (N < (sizeof...(T) - 1)) this->body += ", "; diff --git a/src/database/type_to_sql.cpp b/src/database/type_to_sql.cpp index 5de012e..0b26185 100644 --- a/src/database/type_to_sql.cpp +++ b/src/database/type_to_sql.cpp @@ -3,5 +3,6 @@ template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "INTEGER"; +template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "TEXT"; diff --git a/src/database/type_to_sql.hpp b/src/database/type_to_sql.hpp index 5ae03ad..1942268 100644 --- a/src/database/type_to_sql.hpp +++ b/src/database/type_to_sql.hpp @@ -4,3 +4,10 @@ template struct TypeToSQLType { static const std::string type; }; + +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; \ No newline at end of file -- cgit v1.2.3 From f671e5d7d021f519c393196058cd50e26d03b7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 11:29:19 +0200 Subject: Re-add the pacman -Syuuuuuuuuuu command in the packaging:archlinux build --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2c39731..7ef2f8e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -305,6 +305,7 @@ packaging:archlinux: image: docker.louiz.org/biboumi-test-archlinux:latest before_script: [] script: + - sudo pacman -Syuu --noconfirm - git clone https://aur.archlinux.org/biboumi-git.git - cd biboumi-git - makepkg -si --noconfirm -- cgit v1.2.3 From 7ba8f1b3250bda7388e6e6e078fb635856531537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 11:34:48 +0200 Subject: Remove c-ares from the alpine image --- docker/biboumi-test/alpine/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/biboumi-test/alpine/Dockerfile b/docker/biboumi-test/alpine/Dockerfile index 1938a8b..f97c58c 100644 --- a/docker/biboumi-test/alpine/Dockerfile +++ b/docker/biboumi-test/alpine/Dockerfile @@ -10,7 +10,6 @@ RUN apk add --no-cache g++\ clang\ valgrind\ udns-dev\ - c-ares-dev\ sqlite-dev\ libuuid\ util-linux-dev\ -- cgit v1.2.3 From 6e5c1a1dc1744753ec1156029a100d200f2de732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 11:47:37 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20forget=20to=20bump=20to=20version=206.0?= =?UTF-8?q?~dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed20870..596d277 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.0) project(biboumi) -set(${PROJECT_NAME}_VERSION_MAJOR 5) +set(${PROJECT_NAME}_VERSION_MAJOR 6) set(${PROJECT_NAME}_VERSION_MINOR 0) -set(${PROJECT_NAME}_VERSION_SUFFIX "") +set(${PROJECT_NAME}_VERSION_SUFFIX "~dev") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING -- cgit v1.2.3 From 49bb28c6231b3c243181e8911d134d6f7960fb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 11:48:24 +0200 Subject: Build the rpm with sqlite3 --- packaging/biboumi.spec.cmake | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packaging/biboumi.spec.cmake b/packaging/biboumi.spec.cmake index 158e289..d28c840 100644 --- a/packaging/biboumi.spec.cmake +++ b/packaging/biboumi.spec.cmake @@ -11,6 +11,7 @@ BuildRequires: libidn-devel BuildRequires: expat-devel BuildRequires: libuuid-devel BuildRequires: systemd-devel +BuildRequires: sqlite-devel BuildRequires: cmake BuildRequires: systemd BuildRequires: pandoc @@ -37,7 +38,8 @@ cmake . -DCMAKE_CXX_FLAGS="%{optflags}" \ -DPOLLER=EPOLL \ -DWITHOUT_BOTAN=1 \ -DWITH_SYSTEMD=1 \ - -DWITH_LIBIDN=1 + -DWITH_LIBIDN=1 \ + -DWITH_SQLITE3=1 make %{?_smp_mflags} @@ -59,6 +61,12 @@ make check %{?_smp_mflags} %changelog +* ${RPM_DATE} Le Coz Florent - ${RPM_VERSION}-1 +- Build latest git revision + +* Wed Jun 14 2017 Le Coz Florent - 6.0-1 + Enable database support by building with sqlite3 + * Wed May 24 2017 Le Coz Florent - 5.0-1 - Update to version 5.0 -- cgit v1.2.3 From 5834dd53f32ff89f5818681a93e416b0a48b6254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 16:21:23 +0200 Subject: Add a 0 default value for template argument N --- src/database/insert_query.hpp | 18 +++++++++--------- src/database/row.hpp | 6 +++--- src/database/select_query.hpp | 6 +++--- src/database/table.hpp | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 4965fc0..9e410ce 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -53,13 +53,13 @@ struct InsertQuery: public Query { auto statement = this->prepare(db); { - this->bind_param<0>(columns, statement); + this->bind_param(columns, statement); if (sqlite3_step(statement.get()) != SQLITE_DONE) log_error("Failed to execute query: ", sqlite3_errmsg(db)); } } - template + template typename std::enable_if::type bind_param(const std::tuple& columns, Statement& statement) { @@ -69,7 +69,7 @@ struct InsertQuery: public Query this->bind_param(columns, statement); } - template + template typename std::enable_if::type bind_param(const std::tuple&, Statement&) {} @@ -78,11 +78,11 @@ struct InsertQuery: public Query void insert_values(const std::tuple& columns) { this->body += "VALUES ("; - this->insert_value<0>(columns); + this->insert_value(columns); this->body += ")"; } - template + template typename std::enable_if::type insert_value(const std::tuple& columns) { @@ -93,7 +93,7 @@ struct InsertQuery: public Query add_param(*this, std::get(columns)); this->insert_value(columns); } - template + template typename std::enable_if::type insert_value(const std::tuple&) { } @@ -102,11 +102,11 @@ struct InsertQuery: public Query void insert_col_names(const std::tuple& columns) { this->body += " ("; - this->insert_col_name<0>(columns); + this->insert_col_name(columns); this->body += ")\n"; } - template + template typename std::enable_if::type insert_col_name(const std::tuple& columns) { @@ -119,7 +119,7 @@ struct InsertQuery: public Query this->insert_col_name(columns); } - template + template typename std::enable_if::type insert_col_name(const std::tuple&) {} diff --git a/src/database/row.hpp b/src/database/row.hpp index b6887cb..e7a58c4 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -23,7 +23,7 @@ update_id(std::tuple& columns, sqlite3* db) column.value = static_cast(res); } -template +template typename std::enable_if::type update_autoincrement_id(std::tuple& columns, sqlite3* db) { @@ -32,7 +32,7 @@ update_autoincrement_id(std::tuple& columns, sqlite3* db) update_autoincrement_id(columns, db); } -template +template typename std::enable_if::type update_autoincrement_id(std::tuple&, sqlite3*) {} @@ -67,7 +67,7 @@ struct Row query.execute(this->columns, db); - update_autoincrement_id<0>(this->columns, db); + update_autoincrement_id(this->columns, db); } std::tuple columns; diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index d0c1d59..93d69ed 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -53,11 +53,11 @@ struct SelectQuery: public Query Query("SELECT"), table_name(table_name) { - this->insert_col_name<0>(); + this->insert_col_name(); this->body += " from " + this->table_name; } - template + template typename std::enable_if::type insert_col_name() { @@ -71,7 +71,7 @@ struct SelectQuery: public Query this->insert_col_name(); } - template + template typename std::enable_if::type insert_col_name() {} diff --git a/src/database/table.hpp b/src/database/table.hpp index 163b97f..dc871c3 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -26,7 +26,7 @@ class Table std::string res{"CREATE TABLE IF NOT EXISTS "}; res += this->name; res += " (\n"; - this->add_column_create<0>(res); + this->add_column_create(res); res += ")"; log_debug(res); @@ -58,7 +58,7 @@ class Table } private: - template + template typename std::enable_if::type add_column_create(std::string& str) { @@ -75,7 +75,7 @@ class Table add_column_create(str); } - template + template typename std::enable_if::type add_column_create(std::string&) { } -- cgit v1.2.3 From 34ed2cc1a97484942ba316ed604b75c8506f355f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 17:23:53 +0200 Subject: Explicitely declare the add_param specialization --- src/database/query.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/database/query.hpp b/src/database/query.hpp index b77a421..42eeda2 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -38,6 +39,8 @@ void add_param(Query& query, const ColumnType& column) { actual_add_param(query, column.value); } +template <> +void add_param(Query& query, const Id& column); template void actual_add_param(Query& query, const T& val) -- cgit v1.2.3 From a77c982f6325408cbcc0afc9876edf28d095b3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 14 Jun 2017 18:46:38 +0200 Subject: Fix cstdint -> cstddef, to fix compilation on freebsd --- src/database/column.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/column.hpp b/src/database/column.hpp index 22f4254..111f9ca 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include template struct Column -- cgit v1.2.3 From 4a963cc480bb5a78e20380131ba886a7a23b0782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Jun 2017 09:49:08 +0200 Subject: At startup, upgrade all database tables by adding missing columns --- src/database/database.cpp | 4 ++++ src/database/table.cpp | 25 +++++++++++++++++++++++++ src/database/table.hpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/database/table.cpp diff --git a/src/database/database.cpp b/src/database/database.cpp index 1738a69..cb41070 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -19,9 +19,13 @@ void Database::open(const std::string& filename) auto res = sqlite3_open_v2(filename.data(), &Database::db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); log_debug("open: ", res); Database::muc_log_lines.create(Database::db); + Database::muc_log_lines.upgrade(Database::db); Database::global_options.create(Database::db); + Database::global_options.upgrade(Database::db); Database::irc_server_options.create(Database::db); + Database::irc_server_options.upgrade(Database::db); Database::irc_channel_options.create(Database::db); + Database::irc_channel_options.upgrade(Database::db); } diff --git a/src/database/table.cpp b/src/database/table.cpp new file mode 100644 index 0000000..5929f33 --- /dev/null +++ b/src/database/table.cpp @@ -0,0 +1,25 @@ +#include + +std::set get_all_columns_from_table(sqlite3* db, const std::string& table_name) +{ + std::set result; + char* errmsg; + std::string query{"PRAGMA table_info("s + table_name + ")"}; + log_debug(query); + int res = sqlite3_exec(db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { + constexpr int name_column = 1; + std::set* result = static_cast*>(param); + log_debug("Table has column ", columns[name_column]); + if (name_column < columns_nb) + result->insert(columns[name_column]); + return 0; + }, &result, &errmsg); + + if (res != SQLITE_OK) + { + log_error("Error executing ", query, ": ", errmsg); + sqlite3_free(errmsg); + } + + return result; +} diff --git a/src/database/table.hpp b/src/database/table.hpp index dc871c3..411ac6a 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -7,6 +7,26 @@ #include #include +#include + +using namespace std::string_literals; + +std::set get_all_columns_from_table(sqlite3* db, const std::string& table_name); + +template +void add_column_to_table(sqlite3* db, const std::string& table_name) +{ + const std::string name = ColumnType::name; + std::string query{"ALTER TABLE "s + table_name + " ADD " + ColumnType::name + " " + TypeToSQLType::type}; + log_debug(query); + char* error; + const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); + if (result != SQLITE_OK) + { + log_error("Error adding column ", name, " to table ", table_name, ": ", error); + sqlite3_free(error); + } +} template class Table @@ -21,6 +41,12 @@ class Table name(std::move(name)) {} + void upgrade(sqlite3* db) + { + const auto existing_columns = get_all_columns_from_table(db, this->name); + add_column_if_not_exists(db, existing_columns); + } + void create(sqlite3* db) { std::string res{"CREATE TABLE IF NOT EXISTS "}; @@ -58,6 +84,23 @@ class Table } private: + + template + typename std::enable_if::type + add_column_if_not_exists(sqlite3* db, const std::set& existing_columns) + { + using ColumnType = typename std::remove_reference(std::declval()))>::type; + if (existing_columns.count(ColumnType::name) != 1) + { + add_column_to_table(db, this->name); + } + add_column_if_not_exists(db, existing_columns); + } + template + typename std::enable_if::type + add_column_if_not_exists(sqlite3*, const std::set&) + {} + template typename std::enable_if::type add_column_create(std::string& str) -- cgit v1.2.3 From 40db183e3753486deaa43e950fff38579c5ced6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Jun 2017 09:52:40 +0200 Subject: Using OptionalBool, add RecordHistoryOptional col into IrcChannelOptions table ref #3269 --- src/database/database.hpp | 7 ++++++- src/database/query.cpp | 10 ++++++++++ src/database/query.hpp | 2 ++ src/database/select_query.hpp | 17 ++++++++++++++++- src/database/type_to_sql.cpp | 1 + src/database/type_to_sql.hpp | 5 ++++- src/utils/optional_bool.hpp | 25 +++++++++++++++++++++++++ tests/database.cpp | 4 ++++ 8 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/utils/optional_bool.hpp diff --git a/src/database/database.hpp b/src/database/database.hpp index 1ad62fc..28b6b1b 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -82,6 +84,9 @@ class Database static constexpr auto options = ""; RecordHistory(): Column(true) {}}; + struct RecordHistoryOptional: Column { static constexpr auto name = "recordHistory_"; + static constexpr auto options = ""; }; + struct VerifyCert: Column { static constexpr auto name = "verifyCert_"; static constexpr auto options = ""; VerifyCert(): Column(true) {} }; @@ -99,7 +104,7 @@ class Database using IrcServerOptionsTable = Table; using IrcServerOptions = IrcServerOptionsTable::RowType; - using IrcChannelOptionsTable = Table; + using IrcChannelOptionsTable = Table; using IrcChannelOptions = IrcChannelOptionsTable::RowType; Database() = default; diff --git a/src/database/query.cpp b/src/database/query.cpp index fb8c055..e6cf072 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -9,3 +9,13 @@ void actual_add_param(Query& query, const std::string& val) { query.params.push_back(val); } + +void actual_add_param(Query& query, const OptionalBool& val) +{ + if (!val.is_set) + query.params.push_back("0"); + else if (val.value) + query.params.push_back("1"); + else + query.params.push_back("-1"); +} \ No newline at end of file diff --git a/src/database/query.hpp b/src/database/query.hpp index 42eeda2..d9638f7 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -49,3 +50,4 @@ void actual_add_param(Query& query, const T& val) } void actual_add_param(Query& query, const std::string& val); +void actual_add_param(Query& query, const OptionalBool& val); diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 93d69ed..837b064 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -20,7 +22,7 @@ extract_row_value(Statement& statement, const int i) } template -typename std::enable_if::value, std::string>::type +typename std::enable_if::value, T>::type extract_row_value(Statement& statement, const int i) { const auto size = sqlite3_column_bytes(statement.get(), i); @@ -29,6 +31,19 @@ extract_row_value(Statement& statement, const int i) return result; } +template +typename std::enable_if::value, T>::type +extract_row_value(Statement& statement, const int i) +{ + const auto integer = sqlite3_column_int(statement.get(), i); + OptionalBool result; + if (integer > 0) + result.set_value(true); + else if (integer < 0) + result.set_value(false); + return result; +} + template typename std::enable_if::type extract_row_values(Row& row, Statement& statement) diff --git a/src/database/type_to_sql.cpp b/src/database/type_to_sql.cpp index 0b26185..bcd9daa 100644 --- a/src/database/type_to_sql.cpp +++ b/src/database/type_to_sql.cpp @@ -6,3 +6,4 @@ template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "INTEGER"; template <> const std::string TypeToSQLType::type = "TEXT"; +template <> const std::string TypeToSQLType::type = "INTEGER"; \ No newline at end of file diff --git a/src/database/type_to_sql.hpp b/src/database/type_to_sql.hpp index 1942268..ba806ab 100644 --- a/src/database/type_to_sql.hpp +++ b/src/database/type_to_sql.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include template @@ -10,4 +12,5 @@ template <> const std::string TypeToSQLType::type; template <> const std::string TypeToSQLType::type; template <> const std::string TypeToSQLType::type; template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; \ No newline at end of file +template <> const std::string TypeToSQLType::type; +template <> const std::string TypeToSQLType::type; \ No newline at end of file diff --git a/src/utils/optional_bool.hpp b/src/utils/optional_bool.hpp new file mode 100644 index 0000000..824e76d --- /dev/null +++ b/src/utils/optional_bool.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +struct OptionalBool +{ + OptionalBool() = default; + + OptionalBool(bool value): + is_set(true), value(value) {} + + void set_value(bool value) + { + this->is_set = true; + this->value = value; + } + + void unset() + { + this->is_set = false; + } + + bool is_set{false}; + bool value{false}; +}; diff --git a/tests/database.cpp b/tests/database.cpp index 47dfd7c..f49220a 100644 --- a/tests/database.cpp +++ b/tests/database.cpp @@ -33,9 +33,13 @@ TEST_CASE("Database") CHECK(o.col() == ""); o.col() = "ISO-8859-1"; + CHECK(o.col().is_set == false); + o.col().set_value(false); o.save(Database::db); auto b = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo"); CHECK(o.col() == "ISO-8859-1"); + CHECK(o.col().is_set == true); + CHECK(o.col().value == false); } SECTION("Channel options with server default") -- cgit v1.2.3 From e75d7ad8ea72044fdfd2317e03f91ba5bea06b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Jun 2017 10:48:54 +0200 Subject: Add a Record History option in the Channel configuration form fix #3269 --- CHANGELOG.rst | 2 ++ doc/biboumi.1.rst | 7 ++++- src/utils/optional_bool.hpp | 10 +++++++ src/xmpp/biboumi_adhoc_commands.cpp | 53 ++++++++++++++++++++++++++++++++++--- src/xmpp/biboumi_adhoc_commands.hpp | 2 +- src/xmpp/biboumi_component.cpp | 2 +- tests/end_to_end/__main__.py | 3 +++ 7 files changed, 72 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9bfecd6..c8cddfe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,8 @@ Version 6.0 - The LiteSQL dependency was removed. Only libsqlite3 is now necessary to work with the database. + - The RecordHistory option can now also be configured for each IRC channel, + individually. Version 5.0 - 2017-05-24 ======================== diff --git a/doc/biboumi.1.rst b/doc/biboumi.1.rst index 5396015..4a2225e 100644 --- a/doc/biboumi.1.rst +++ b/doc/biboumi.1.rst @@ -358,7 +358,7 @@ History ------- Public channel messages are saved into archives, inside the database, unless -the `record_history` option is set to false for that user `Ad-hoc commands`. +the `record_history` option is set to false by that user (see `Ad-hoc commands`). Private messages (messages that are sent directly to a nickname, not a channel) are never stored in the database. When a channel is joined, biboumi sends the `max_history_length` messages found in the database as the MUC @@ -631,6 +631,11 @@ On a channel JID (e.g on the JID #test%chat.freenode.org@biboumi.example.com) the archiving of messages is enabled for this room, the client will receive the messages that where sent in this channel. This option can be used to make biboumi act as an IRC bouncer. + * Record History: whether or not history messages should be saved in + the database, for this specific channel. If the value is “unset” (the + default), then the value configured globally is used. This option is there, + for example, to be able to enable history recording globally while disabling + it for a few specific “private” channels. Raw IRC messages ---------------- diff --git a/src/utils/optional_bool.hpp b/src/utils/optional_bool.hpp index 824e76d..59bbbab 100644 --- a/src/utils/optional_bool.hpp +++ b/src/utils/optional_bool.hpp @@ -20,6 +20,16 @@ struct OptionalBool this->is_set = false; } + std::string to_string() + { + if (this->is_set == false) + return "unset"; + else if (this->value) + return "true"; + else + return "false"; + } + bool is_set{false}; bool value{false}; }; diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index a13dbb8..ad4faf8 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -434,6 +434,26 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, XmlSubNode instructions(x, "instructions"); instructions.set_inner("Edit the form, to configure the settings of the IRC channel "s + iid.get_local()); + XmlSubNode record_history(x, "field"); + record_history["var"] = "record_history"; + record_history["type"] = "list-single"; + record_history["label"] = "Record history for this channel"; + record_history["desc"] = "If unset, the value is the one configured globally"; + + { + // Value selected by default + XmlSubNode value(record_history, "value"); + value.set_inner(options.col().to_string()); + } + // All three possible values + for (const auto& val: {"unset", "true", "false"}) + { + XmlSubNode option(record_history, "option"); + option["label"] = val; + XmlSubNode value(option, "value"); + value.set_inner(val); + } + XmlSubNode encoding_out(x, "field"); encoding_out["var"] = "encoding_out"; encoding_out["type"] = "text-single"; @@ -471,12 +491,12 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, } } -void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node) +void ConfigureIrcChannelStep2(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node) { const Jid owner(session.get_owner_jid()); const Jid target(session.get_target_jid()); - if (handle_irc_channel_configuration_form(command_node, owner, target)) + if (handle_irc_channel_configuration_form(xmpp_component, command_node, owner, target)) { command_node.delete_all_children(); XmlSubNode note(command_node, "note"); @@ -492,7 +512,7 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co } } -bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target) +bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const XmlNode& node, const Jid& requester, const Jid& target) { const XmlNode* x = node.get_child("x", "jabber:x:data"); if (x) @@ -500,7 +520,7 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque if (x->get_tag("type") == "submit") { const Iid iid(target.local, {}); - auto options = Database::get_irc_channel_options(requester.local + "@" + requester.domain, + auto options = Database::get_irc_channel_options(requester.bare(), iid.get_server(), iid.get_local()); for (const XmlNode *field: x->get_children("field", "jabber:x:data")) { @@ -517,6 +537,31 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque else if (field->get_tag("var") == "persistent" && value) options.col() = to_bool(value->get_inner()); + else if (field->get_tag("var") == "record_history" && + value && !value->get_inner().empty()) + { + OptionalBool& database_value = options.col(); + if (value->get_inner() == "true") + database_value.set_value(true); + else if (value->get_inner() == "false") + database_value.set_value(false); + else + database_value.unset(); + auto& biboumi_component = dynamic_cast(xmpp_component); + Bridge* bridge = biboumi_component.find_user_bridge(requester.bare()); + if (bridge) + { + if (database_value.is_set) + bridge->set_record_history(database_value.value); + else + { // It is unset, we need to fetch the Global option, to + // know if it’s enabled or not + auto g_options = Database::get_global_options(requester.bare()); + bridge->set_record_history(g_options.col()); + } + } + } + } options.save(Database::db); diff --git a/src/xmpp/biboumi_adhoc_commands.hpp b/src/xmpp/biboumi_adhoc_commands.hpp index 7d29cc2..cb6acb9 100644 --- a/src/xmpp/biboumi_adhoc_commands.hpp +++ b/src/xmpp/biboumi_adhoc_commands.hpp @@ -20,7 +20,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, const Jid& target); void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node); -bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target); +bool handle_irc_channel_configuration_form(XmppComponent&, const XmlNode& node, const Jid& requester, const Jid& target); void DisconnectUserFromServerStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); void DisconnectUserFromServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node); diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 881e757..32f3968 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -719,7 +719,7 @@ bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, cons return false; Jid requester(from); - if (!handle_irc_channel_configuration_form(query, requester, to)) + if (!handle_irc_channel_configuration_form(*this, query, requester, to)) return false; Stanza iq("iq"); diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index 8f108ee..f7fa2ff 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -2467,6 +2467,7 @@ if __name__ == '__main__': partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']", ), after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) ), @@ -2476,6 +2477,7 @@ if __name__ == '__main__': "" "UTF-8" "latin-1" + "true" ""), partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"), @@ -2484,6 +2486,7 @@ if __name__ == '__main__': "/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']", "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']", + "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']", "/iq/commands:command/commands:actions/commands:next", ), after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) -- cgit v1.2.3 From 5b6b9bd3720a0e0189efce67518793332713f4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 21 Jun 2017 01:17:12 +0200 Subject: =?UTF-8?q?Disable=20charybdis=E2=80=99=20connection=20throttling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/end_to_end/ircd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/end_to_end/ircd.conf b/tests/end_to_end/ircd.conf index cdb06d5..ccfbd90 100644 --- a/tests/end_to_end/ircd.conf +++ b/tests/end_to_end/ircd.conf @@ -498,7 +498,7 @@ general { reject_after_count = 3; reject_duration = 5 minutes; throttle_duration = 60; - throttle_count = 4; + throttle_count = 8888; max_ratelimit_tokens = 30; away_interval = 30; certfp_method = sha1; -- cgit v1.2.3 From 651caabb2d2f4e2d6bac3fcde59a69f9cad7eb75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 16 Jun 2017 10:54:09 +0200 Subject: Remove a useless debug print in the e2e tests --- tests/end_to_end/__main__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py index f7fa2ff..19dc270 100644 --- a/tests/end_to_end/__main__.py +++ b/tests/end_to_end/__main__.py @@ -278,7 +278,6 @@ def expect_stanza(xpaths, xmpp, biboumi, optional=False, after=None): def save_current_timestamp_plus_delta(key, delta, message, xmpp): now_plus_delta = datetime.datetime.utcnow() + delta xmpp.saved_values[key] = now_plus_delta.strftime("%FT%T.967Z") - print(xmpp.saved_values[key]) def sleep_for(duration, xmpp, biboumi): time.sleep(duration) -- cgit v1.2.3 From 4d55a120d8fa5564a439102ab97b43da589bf4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 21 Jun 2017 17:36:53 +0200 Subject: Re-implement correctly the handling of failure to open the database MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we can’t open it at startup, we exit. If we can’t open it on reload, we keep the previously-opened database. This way, we’re assured to always have a valid and open database available. --- src/database/database.cpp | 14 ++++++++++++-- src/utils/reload.cpp | 1 - 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/database/database.cpp b/src/database/database.cpp index cb41070..ba69d39 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -16,8 +16,18 @@ Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOption void Database::open(const std::string& filename) { - auto res = sqlite3_open_v2(filename.data(), &Database::db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); - log_debug("open: ", res); + // Try to open the specified database. + // Close and replace the previous database pointer if it succeeded. If it did + // not, just leave things untouched + sqlite3* new_db; + auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + if (res != SQLITE_OK) + { + log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(Database::db)); + throw std::runtime_error(""); + } + Database::close(); + Database::db = new_db; Database::muc_log_lines.create(Database::db); Database::muc_log_lines.upgrade(Database::db); Database::global_options.create(Database::db); diff --git a/src/utils/reload.cpp b/src/utils/reload.cpp index 807a9ab..fdca9bc 100644 --- a/src/utils/reload.cpp +++ b/src/utils/reload.cpp @@ -11,7 +11,6 @@ void open_database() #ifdef USE_DATABASE const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite")); log_info("Opening database: ", db_filename); - Database::close(); Database::open(db_filename); log_info("database successfully opened."); #endif -- cgit v1.2.3 From 8a7166eef9d46ec15850cadb674edb2873cebecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 22 Jun 2017 16:14:00 +0200 Subject: Set the database pointer to nullptr after a close --- src/database/database.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database/database.cpp b/src/database/database.cpp index ba69d39..246cdbc 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -176,6 +176,7 @@ std::vector Database::get_muc_logs(const std::string& owne void Database::close() { sqlite3_close_v2(Database::db); + Database::db = nullptr; } std::string Database::gen_uuid() -- cgit v1.2.3 From b71ca15a0f9114db38eec23b49d1489a2ff1d0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 23 Jun 2017 00:11:07 +0200 Subject: Move a few functions from select_query to query --- src/database/database.cpp | 20 ++++++++++++-------- src/database/query.cpp | 15 ++++++++++++++- src/database/query.hpp | 43 ++++++++++++++++++++++++++++++++++++++++--- src/database/select_query.hpp | 41 ----------------------------------------- 4 files changed, 66 insertions(+), 53 deletions(-) diff --git a/src/database/database.cpp b/src/database/database.cpp index 246cdbc..92f7682 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -41,7 +41,8 @@ void Database::open(const std::string& filename) Database::GlobalOptions Database::get_global_options(const std::string& owner) { - auto request = Database::global_options.select().where() << Owner{} << "=" << owner; + auto request = Database::global_options.select(); + request.where() << Owner{} << "=" << owner; Database::GlobalOptions options{Database::global_options.get_name()}; auto result = request.execute(Database::db); @@ -54,7 +55,8 @@ Database::GlobalOptions Database::get_global_options(const std::string& owner) Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server) { - auto request = Database::irc_server_options.select().where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server; + auto request = Database::irc_server_options.select(); + request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server; Database::IrcServerOptions options{Database::irc_server_options.get_name()}; auto result = request.execute(Database::db); @@ -70,9 +72,10 @@ Database::IrcServerOptions Database::get_irc_server_options(const std::string& o Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel) { - auto request = Database::irc_channel_options.select().where() << Owner{} << "=" << owner <<\ - " and " << Server{} << "=" << server <<\ - " and " << Channel{} << "=" << channel; + auto request = Database::irc_channel_options.select(); + request.where() << Owner{} << "=" << owner <<\ + " and " << Server{} << "=" << server <<\ + " and " << Channel{} << "=" << channel; Database::IrcChannelOptions options{Database::irc_channel_options.get_name()}; auto result = request.execute(Database::db); if (result.size() == 1) @@ -146,9 +149,10 @@ std::string Database::store_muc_message(const std::string& owner, const std::str std::vector Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, int limit, const std::string& start, const std::string& end) { - auto request = Database::muc_log_lines.select().where() << Database::Owner{} << "=" << owner << \ - " and " << Database::IrcChanName{} << "=" << chan_name << \ - " and " << Database::IrcServerName{} << "=" << server; + auto request = Database::muc_log_lines.select(); + request.where() << Database::Owner{} << "=" << owner << \ + " and " << Database::IrcChanName{} << "=" << chan_name << \ + " and " << Database::IrcServerName{} << "=" << server; if (!start.empty()) { diff --git a/src/database/query.cpp b/src/database/query.cpp index e6cf072..ba63a92 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -18,4 +18,17 @@ void actual_add_param(Query& query, const OptionalBool& val) query.params.push_back("1"); else query.params.push_back("-1"); -} \ No newline at end of file +} + +Query& operator<<(Query& query, const char* str) +{ + query.body += str; + return query; +} + +Query& operator<<(Query& query, const std::string& str) +{ + query.body += "?"; + actual_add_param(query, str); + return query; +} diff --git a/src/database/query.hpp b/src/database/query.hpp index d9638f7..f103fe9 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -22,16 +22,34 @@ struct Query Statement prepare(sqlite3* db) { - sqlite3_stmt* statement; + sqlite3_stmt* stmt; log_debug(this->body); auto res = sqlite3_prepare(db, this->body.data(), static_cast(this->body.size()) + 1, - &statement, nullptr); + &stmt, nullptr); if (res != SQLITE_OK) { log_error("Error preparing statement: ", sqlite3_errmsg(db)); return nullptr; } - return {statement}; + Statement statement(stmt); + int i = 1; + for (const std::string& param: this->params) + { + if (sqlite3_bind_text(statement.get(), i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) + log_debug("Failed to bind ", param, " to param ", i); + else + log_debug("Bound ", param, " to ", i); + i++; + } + + return statement; + } + + void execute(sqlite3* db) + { + auto statement = this->prepare(db); + while (sqlite3_step(statement.get()) != SQLITE_DONE) + ; } }; @@ -51,3 +69,22 @@ void actual_add_param(Query& query, const T& val) void actual_add_param(Query& query, const std::string& val); void actual_add_param(Query& query, const OptionalBool& val); + +template +typename std::enable_if::value, Query&>::type +operator<<(Query& query, const T&) +{ + query.body += T::name; + return query; +} + +Query& operator<<(Query& query, const char* str); +Query& operator<<(Query& query, const std::string& str); +template +typename std::enable_if::value, Query&>::type +operator<<(Query& query, const Integer& i) +{ + query.body += "?"; + actual_add_param(query, i); + return query; +} diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 837b064..f4d71af 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -112,16 +112,6 @@ struct SelectQuery: public Query auto execute(sqlite3* db) { auto statement = this->prepare(db); - int i = 1; - for (const std::string& param: this->params) - { - if (sqlite3_bind_text(statement.get(), i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) - log_debug("Failed to bind ", param, " to param ", i); - else - log_debug("Bound ", param, " to ", i); - - i++; - } std::vector> rows; while (sqlite3_step(statement.get()) == SQLITE_ROW) { @@ -135,34 +125,3 @@ struct SelectQuery: public Query const std::string table_name; }; -template -typename std::enable_if::value, SelectQuery&>::type -operator<<(SelectQuery& query, const T&) -{ - query.body += T::name; - return query; -} - -template -SelectQuery& operator<<(SelectQuery& query, const char* str) -{ - query.body += str; - return query; -} - -template -SelectQuery& operator<<(SelectQuery& query, const std::string& str) -{ - query.body += "?"; - actual_add_param(query, str); - return query; -} - -template -typename std::enable_if::value, SelectQuery&>::type -operator<<(SelectQuery& query, const Integer& i) -{ - query.body += "?"; - actual_add_param(query, i); - return query; -} -- cgit v1.2.3