Merge branch 'orm' into 'master'
Pure c++ sqlite3 ORM Closes #3271 See merge request !11
This commit is contained in:
+11
-14
@@ -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
|
||||
|
||||
@@ -156,14 +156,14 @@ test:alpine:
|
||||
|
||||
test:freebsd:
|
||||
only:
|
||||
- master@louiz/biboumi
|
||||
- branches@louiz/biboumi
|
||||
tags:
|
||||
- freebsd
|
||||
variables:
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -298,16 +298,13 @@ packaging:deb:
|
||||
|
||||
packaging:archlinux:
|
||||
stage: packaging
|
||||
only:
|
||||
- master@louiz/biboumi
|
||||
tags:
|
||||
- docker
|
||||
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
|
||||
|
||||
@@ -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
|
||||
========================
|
||||
|
||||
|
||||
+11
-20
@@ -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)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
|
||||
+7
-6
@@ -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/
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -1,70 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE database SYSTEM "litesql.dtd">
|
||||
|
||||
<database name="BibouDB" namespace="db">
|
||||
<object name="GlobalOptions">
|
||||
<field name="owner" type="string" length="3071"/>
|
||||
|
||||
<field name="maxHistoryLength" type="integer" default="20"/>
|
||||
<field name="recordHistory" type="boolean" default="true"/>
|
||||
<index unique="true">
|
||||
<indexfield name="owner"/>
|
||||
</index>
|
||||
</object>
|
||||
|
||||
<object name="IrcServerOptions">
|
||||
<field name="owner" type="string" length="3071"/>
|
||||
<field name="server" type="string" length="3071"/>
|
||||
|
||||
<field name="pass" type="string" length="1024" default=""/>
|
||||
<field name="afterConnectionCommand" type="string" length="510" default=""/>
|
||||
<field name="tlsPorts" type="string" length="4096" default="6697;6670" />
|
||||
<field name="ports" type="string" length="4096" default="6667" />
|
||||
<field name="username" type="string" length="1024" default=""/>
|
||||
<field name="realname" type="string" length="1024" default=""/>
|
||||
<field name="verifyCert" type="boolean" default="true"/>
|
||||
<field name="trustedFingerprint" type="string"/>
|
||||
|
||||
<field name="encodingOut" type="string" default="ISO-8859-1"/>
|
||||
<field name="encodingIn" type="string" default="ISO-8859-1"/>
|
||||
|
||||
<field name="maxHistoryLength" type="integer" default="20"/>
|
||||
<index unique="true">
|
||||
<indexfield name="owner"/>
|
||||
<indexfield name="server"/>
|
||||
</index>
|
||||
</object>
|
||||
|
||||
<object name="IrcChannelOptions">
|
||||
<field name="owner" type="string" length="3071"/>
|
||||
<field name="server" type="string" length="3071"/>
|
||||
<field name="channel" type="string" length="1024"/>
|
||||
|
||||
<field name="encodingOut" type="string"/>
|
||||
<field name="encodingIn" type="string"/>
|
||||
|
||||
<field name="maxHistoryLength" type="integer" default="20"/>
|
||||
|
||||
<field name="persistent" type="boolean" default="false"/>
|
||||
|
||||
<index unique="true">
|
||||
<indexfield name="owner"/>
|
||||
<indexfield name="server"/>
|
||||
<indexfield name="channel"/>
|
||||
</index>
|
||||
</object>
|
||||
|
||||
<object name="MucLogLine">
|
||||
<field name="uuid" type="string" length="36" />
|
||||
<!-- The bare JID of the user for which we stored the line. It's
|
||||
the JID associated with the Bridge -->
|
||||
<field name="owner" type="string" length="4096" />
|
||||
<!-- The room IID -->
|
||||
<field name="ircChanName" type="string" length="4096" />
|
||||
<field name="ircServerName" type="string" length="4096" />
|
||||
|
||||
<field name="date" type="datetime" />
|
||||
<field name="body" type="string" length="65536"/>
|
||||
<field name="nick" type="string" length="4096" />
|
||||
</object>
|
||||
</database>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
+11
-9
@@ -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.encodingIn.value();
|
||||
auto result = options.col<Database::EncodingIn>();
|
||||
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>& poller):
|
||||
@@ -38,7 +40,7 @@ Bridge::Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr<Pol
|
||||
{
|
||||
#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<Database::RecordHistory>());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -258,7 +260,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 +438,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<Database::Persistent>();
|
||||
#endif
|
||||
if (channel->joined && !channel->parting && !persistent)
|
||||
{
|
||||
@@ -837,7 +839,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 +996,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<Database::MaxHistoryLength>());
|
||||
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<Database::Date>();
|
||||
this->xmpp.send_history_message(chan_name, line.col<Database::Nick>(), line.col<Database::Body>(),
|
||||
this->user_jid + "/" + resource, seconds);
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
template <typename T>
|
||||
struct Column
|
||||
{
|
||||
Column(T default_value):
|
||||
value{default_value} {}
|
||||
Column():
|
||||
value{} {}
|
||||
using real_type = T;
|
||||
T value{};
|
||||
};
|
||||
|
||||
struct Id: Column<std::size_t> { static constexpr auto name = "id_";
|
||||
static constexpr auto options = "PRIMARY KEY AUTOINCREMENT"; };
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/query.hpp>
|
||||
#include <database/table.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
struct CountQuery: public Query
|
||||
{
|
||||
CountQuery(std::string name):
|
||||
Query("SELECT count(*) FROM ")
|
||||
{
|
||||
this->body += std::move(name);
|
||||
}
|
||||
|
||||
int64_t execute(sqlite3* db)
|
||||
{
|
||||
auto statement = this->prepare(db);
|
||||
int64_t res = 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.get()) != SQLITE_DONE)
|
||||
log_warning("Count request returned more than one result.");
|
||||
|
||||
log_debug("Returning count: ", res);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
+94
-104
@@ -2,175 +2,166 @@
|
||||
#ifdef USE_DATABASE
|
||||
|
||||
#include <database/database.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
#include <irc/iid.hpp>
|
||||
#include <uuid/uuid.h>
|
||||
#include <utils/get_first_non_empty.hpp>
|
||||
#include <utils/time.hpp>
|
||||
|
||||
using namespace std::string_literals;
|
||||
#include <sqlite3.h>
|
||||
|
||||
std::unique_ptr<db::BibouDB> 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::BibouDB>(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<db::GlobalOptions>(*Database::db,
|
||||
db::GlobalOptions::Owner == owner).one();
|
||||
return options;
|
||||
} catch (const litesql::NotFound& e) {
|
||||
db::GlobalOptions options(*Database::db);
|
||||
options.owner = owner;
|
||||
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>() = 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<db::IrcServerOptions>(*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;
|
||||
|
||||
db::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner,
|
||||
const std::string& server,
|
||||
const std::string& channel)
|
||||
Database::IrcServerOptions options{Database::irc_server_options.get_name()};
|
||||
auto result = request.execute(Database::db);
|
||||
if (result.size() == 1)
|
||||
options = result.front();
|
||||
else
|
||||
{
|
||||
try {
|
||||
auto options = litesql::select<db::IrcChannelOptions>(*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;
|
||||
options.col<Owner>() = owner;
|
||||
options.col<Server>() = server;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
db::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner,
|
||||
const std::string& server,
|
||||
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;
|
||||
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>() = owner;
|
||||
options.col<Server>() = server;
|
||||
options.col<Channel>() = channel;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
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<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
|
||||
soptions.col<EncodingIn>());
|
||||
coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
|
||||
soptions.col<EncodingOut>());
|
||||
|
||||
coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(),
|
||||
soptions.maxHistoryLength.value());
|
||||
coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
|
||||
soptions.col<MaxHistoryLength>());
|
||||
|
||||
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<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
|
||||
soptions.col<EncodingIn>());
|
||||
|
||||
coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(),
|
||||
soptions.maxHistoryLength.value(),
|
||||
goptions.maxHistoryLength.value());
|
||||
coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
|
||||
soptions.col<EncodingOut>());
|
||||
|
||||
coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
|
||||
soptions.col<MaxHistoryLength>(),
|
||||
goptions.col<MaxHistoryLength>());
|
||||
|
||||
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<std::chrono::seconds>(date.time_since_epoch()).count();
|
||||
line.body = body;
|
||||
line.nick = nick;
|
||||
line.col<Uuid>() = uuid;
|
||||
line.col<Owner>() = owner;
|
||||
line.col<IrcChanName>() = chan_name;
|
||||
line.col<IrcServerName>() = server_name;
|
||||
line.col<Date>() = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count();
|
||||
line.col<Body>() = body;
|
||||
line.col<Nick>() = nick;
|
||||
|
||||
line.update();
|
||||
line.save(Database::db);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::vector<db::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
|
||||
std::vector<Database::MucLogLine> 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<db::MucLogLine>(*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
|
||||
+111
-28
@@ -1,22 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <biboumi.h>
|
||||
#ifdef USE_DATABASE
|
||||
|
||||
#include "biboudb.hpp"
|
||||
#include <database/table.hpp>
|
||||
#include <database/column.hpp>
|
||||
#include <database/count_query.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <litesql.hpp>
|
||||
#include <chrono>
|
||||
|
||||
class Iid;
|
||||
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
using time_point = std::chrono::system_clock::time_point;
|
||||
|
||||
struct Uuid: Column<std::string> { static constexpr auto name = "uuid_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Owner: Column<std::string> { static constexpr auto name = "owner_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct IrcChanName: Column<std::string> { static constexpr auto name = "ircChanName_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Channel: Column<std::string> { static constexpr auto name = "channel_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct IrcServerName: Column<std::string> { static constexpr auto name = "ircServerName_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Server: Column<std::string> { static constexpr auto name = "server_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Date: Column<time_point::rep> { static constexpr auto name = "date_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Body: Column<std::string> { static constexpr auto name = "body_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Nick: Column<std::string> { static constexpr auto name = "nick_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Pass: Column<std::string> { static constexpr auto name = "pass_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Ports: Column<std::string> { static constexpr auto name = "ports_";
|
||||
static constexpr auto options = "";
|
||||
Ports(): Column<std::string>("6667") {} };
|
||||
|
||||
struct TlsPorts: Column<std::string> { static constexpr auto name = "tlsPorts_";
|
||||
static constexpr auto options = "";
|
||||
TlsPorts(): Column<std::string>("6697;6670") {} };
|
||||
|
||||
struct Username: Column<std::string> { static constexpr auto name = "username_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct Realname: Column<std::string> { static constexpr auto name = "realname_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct AfterConnectionCommand: Column<std::string> { static constexpr auto name = "afterConnectionCommand_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct TrustedFingerprint: Column<std::string> { static constexpr auto name = "trustedFingerprint_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct EncodingOut: Column<std::string> { static constexpr auto name = "encodingOut_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct EncodingIn: Column<std::string> { static constexpr auto name = "encodingIn_";
|
||||
static constexpr auto options = ""; };
|
||||
|
||||
struct MaxHistoryLength: Column<int> { static constexpr auto name = "maxHistoryLength_";
|
||||
static constexpr auto options = "";
|
||||
MaxHistoryLength(): Column<int>(20) {} };
|
||||
|
||||
struct RecordHistory: Column<bool> { static constexpr auto name = "recordHistory_";
|
||||
static constexpr auto options = "";
|
||||
RecordHistory(): Column<bool>(true) {}};
|
||||
|
||||
struct VerifyCert: Column<bool> { static constexpr auto name = "verifyCert_";
|
||||
static constexpr auto options = "";
|
||||
VerifyCert(): Column<bool>(true) {} };
|
||||
|
||||
struct Persistent: Column<bool> { static constexpr auto name = "persistent_";
|
||||
static constexpr auto options = "";
|
||||
Persistent(): Column<bool>(false) {} };
|
||||
|
||||
using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>;
|
||||
using MucLogLine = MucLogLineTable::RowType;
|
||||
|
||||
using GlobalOptionsTable = Table<Id, Owner, MaxHistoryLength, RecordHistory>;
|
||||
using GlobalOptions = GlobalOptionsTable::RowType;
|
||||
|
||||
using IrcServerOptionsTable = Table<Id, Owner, Server, Pass, AfterConnectionCommand, TlsPorts, Ports, Username, Realname, VerifyCert, TrustedFingerprint, EncodingOut, EncodingIn, MaxHistoryLength>;
|
||||
using IrcServerOptions = IrcServerOptionsTable::RowType;
|
||||
|
||||
using IrcChannelOptionsTable = Table<Id, Owner, Server, Channel, EncodingOut, EncodingIn, MaxHistoryLength, Persistent>;
|
||||
using IrcChannelOptions = IrcChannelOptionsTable::RowType;
|
||||
|
||||
Database() = default;
|
||||
~Database() = default;
|
||||
|
||||
@@ -25,42 +110,40 @@ public:
|
||||
Database& operator=(const Database&) = delete;
|
||||
Database& operator=(Database&&) = delete;
|
||||
|
||||
static void set_verbose(const bool val);
|
||||
|
||||
template<typename PersistentType>
|
||||
static size_t count()
|
||||
{
|
||||
return litesql::select<PersistentType>(*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,
|
||||
static 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,
|
||||
static 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,
|
||||
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<db::MucLogLine> get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
|
||||
static std::vector<MucLogLine> 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,
|
||||
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 <typename TableType>
|
||||
static int64_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:
|
||||
static std::string gen_uuid();
|
||||
static std::unique_ptr<db::BibouDB> db;
|
||||
};
|
||||
#endif /* USE_DATABASE */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/statement.hpp>
|
||||
#include <database/column.hpp>
|
||||
#include <database/query.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
template <int N, typename ColumnType, typename... T>
|
||||
typename std::enable_if<!std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
|
||||
actual_bind(Statement& statement, std::vector<std::string>& params, const std::tuple<T...>&)
|
||||
{
|
||||
const auto value = params.front();
|
||||
params.erase(params.begin());
|
||||
if (sqlite3_bind_text(statement.get(), N + 1, value.data(), static_cast<int>(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 <int N, typename ColumnType, typename... T>
|
||||
typename std::enable_if<std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
|
||||
actual_bind(Statement& statement, std::vector<std::string>&, const std::tuple<T...>& columns)
|
||||
{
|
||||
auto&& column = std::get<Id>(columns);
|
||||
if (column.value != 0)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement.get(), N + 1, static_cast<sqlite3_int64>(column.value)) != SQLITE_OK)
|
||||
log_error("Failed to bind ", column.value, " to id.");
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
struct InsertQuery: public Query
|
||||
{
|
||||
InsertQuery(const std::string& name):
|
||||
Query("INSERT OR REPLACE INTO ")
|
||||
{
|
||||
this->body += name;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void execute(const std::tuple<T...>& columns, sqlite3* db)
|
||||
{
|
||||
auto statement = this->prepare(db);
|
||||
{
|
||||
this->bind_param<0>(columns, statement);
|
||||
if (sqlite3_step(statement.get()) != SQLITE_DONE)
|
||||
log_error("Failed to execute query: ", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
bind_param(const std::tuple<T...>& columns, Statement& statement)
|
||||
{
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(columns))>::type;
|
||||
|
||||
actual_bind<N, ColumnType>(statement, this->params, columns);
|
||||
this->bind_param<N+1>(columns, statement);
|
||||
}
|
||||
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
bind_param(const std::tuple<T...>&, Statement&)
|
||||
{}
|
||||
|
||||
template <typename... T>
|
||||
void insert_values(const std::tuple<T...>& columns)
|
||||
{
|
||||
this->body += "VALUES (";
|
||||
this->insert_value<0>(columns);
|
||||
this->body += ")";
|
||||
}
|
||||
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
insert_value(const std::tuple<T...>& columns)
|
||||
{
|
||||
this->body += "?";
|
||||
if (N != sizeof...(T) - 1)
|
||||
this->body += ",";
|
||||
this->body += " ";
|
||||
add_param(*this, std::get<N>(columns));
|
||||
this->insert_value<N+1>(columns);
|
||||
}
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
insert_value(const std::tuple<T...>&)
|
||||
{ }
|
||||
|
||||
template <typename... T>
|
||||
void insert_col_names(const std::tuple<T...>& columns)
|
||||
{
|
||||
this->body += " (";
|
||||
this->insert_col_name<0>(columns);
|
||||
this->body += ")\n";
|
||||
}
|
||||
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
insert_col_name(const std::tuple<T...>& columns)
|
||||
{
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(columns))>::type;
|
||||
|
||||
this->body += ColumnType::name;
|
||||
|
||||
if (N < (sizeof...(T) - 1))
|
||||
this->body += ", ";
|
||||
|
||||
this->insert_col_name<N+1>(columns);
|
||||
}
|
||||
template <int N, typename... T>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
insert_col_name(const std::tuple<T...>&)
|
||||
{}
|
||||
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <database/query.hpp>
|
||||
#include <database/column.hpp>
|
||||
|
||||
template <>
|
||||
void add_param<Id>(Query&, const Id&)
|
||||
{}
|
||||
|
||||
void actual_add_param(Query& query, const std::string& val)
|
||||
{
|
||||
query.params.push_back(val);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/statement.hpp>
|
||||
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
struct Query
|
||||
{
|
||||
std::string body;
|
||||
std::vector<std::string> params;
|
||||
|
||||
Query(std::string str):
|
||||
body(std::move(str))
|
||||
{}
|
||||
|
||||
Statement prepare(sqlite3* db)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
log_debug(this->body);
|
||||
auto res = sqlite3_prepare(db, this->body.data(), static_cast<int>(this->body.size()) + 1,
|
||||
&statement, nullptr);
|
||||
if (res != SQLITE_OK)
|
||||
{
|
||||
log_error("Error preparing statement: ", sqlite3_errmsg(db));
|
||||
return nullptr;
|
||||
}
|
||||
return {statement};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ColumnType>
|
||||
void add_param(Query& query, const ColumnType& column)
|
||||
{
|
||||
actual_add_param(query, column.value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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);
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/insert_query.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
template <typename ColumnType, typename... T>
|
||||
typename std::enable_if<!std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
|
||||
update_id(std::tuple<T...>&, sqlite3*)
|
||||
{}
|
||||
|
||||
template <typename ColumnType, typename... T>
|
||||
typename std::enable_if<std::is_same<std::decay_t<ColumnType>, Id>::value, void>::type
|
||||
update_id(std::tuple<T...>& columns, sqlite3* db)
|
||||
{
|
||||
auto&& column = std::get<ColumnType>(columns);
|
||||
log_debug("Found an autoincrement col.");
|
||||
auto res = sqlite3_last_insert_rowid(db);
|
||||
log_debug("Value is now: ", res);
|
||||
column.value = static_cast<Id::real_type>(res);
|
||||
}
|
||||
|
||||
template <std::size_t N, typename... T>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
update_autoincrement_id(std::tuple<T...>& columns, sqlite3* db)
|
||||
{
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(columns))>::type;
|
||||
update_id<ColumnType>(columns, db);
|
||||
update_autoincrement_id<N+1>(columns, db);
|
||||
}
|
||||
|
||||
template <std::size_t N, typename... T>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
update_autoincrement_id(std::tuple<T...>&, sqlite3*)
|
||||
{}
|
||||
|
||||
template <typename... T>
|
||||
struct Row
|
||||
{
|
||||
Row(std::string name):
|
||||
table_name(std::move(name))
|
||||
{}
|
||||
|
||||
template <typename Type>
|
||||
auto& col()
|
||||
{
|
||||
auto&& col = std::get<Type>(this->columns);
|
||||
return col.value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const auto& col() const
|
||||
{
|
||||
auto&& col = std::get<Type>(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<T...> columns;
|
||||
std::string table_name;
|
||||
};
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/statement.hpp>
|
||||
#include <database/query.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
#include <database/row.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, sqlite3_int64>::type
|
||||
extract_row_value(Statement& statement, const int i)
|
||||
{
|
||||
return sqlite3_column_int64(statement.get(), i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_same<std::string, T>::value, std::string>::type
|
||||
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<const char*>(str), static_cast<std::size_t>(size));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <std::size_t N=0, typename... T>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
extract_row_values(Row<T...>& row, Statement& statement)
|
||||
{
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(row.columns))>::type;
|
||||
|
||||
auto&& column = std::get<N>(row.columns);
|
||||
column.value = static_cast<decltype(column.value)>(extract_row_value<typename ColumnType::real_type>(statement, N));
|
||||
|
||||
extract_row_values<N+1>(row, statement);
|
||||
}
|
||||
|
||||
template <std::size_t N=0, typename... T>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
extract_row_values(Row<T...>&, Statement&)
|
||||
{}
|
||||
|
||||
template <typename... T>
|
||||
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 <std::size_t N>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
insert_col_name()
|
||||
{
|
||||
using ColumnsType = std::tuple<T...>;
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnsType>()))>::type;
|
||||
|
||||
this->body += " "s + ColumnType::name;
|
||||
|
||||
if (N < (sizeof...(T) - 1))
|
||||
this->body += ", ";
|
||||
|
||||
this->insert_col_name<N+1>();
|
||||
}
|
||||
template <std::size_t N>
|
||||
typename std::enable_if<N == sizeof...(T), void>::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.get(), i, param.data(), static_cast<int>(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<Row<T...>> rows;
|
||||
while (sqlite3_step(statement.get()) == SQLITE_ROW)
|
||||
{
|
||||
Row<T...> row(this->table_name);
|
||||
extract_row_values(row, statement);
|
||||
rows.push_back(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
const std::string table_name;
|
||||
};
|
||||
|
||||
template <typename T, typename... ColumnTypes>
|
||||
typename std::enable_if<!std::is_integral<T>::value, SelectQuery<ColumnTypes...>&>::type
|
||||
operator<<(SelectQuery<ColumnTypes...>& query, const T&)
|
||||
{
|
||||
query.body += T::name;
|
||||
return query;
|
||||
}
|
||||
|
||||
template <typename... ColumnTypes>
|
||||
SelectQuery<ColumnTypes...>& operator<<(SelectQuery<ColumnTypes...>& query, const char* str)
|
||||
{
|
||||
query.body += str;
|
||||
return query;
|
||||
}
|
||||
|
||||
template <typename... ColumnTypes>
|
||||
SelectQuery<ColumnTypes...>& operator<<(SelectQuery<ColumnTypes...>& query, const std::string& str)
|
||||
{
|
||||
query.body += "?";
|
||||
actual_add_param(query, str);
|
||||
return query;
|
||||
}
|
||||
|
||||
template <typename Integer, typename... ColumnTypes>
|
||||
typename std::enable_if<std::is_integral<Integer>::value, SelectQuery<ColumnTypes...>&>::type
|
||||
operator<<(SelectQuery<ColumnTypes...>& query, const Integer& i)
|
||||
{
|
||||
query.body += "?";
|
||||
actual_add_param(query, i);
|
||||
return query;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <database/select_query.hpp>
|
||||
#include <database/type_to_sql.hpp>
|
||||
#include <logger/logger.hpp>
|
||||
#include <database/row.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
template <typename... T>
|
||||
class Table
|
||||
{
|
||||
static_assert(sizeof...(T) > 0, "Table cannot be empty");
|
||||
using ColumnTypes = std::tuple<T...>;
|
||||
|
||||
public:
|
||||
using RowType = Row<T...>;
|
||||
|
||||
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<T...> select()
|
||||
{
|
||||
SelectQuery<T...> select(this->name);
|
||||
return select;
|
||||
}
|
||||
|
||||
const std::string& get_name() const
|
||||
{
|
||||
return this->name;
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::size_t N>
|
||||
typename std::enable_if<N < sizeof...(T), void>::type
|
||||
add_column_create(std::string& str)
|
||||
{
|
||||
using ColumnType = typename std::remove_reference<decltype(std::get<N>(std::declval<ColumnTypes>()))>::type;
|
||||
using RealType = typename ColumnType::real_type;
|
||||
str += ColumnType::name;
|
||||
str += " ";
|
||||
str += TypeToSQLType<RealType>::type;
|
||||
str += " "s + ColumnType::options;
|
||||
if (N != sizeof...(T) - 1)
|
||||
str += ",";
|
||||
str += "\n";
|
||||
|
||||
add_column_create<N+1>(str);
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
typename std::enable_if<N == sizeof...(T), void>::type
|
||||
add_column_create(std::string&)
|
||||
{ }
|
||||
|
||||
const std::string name;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <database/type_to_sql.hpp>
|
||||
|
||||
template <> const std::string TypeToSQLType<int>::type = "INTEGER";
|
||||
template <> const std::string TypeToSQLType<std::size_t>::type = "INTEGER";
|
||||
template <> const std::string TypeToSQLType<long>::type = "INTEGER";
|
||||
template <> const std::string TypeToSQLType<long long>::type = "INTEGER";
|
||||
template <> const std::string TypeToSQLType<bool>::type = "INTEGER";
|
||||
template <> const std::string TypeToSQLType<std::string>::type = "TEXT";
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
template <typename T>
|
||||
struct TypeToSQLType { static const std::string type; };
|
||||
|
||||
template <> const std::string TypeToSQLType<int>::type;
|
||||
template <> const std::string TypeToSQLType<std::size_t>::type;
|
||||
template <> const std::string TypeToSQLType<long>::type;
|
||||
template <> const std::string TypeToSQLType<long long>::type;
|
||||
template <> const std::string TypeToSQLType<bool>::type;
|
||||
template <> const std::string TypeToSQLType<std::string>::type;
|
||||
+12
-12
@@ -156,11 +156,11 @@ IrcClient::IrcClient(std::shared_ptr<Poller>& poller, std::string hostname,
|
||||
#ifdef USE_DATABASE
|
||||
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
|
||||
this->get_hostname());
|
||||
std::vector<std::string> ports = utils::split(options.ports, ';', false);
|
||||
std::vector<std::string> ports = utils::split(options.col<Database::Ports>(), ';', 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<Database::TlsPorts>(), ';', 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<Database::TrustedFingerprint>());
|
||||
# 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<Database::Pass>().empty())
|
||||
this->send_pass_command(options.col<Database::Pass>());
|
||||
#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<Database::Username>().empty())
|
||||
this->username = options.col<Database::Username>();
|
||||
if (!options.col<Database::Realname>().empty())
|
||||
this->realname = options.col<Database::Realname>();
|
||||
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<Database::AfterConnectionCommand>().empty())
|
||||
this->send_raw(options.col<Database::AfterConnectionCommand>());
|
||||
#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<Database::VerifyCert>();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-4
@@ -12,9 +12,6 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#ifdef USE_DATABASE
|
||||
# include <litesql.hpp>
|
||||
#endif
|
||||
|
||||
#include <identd/identd_server.hpp>
|
||||
|
||||
@@ -91,7 +88,7 @@ int main(int ac, char** av)
|
||||
#ifdef USE_DATABASE
|
||||
try {
|
||||
open_database();
|
||||
} catch (const litesql::DatabaseError&) {
|
||||
} catch (...) {
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -26,7 +27,7 @@ void reload_process()
|
||||
#ifdef USE_DATABASE
|
||||
try {
|
||||
open_database();
|
||||
} catch (const litesql::DatabaseError&) {
|
||||
} catch (...) {
|
||||
log_warning("Re-using the previous database.");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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<Database::MaxHistoryLength>()));
|
||||
}
|
||||
|
||||
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<Database::RecordHistory>())
|
||||
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<Database::MaxHistoryLength>() = 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<Database::RecordHistory>() = 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<Database::RecordHistory>());
|
||||
}
|
||||
}
|
||||
|
||||
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<Database::Ports>(), ';', 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<Database::TlsPorts>(), ';', 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<Database::VerifyCert>())
|
||||
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<Database::TrustedFingerprint>().empty())
|
||||
{
|
||||
XmlSubNode fingerprint_value(fingerprint, "value");
|
||||
fingerprint_value.set_inner(options.trustedFingerprint.value());
|
||||
fingerprint_value.set_inner(options.col<Database::TrustedFingerprint>());
|
||||
}
|
||||
#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<Database::Pass>().empty())
|
||||
{
|
||||
XmlSubNode pass_value(pass, "value");
|
||||
pass_value.set_inner(options.pass.value());
|
||||
pass_value.set_inner(options.col<Database::Pass>());
|
||||
}
|
||||
|
||||
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<Database::AfterConnectionCommand>().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<Database::AfterConnectionCommand>());
|
||||
}
|
||||
|
||||
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<Database::Username>().empty())
|
||||
{
|
||||
XmlSubNode username_value(username, "value");
|
||||
username_value.set_inner(options.username.value());
|
||||
username_value.set_inner(options.col<Database::Username>());
|
||||
}
|
||||
|
||||
XmlSubNode realname(x, "field");
|
||||
realname["var"] = "realname";
|
||||
realname["type"] = "text-single";
|
||||
realname["label"] = "Realname";
|
||||
if (!options.realname.value().empty())
|
||||
if (!options.col<Database::Realname>().empty())
|
||||
{
|
||||
XmlSubNode realname_value(realname, "value");
|
||||
realname_value.set_inner(options.realname.value());
|
||||
realname_value.set_inner(options.col<Database::Realname>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Database::EncodingOut>().empty())
|
||||
{
|
||||
XmlSubNode encoding_out_value(encoding_out, "value");
|
||||
encoding_out_value.set_inner(options.encodingOut.value());
|
||||
encoding_out_value.set_inner(options.col<Database::EncodingOut>());
|
||||
}
|
||||
|
||||
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<Database::EncodingIn>().empty())
|
||||
{
|
||||
XmlSubNode encoding_in_value(encoding_in, "value");
|
||||
encoding_in_value.set_inner(options.encodingIn.value());
|
||||
encoding_in_value.set_inner(options.col<Database::EncodingIn>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Database::Ports>() = 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<Database::TlsPorts>() = 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<Database::VerifyCert>() = val;
|
||||
}
|
||||
|
||||
else if (field->get_tag("var") == "fingerprint" && value &&
|
||||
!value->get_inner().empty())
|
||||
{
|
||||
options.trustedFingerprint = value->get_inner();
|
||||
options.col<Database::TrustedFingerprint>() = 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<Database::Pass>() = value->get_inner();
|
||||
|
||||
else if (field->get_tag("var") == "after_connect_command" &&
|
||||
value && !value->get_inner().empty())
|
||||
options.afterConnectionCommand = value->get_inner();
|
||||
options.col<Database::AfterConnectionCommand>() = 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<Database::Username>() = username;
|
||||
}
|
||||
|
||||
else if (field->get_tag("var") == "realname" &&
|
||||
value && !value->get_inner().empty())
|
||||
options.realname = value->get_inner();
|
||||
options.col<Database::Realname>() = value->get_inner();
|
||||
|
||||
else if (field->get_tag("var") == "encoding_out" &&
|
||||
value && !value->get_inner().empty())
|
||||
options.encodingOut = value->get_inner();
|
||||
options.col<Database::EncodingOut>() = value->get_inner();
|
||||
|
||||
else if (field->get_tag("var") == "encoding_in" &&
|
||||
value && !value->get_inner().empty())
|
||||
options.encodingIn = value->get_inner();
|
||||
options.col<Database::EncodingIn>() = 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<Database::EncodingOut>().empty())
|
||||
{
|
||||
XmlSubNode encoding_out_value(encoding_out, "value");
|
||||
encoding_out_value.set_inner(options.encodingOut.value());
|
||||
encoding_out_value.set_inner(options.col<Database::EncodingOut>());
|
||||
}
|
||||
|
||||
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<Database::EncodingIn>().empty())
|
||||
{
|
||||
XmlSubNode encoding_in_value(encoding_in, "value");
|
||||
encoding_in_value.set_inner(options.encodingIn.value());
|
||||
encoding_in_value.set_inner(options.col<Database::EncodingIn>());
|
||||
}
|
||||
|
||||
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<Database::Persistent>())
|
||||
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<Database::EncodingOut>() = value->get_inner();
|
||||
|
||||
else if (field->get_tag("var") == "encoding_in" &&
|
||||
value && !value->get_inner().empty())
|
||||
options.encodingIn = value->get_inner();
|
||||
options.col<Database::EncodingIn>() = value->get_inner();
|
||||
|
||||
else if (field->get_tag("var") == "persistent" &&
|
||||
value)
|
||||
options.persistent = to_bool(value->get_inner());
|
||||
options.col<Database::Persistent>() = to_bool(value->get_inner());
|
||||
}
|
||||
|
||||
options.update();
|
||||
options.save(Database::db);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include <biboumi.h>
|
||||
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#ifdef SYSTEMD_FOUND
|
||||
# include <systemd/sd-daemon.h>
|
||||
#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<Database::Nick>().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<Database::Uuid>();
|
||||
|
||||
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<Database::Date>());
|
||||
|
||||
XmlSubNode submessage(forwarded, "message");
|
||||
submessage["xmlns"] = CLIENT_NS;
|
||||
submessage["from"] = from + "/" + log_line.nick.value();
|
||||
submessage["from"] = from + "/" + log_line.col<Database::Nick>();
|
||||
submessage["type"] = "groupchat";
|
||||
|
||||
XmlSubNode body(submessage, "body");
|
||||
body.set_inner(log_line.body.value());
|
||||
body.set_inner(log_line.col<Database::Body>());
|
||||
}
|
||||
this->send_stanza(message);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <database/database.hpp>
|
||||
#include <xmpp/xmpp_component.hpp>
|
||||
#include <xmpp/jid.hpp>
|
||||
|
||||
@@ -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);
|
||||
|
||||
+22
-24
@@ -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<db::IrcServerOptions>());
|
||||
CHECK(1 == Database::count(Database::irc_server_options));
|
||||
|
||||
b.update();
|
||||
CHECK(2 == Database::count<db::IrcServerOptions>());
|
||||
b.save(Database::db);
|
||||
CHECK(2 == Database::count(Database::irc_server_options));
|
||||
|
||||
CHECK(b.pass == "");
|
||||
CHECK(b.pass.value() == "");
|
||||
CHECK(b.col<Database::Pass>() == "");
|
||||
}
|
||||
|
||||
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<Database::EncodingIn>() == "");
|
||||
o.col<Database::EncodingIn>() = "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<Database::EncodingIn>() == "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<Database::EncodingIn>() = "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<Database::EncodingIn>() == "channelEncoding");
|
||||
}
|
||||
}
|
||||
GIVEN("An option defined for the server but not the channel")
|
||||
{
|
||||
s.encodingIn = "serverEncoding";
|
||||
s.update();
|
||||
s.col<Database::EncodingIn>() = "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<Database::EncodingIn>() == "serverEncoding");
|
||||
}
|
||||
}
|
||||
GIVEN("An option defined for both the server and the channel")
|
||||
{
|
||||
s.encodingIn = "serverEncoding";
|
||||
s.update();
|
||||
c.encodingIn = "channelEncoding";
|
||||
c.update();
|
||||
s.col<Database::EncodingIn>() = "serverEncoding";
|
||||
s.save(Database::db);
|
||||
c.col<Database::EncodingIn>() = "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<Database::EncodingIn>() == "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<Database::EncodingIn>() == "serverEncoding");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
|
||||
Reference in New Issue
Block a user