Merge branch 'orm' into 'master'

Pure c++ sqlite3 ORM

Closes #3271

See merge request !11
This commit is contained in:
louiz
2017-06-14 11:11:17 +02:00
37 changed files with 1170 additions and 627 deletions
+11 -14
View File
@@ -22,7 +22,7 @@ variables:
UDNS: "-DWITH_UDNS=1" UDNS: "-DWITH_UDNS=1"
SYSTEMD: "-DWITH_SYSTEMD=1" SYSTEMD: "-DWITH_SYSTEMD=1"
LIBIDN: "-DWITH_LIBIDN=1" LIBIDN: "-DWITH_LIBIDN=1"
LITESQL: "-DWITH_LITESQL=1" SQLITE3: "-DWITH_SQLITE3=1"
# #
## Build jobs ## Build jobs
@@ -33,8 +33,8 @@ variables:
tags: tags:
- docker - docker
script: script:
- "echo Running cmake with the following parameters: -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} ${LITESQL} - cmake .. -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BOTAN} ${UDNS} ${SYSTEMD} ${LIBIDN} ${SQLITE3}
- make everything -j$(nproc || echo 1) - make everything -j$(nproc || echo 1)
- make coverage_check -j$(nproc || echo 1) - make coverage_check -j$(nproc || echo 1)
artifacts: artifacts:
@@ -77,19 +77,19 @@ build:2:
build:3: build:3:
variables: variables:
LITESQL: "-DWITHOUT_LITESQL=1" SQLITE3: "-DWITHOUT_SQLITE3=1"
<<: *fedora_build <<: *fedora_build
build:4: build:4:
variables: variables:
LITESQL: "-DWITHOUT_LITESQL=1" SQLITE3: "-DWITHOUT_SQLITE3=1"
BOTAN: "-DWITHOUT_BOTAN=1" BOTAN: "-DWITHOUT_BOTAN=1"
LIBIDN: "-DWITHOUT_LIBIDN=1" LIBIDN: "-DWITHOUT_LIBIDN=1"
<<: *fedora_build <<: *fedora_build
build:5: build:5:
variables: variables:
LITESQL: "-DWITHOUT_LITESQL=1" SQLITE3: "-DWITHOUT_SQLITE3=1"
UDNS: "-DWITHOUT_UDNS=1" UDNS: "-DWITHOUT_UDNS=1"
<<: *fedora_build <<: *fedora_build
@@ -156,14 +156,14 @@ test:alpine:
test:freebsd: test:freebsd:
only: only:
- master@louiz/biboumi - branches@louiz/biboumi
tags: tags:
- freebsd - freebsd
variables: variables:
SYSTEMD: "-DWITHOUT_SYSTEMD=1" SYSTEMD: "-DWITHOUT_SYSTEMD=1"
stage: test stage: test
script: 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 check
- make e2e - make e2e
@@ -240,7 +240,7 @@ codecov:build:7:
coverity: coverity:
stage: external stage: external
only: only:
- master@louiz/biboumi - branches@louiz/biboumi
tags: tags:
- docker - docker
image: docker.louiz.org/biboumi-test-fedora:latest image: docker.louiz.org/biboumi-test-fedora:latest
@@ -298,16 +298,13 @@ packaging:deb:
packaging:archlinux: packaging:archlinux:
stage: packaging stage: packaging
only:
- master@louiz/biboumi
tags: tags:
- docker - docker
image: docker.louiz.org/biboumi-test-archlinux:latest image: docker.louiz.org/biboumi-test-archlinux:latest
before_script: [] before_script: []
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 - git clone https://aur.archlinux.org/biboumi-git.git
- cd biboumi-git - cd biboumi-git
- makepkg -si --noconfirm - makepkg -si --noconfirm
+6
View File
@@ -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 Version 5.0 - 2017-05-24
======================== ========================
+11 -20
View File
@@ -124,10 +124,10 @@ elseif(NOT WITHOUT_UDNS)
find_package(UDNS) find_package(UDNS)
endif() endif()
if(WITH_LITESQL) if(WITH_SQLITE3)
find_package(LITESQL REQUIRED) find_package(SQLITE3 REQUIRED)
elseif(NOT WITHOUT_LITESQL) elseif(NOT WITHOUT_SQLITE3)
find_package(LITESQL) find_package(SQLITE3)
endif() endif()
# #
@@ -158,17 +158,14 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}/")
file(GLOB source_utils file(GLOB source_utils
src/utils/*.[hc]pp) src/utils/*.[hc]pp)
add_library(utils OBJECT ${source_utils}) add_library(utils OBJECT ${source_utils})
add_dependencies(utils litesql_generated_sources)
file(GLOB source_irc file(GLOB source_irc
src/irc/*.[hc]pp) src/irc/*.[hc]pp)
add_library(irc OBJECT ${source_irc}) add_library(irc OBJECT ${source_irc})
add_dependencies(irc litesql_generated_sources)
file(GLOB source_xmpp file(GLOB source_xmpp
src/xmpp/*.[hc]pp) src/xmpp/*.[hc]pp)
add_library(xmpp OBJECT ${source_xmpp}) add_library(xmpp OBJECT ${source_xmpp})
add_dependencies(xmpp litesql_generated_sources)
file(GLOB source_identd file(GLOB source_identd
src/identd/*.[hc]pp) src/identd/*.[hc]pp)
@@ -177,7 +174,6 @@ add_library(identd OBJECT ${source_identd})
file(GLOB source_bridge file(GLOB source_bridge
src/bridge/*.[hc]pp) src/bridge/*.[hc]pp)
add_library(bridge OBJECT ${source_bridge}) add_library(bridge OBJECT ${source_bridge})
add_dependencies(bridge litesql_generated_sources)
file(GLOB source_config file(GLOB source_config
src/config/*.[hc]pp) src/config/*.[hc]pp)
@@ -191,20 +187,15 @@ file(GLOB source_network
src/network/*.[hc]pp) src/network/*.[hc]pp)
add_library(network OBJECT ${source_network}) add_library(network OBJECT ${source_network})
if(LITESQL_FOUND) if(SQLITE3_FOUND)
LITESQL_GENERATE_CPP("database/database.xml" file(GLOB source_database
"biboudb" src/database/*.[hc]pp)
LITESQL_GENERATED_SOURCES) add_library(database OBJECT ${source_database})
add_custom_target(litesql_generated_sources SOURCES ${LITESQL_GENERATED_SOURCES})
add_library(database OBJECT src/database/database.cpp ${LITESQL_GENERATED_SOURCES}) include_directories(database ${SQLITE3_INCLUDE_DIRS})
add_dependencies(database litesql_generated_sources)
include_directories(database ${LITESQL_INCLUDE_DIRS})
set(USE_DATABASE TRUE) set(USE_DATABASE TRUE)
else() else()
add_library(database OBJECT "") add_library(database OBJECT "")
add_custom_target(litesql_generated_sources)
endif() endif()
# #
@@ -269,8 +260,8 @@ if(LIBIDN_FOUND)
target_link_libraries(test_suite ${LIBIDN_LIBRARIES}) target_link_libraries(test_suite ${LIBIDN_LIBRARIES})
endif() endif()
if(USE_DATABASE) if(USE_DATABASE)
target_link_libraries(${PROJECT_NAME} ${LITESQL_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${SQLITE3_LIBRARIES})
target_link_libraries(test_suite ${LITESQL_LIBRARIES}) target_link_libraries(test_suite ${SQLITE3_LIBRARIES})
endif() endif()
# Define a __FILENAME__ macro with the relative path (from the base project directory) # Define a __FILENAME__ macro with the relative path (from the base project directory)
+2 -2
View File
@@ -52,8 +52,8 @@ There are two test suites for biboumi:
uses a specific IRC server (`charybdis`_), and only tests the most complete uses a specific IRC server (`charybdis`_), and only tests the most complete
biboumi configuration (when all dependencies are used). To run it, you need biboumi configuration (when all dependencies are used). To run it, you need
to install various dependencies: refer to fedora’s `Dockerfile.base`_ and to install various dependencies: refer to fedora’s `Dockerfile.base`_ and
`Dockerfile`_ to see how to install charybdis, slixmpp, botan, litesql, an `Dockerfile`_ to see how to install charybdis, slixmpp, botan, a ssl
ssl certificate, etc. certificate, etc.
Once all the dependencies are correctly installed, the tests are run with Once all the dependencies are correctly installed, the tests are run with
+7 -6
View File
@@ -32,6 +32,12 @@ libiconv_
libuuid_ libuuid_
Generate unique IDs 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) libidn_ (optional, but recommended)
Provides the stringprep functionality. Without it, JIDs for IRC users are Provides the stringprep functionality. Without it, JIDs for IRC users are
not provided. not provided.
@@ -48,11 +54,6 @@ libbotan_ 1.11 or 2.0 (optional)
gcrypt_ (mandatory only if botan is absent) gcrypt_ (mandatory only if botan is absent)
Provides the SHA-1 hash function, for the case where 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) systemd_ (optional)
Provides the support for a systemd service of Type=notify. This is useful only Provides the support for a systemd service of Type=notify. This is useful only
if you are packaging biboumi in a distribution with Systemd. if you are packaging biboumi in a distribution with Systemd.
@@ -160,7 +161,7 @@ to use biboumi.
.. _libidn: http://www.gnu.org/software/libidn/ .. _libidn: http://www.gnu.org/software/libidn/
.. _libbotan: http://botan.randombit.net/ .. _libbotan: http://botan.randombit.net/
.. _udns: http://www.corpit.ru/mjt/udns.html .. _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/ .. _systemd: https://www.freedesktop.org/wiki/Software/systemd/
.. _biboumi.1.rst: doc/biboumi.1.rst .. _biboumi.1.rst: doc/biboumi.1.rst
.. _gcrypt: https://www.gnu.org/software/libgcrypt/ .. _gcrypt: https://www.gnu.org/software/libgcrypt/
-76
View File
@@ -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()
+43
View File
@@ -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)
-70
View File
@@ -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>
+47 -3
View File
@@ -1,10 +1,54 @@
# This Dockerfile creates a docker image suitable to run biboumi’s build and # This Dockerfile creates a docker image suitable to run biboumi’s build and
# tests. For example, it can be used on with gitlab-ci. # 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 ENV LC_ALL C.UTF-8
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
# 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 WORKDIR /home/tester
USER 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
+53 -3
View File
@@ -1,10 +1,60 @@
# This Dockerfile creates a docker image suitable to run biboumi’s build and # This Dockerfile creates a docker image suitable to run biboumi’s build and
# tests. For example, it can be used on with gitlab-ci. # 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 ENV LC_ALL C.UTF-8
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
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 WORKDIR /home/tester
USER 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
+55 -3
View File
@@ -1,10 +1,62 @@
# This Dockerfile creates a docker image suitable to run biboumi’s build and # This Dockerfile creates a docker image suitable to run biboumi’s build and
# tests. For example, it can be used on with gitlab-ci. # 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 ENV LC_ALL C.UTF-8
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
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 WORKDIR /home/tester
USER 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
+1 -4
View File
@@ -18,15 +18,12 @@ RUN apk add --no-cache\
# Install botan # Install botan
RUN git clone https://github.com/randombit/botan.git && cd botan && ./configure.py --prefix=/usr && make -j8 && make install && rm -rf /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 # Install biboumi
RUN git clone git://git.louiz.org/biboumi && mkdir ./biboumi/build && cd ./biboumi/build &&\ RUN git clone git://git.louiz.org/biboumi && mkdir ./biboumi/build && cd ./biboumi/build &&\
cmake .. -DCMAKE_INSTALL_PREFIX=/usr\ cmake .. -DCMAKE_INSTALL_PREFIX=/usr\
-DCMAKE_BUILD_TYPE=Release\ -DCMAKE_BUILD_TYPE=Release\
-DWITH_BOTAN=1\ -DWITH_BOTAN=1\
-DWITH_LITESQL=1\ -DWITH_SQLITE3=1\
-DWITH_LIBIDN=1\ -DWITH_LIBIDN=1\
&& make -j8 && make install && rm -rf /biboumi && make -j8 && make install && rm -rf /biboumi
+11 -9
View File
@@ -23,12 +23,14 @@ static std::string in_encoding_for(const Bridge& bridge, const Iid& iid)
#ifdef USE_DATABASE #ifdef USE_DATABASE
const auto jid = bridge.get_bare_jid(); const auto jid = bridge.get_bare_jid();
auto options = Database::get_irc_channel_options_with_server_default(jid, iid.get_server(), iid.get_local()); 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 #else
(void)bridge; (void)bridge;
(void)iid; (void)iid;
return {"ISO-8859-1"};
#endif #endif
return {"ISO-8859-1"};
} }
Bridge::Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr<Poller>& poller): 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 #ifdef USE_DATABASE
const auto options = Database::get_global_options(this->user_jid); 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 #endif
} }
@@ -258,7 +260,7 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body)
#ifdef USE_DATABASE #ifdef USE_DATABASE
const auto xmpp_body = this->make_xmpp_body(line); const auto xmpp_body = this->make_xmpp_body(line);
if (this->record_history) 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()); std::get<0>(xmpp_body), irc->get_own_nick());
#endif #endif
for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) 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 #ifdef USE_DATABASE
const auto coptions = Database::get_irc_channel_options_with_server_default(this->user_jid, const auto coptions = Database::get_irc_channel_options_with_server_default(this->user_jid,
iid.get_server(), iid.get_local()); iid.get_server(), iid.get_local());
persistent = coptions.persistent.value(); persistent = coptions.col<Database::Persistent>();
#endif #endif
if (channel->joined && !channel->parting && !persistent) 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 #ifdef USE_DATABASE
const auto xmpp_body = this->make_xmpp_body(body, encoding); const auto xmpp_body = this->make_xmpp_body(body, encoding);
if (!nick.empty() && this->record_history) 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); std::get<0>(xmpp_body), nick);
#endif #endif
for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) 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 #ifdef USE_DATABASE
const auto coptions = Database::get_irc_channel_options_with_server_and_global_default(this->user_jid, hostname, chan_name); 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)); chan_name.append(utils::empty_if_fixed_server("%" + hostname));
for (const auto& line: lines) for (const auto& line: lines)
{ {
const auto seconds = line.date.value().timeStamp(); const auto seconds = line.col<Database::Date>();
this->xmpp.send_history_message(chan_name, line.nick.value(), line.body.value(), this->xmpp.send_history_message(chan_name, line.col<Database::Nick>(), line.col<Database::Body>(),
this->user_jid + "/" + resource, seconds); this->user_jid + "/" + resource, seconds);
} }
#else #else
+17
View File
@@ -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"; };
+35
View File
@@ -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
View File
@@ -2,175 +2,166 @@
#ifdef USE_DATABASE #ifdef USE_DATABASE
#include <database/database.hpp> #include <database/database.hpp>
#include <logger/logger.hpp>
#include <irc/iid.hpp>
#include <uuid/uuid.h> #include <uuid/uuid.h>
#include <utils/get_first_non_empty.hpp> #include <utils/get_first_non_empty.hpp>
#include <utils/time.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 res = sqlite3_open_v2(filename.data(), &Database::db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
{ log_debug("open: ", res);
auto new_db = std::make_unique<db::BibouDB>(db_type, Database::muc_log_lines.create(Database::db);
"database="s + filename); Database::global_options.create(Database::db);
if (new_db->needsUpgrade()) Database::irc_server_options.create(Database::db);
new_db->upgrade(); Database::irc_channel_options.create(Database::db);
Database::db = std::move(new_db);
} catch (const litesql::DatabaseError& e) {
log_error("Failed to open database ", filename, ". ", e.what());
throw;
}
} }
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 request = Database::global_options.select().where() << Owner{} << "=" << owner;
auto options = litesql::select<db::GlobalOptions>(*Database::db,
db::GlobalOptions::Owner == owner).one(); Database::GlobalOptions options{Database::global_options.get_name()};
return options; auto result = request.execute(Database::db);
} catch (const litesql::NotFound& e) { if (result.size() == 1)
db::GlobalOptions options(*Database::db); options = result.front();
options.owner = owner; else
options.col<Owner>() = owner;
return options; return options;
} }
}
db::IrcServerOptions Database::get_irc_server_options(const std::string& owner, Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server)
const std::string& server)
{ {
try { auto request = Database::irc_server_options.select().where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server;
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;
}
}
db::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, Database::IrcServerOptions options{Database::irc_server_options.get_name()};
const std::string& server, auto result = request.execute(Database::db);
const std::string& channel) if (result.size() == 1)
options = result.front();
else
{ {
try { options.col<Owner>() = owner;
auto options = litesql::select<db::IrcChannelOptions>(*Database::db, options.col<Server>() = server;
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;
} }
return options;
} }
db::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel)
const std::string& server, {
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) const std::string& channel)
{ {
auto coptions = Database::get_irc_channel_options(owner, server, channel); auto coptions = Database::get_irc_channel_options(owner, server, channel);
auto soptions = Database::get_irc_server_options(owner, server); auto soptions = Database::get_irc_server_options(owner, server);
coptions.encodingIn = get_first_non_empty(coptions.encodingIn.value(), coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
soptions.encodingIn.value()); soptions.col<EncodingIn>());
coptions.encodingOut = get_first_non_empty(coptions.encodingOut.value(), coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
soptions.encodingOut.value()); soptions.col<EncodingOut>());
coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(), coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
soptions.maxHistoryLength.value()); soptions.col<MaxHistoryLength>());
return coptions; return coptions;
} }
db::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, const std::string& server, const std::string& channel)
const std::string& server,
const std::string& channel)
{ {
auto coptions = Database::get_irc_channel_options(owner, server, channel); auto coptions = Database::get_irc_channel_options(owner, server, channel);
auto soptions = Database::get_irc_server_options(owner, server); auto soptions = Database::get_irc_server_options(owner, server);
auto goptions = Database::get_global_options(owner); auto goptions = Database::get_global_options(owner);
coptions.encodingIn = get_first_non_empty(coptions.encodingIn.value(), coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
soptions.encodingIn.value()); soptions.col<EncodingIn>());
coptions.encodingOut = get_first_non_empty(coptions.encodingOut.value(),
soptions.encodingOut.value());
coptions.maxHistoryLength = get_first_non_empty(coptions.maxHistoryLength.value(), coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
soptions.maxHistoryLength.value(), soptions.col<EncodingOut>());
goptions.maxHistoryLength.value());
coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
soptions.col<MaxHistoryLength>(),
goptions.col<MaxHistoryLength>());
return coptions; return coptions;
} }
std::string Database::store_muc_message(const std::string& owner, const Iid& iid, std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name,
Database::time_point date, const std::string& server_name, Database::time_point date,
const std::string& body, const std::string& body, const std::string& nick)
const std::string& nick)
{ {
db::MucLogLine line(*Database::db); auto line = Database::muc_log_lines.row();
auto uuid = Database::gen_uuid(); auto uuid = Database::gen_uuid();
line.uuid = uuid; line.col<Uuid>() = uuid;
line.owner = owner; line.col<Owner>() = owner;
line.ircChanName = iid.get_local(); line.col<IrcChanName>() = chan_name;
line.ircServerName = iid.get_server(); line.col<IrcServerName>() = server_name;
line.date = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count(); line.col<Date>() = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count();
line.body = body; line.col<Body>() = body;
line.nick = nick; line.col<Nick>() = nick;
line.update(); line.save(Database::db);
return uuid; 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) int limit, const std::string& start, const std::string& end)
{ {
auto request = litesql::select<db::MucLogLine>(*Database::db, auto request = Database::muc_log_lines.select().where() << Database::Owner{} << "=" << owner << \
db::MucLogLine::Owner == owner && " and " << Database::IrcChanName{} << "=" << chan_name << \
db::MucLogLine::IrcChanName == chan_name && " and " << Database::IrcServerName{} << "=" << server;
db::MucLogLine::IrcServerName == server);
request.orderBy(db::MucLogLine::Id, false);
if (limit >= 0)
request.limit(limit);
if (!start.empty()) if (!start.empty())
{ {
const auto start_time = utils::parse_datetime(start); const auto start_time = utils::parse_datetime(start);
if (start_time != -1) if (start_time != -1)
request.where(db::MucLogLine::Date >= start_time); request << " and " << Database::Date{} << ">=" << start_time;
} }
if (!end.empty()) if (!end.empty())
{ {
const auto end_time = utils::parse_datetime(end); const auto end_time = utils::parse_datetime(end);
if (end_time != -1) 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() void Database::close()
{ {
Database::db.reset(nullptr); sqlite3_close_v2(Database::db);
} }
std::string Database::gen_uuid() std::string Database::gen_uuid()
@@ -182,5 +173,4 @@ std::string Database::gen_uuid()
return uuid_str; return uuid_str;
} }
#endif #endif
+111 -28
View File
@@ -1,22 +1,107 @@
#pragma once #pragma once
#include <biboumi.h> #include <biboumi.h>
#ifdef USE_DATABASE #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 <memory>
#include <litesql.hpp>
#include <chrono>
class Iid;
class Database class Database
{ {
public: public:
using time_point = std::chrono::system_clock::time_point; 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;
~Database() = default; ~Database() = default;
@@ -25,42 +110,40 @@ public:
Database& operator=(const Database&) = delete; Database& operator=(const Database&) = delete;
Database& operator=(Database&&) = delete; Database& operator=(Database&&) = delete;
static void set_verbose(const bool val); static GlobalOptions get_global_options(const std::string& owner);
static IrcServerOptions get_irc_server_options(const std::string& owner,
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,
const std::string& server); 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& server,
const std::string& channel); 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& server,
const std::string& channel); 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& server,
const std::string& channel); 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=""); 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); time_point date, const std::string& body, const std::string& nick);
static void close(); 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: private:
static std::string gen_uuid(); static std::string gen_uuid();
static std::unique_ptr<db::BibouDB> db;
}; };
#endif /* USE_DATABASE */ #endif /* USE_DATABASE */
+129
View File
@@ -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:
};
+11
View File
@@ -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);
}
+48
View File
@@ -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);
+75
View File
@@ -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;
};
+153
View File
@@ -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;
}
+35
View File
@@ -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;
};
+84
View File
@@ -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;
};
+8
View File
@@ -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";
+13
View File
@@ -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
View File
@@ -156,11 +156,11 @@ IrcClient::IrcClient(std::shared_ptr<Poller>& poller, std::string hostname,
#ifdef USE_DATABASE #ifdef USE_DATABASE
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
this->get_hostname()); 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) for (auto it = ports.rbegin(); it != ports.rend(); ++it)
this->ports_to_try.emplace(*it, false); this->ports_to_try.emplace(*it, false);
# ifdef BOTAN_FOUND # 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) for (auto it = ports.rbegin(); it != ports.rend(); ++it)
this->ports_to_try.emplace(*it, true); this->ports_to_try.emplace(*it, true);
# endif // BOTAN_FOUND # endif // BOTAN_FOUND
@@ -204,7 +204,7 @@ void IrcClient::start()
# ifdef USE_DATABASE # ifdef USE_DATABASE
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
this->get_hostname()); this->get_hostname());
this->credential_manager.set_trusted_fingerprint(options.trustedFingerprint); this->credential_manager.set_trusted_fingerprint(options.col<Database::TrustedFingerprint>());
# endif # endif
#endif #endif
this->connect(this->hostname, port, tls); this->connect(this->hostname, port, tls);
@@ -275,8 +275,8 @@ void IrcClient::on_connected()
#ifdef USE_DATABASE #ifdef USE_DATABASE
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
this->get_hostname()); this->get_hostname());
if (!options.pass.value().empty()) if (!options.col<Database::Pass>().empty())
this->send_pass_command(options.pass.value()); this->send_pass_command(options.col<Database::Pass>());
#endif #endif
this->send_nick_command(this->current_nick); this->send_nick_command(this->current_nick);
@@ -284,10 +284,10 @@ void IrcClient::on_connected()
#ifdef USE_DATABASE #ifdef USE_DATABASE
if (Config::get("realname_customization", "true") == "true") if (Config::get("realname_customization", "true") == "true")
{ {
if (!options.username.value().empty()) if (!options.col<Database::Username>().empty())
this->username = options.username.value(); this->username = options.col<Database::Username>();
if (!options.realname.value().empty()) if (!options.col<Database::Realname>().empty())
this->realname = options.realname.value(); this->realname = options.col<Database::Realname>();
this->send_user_command(username, realname); this->send_user_command(username, realname);
} }
else else
@@ -894,8 +894,8 @@ void IrcClient::on_welcome_message(const IrcMessage& message)
#ifdef USE_DATABASE #ifdef USE_DATABASE
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
this->get_hostname()); this->get_hostname());
if (!options.afterConnectionCommand.value().empty()) if (!options.col<Database::AfterConnectionCommand>().empty())
this->send_raw(options.afterConnectionCommand.value()); this->send_raw(options.col<Database::AfterConnectionCommand>());
#endif #endif
// Install a repeated events to regularly send a PING // Install a repeated events to regularly send a PING
TimedEventsManager::instance().add_event(TimedEvent(240s, std::bind(&IrcClient::send_ping_command, this), 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 #ifdef USE_DATABASE
auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->hostname); auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->hostname);
return options.verifyCert.value(); return options.col<Database::VerifyCert>();
#endif #endif
return true; return true;
} }
+1 -4
View File
@@ -12,9 +12,6 @@
#include <atomic> #include <atomic>
#include <csignal> #include <csignal>
#ifdef USE_DATABASE
# include <litesql.hpp>
#endif
#include <identd/identd_server.hpp> #include <identd/identd_server.hpp>
@@ -91,7 +88,7 @@ int main(int ac, char** av)
#ifdef USE_DATABASE #ifdef USE_DATABASE
try { try {
open_database(); open_database();
} catch (const litesql::DatabaseError&) { } catch (...) {
return 1; return 1;
} }
#endif #endif
+2 -1
View File
@@ -11,6 +11,7 @@ void open_database()
#ifdef USE_DATABASE #ifdef USE_DATABASE
const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite")); const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite"));
log_info("Opening database: ", db_filename); log_info("Opening database: ", db_filename);
Database::close();
Database::open(db_filename); Database::open(db_filename);
log_info("database successfully opened."); log_info("database successfully opened.");
#endif #endif
@@ -26,7 +27,7 @@ void reload_process()
#ifdef USE_DATABASE #ifdef USE_DATABASE
try { try {
open_database(); open_database();
} catch (const litesql::DatabaseError&) { } catch (...) {
log_warning("Re-using the previous database."); log_warning("Re-using the previous database.");
} }
#endif #endif
+43 -45
View File
@@ -130,7 +130,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
{ {
XmlSubNode value(max_histo_length, "value"); 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"); XmlSubNode record_history(x, "field");
@@ -142,7 +142,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
{ {
XmlSubNode value(record_history, "value"); XmlSubNode value(record_history, "value");
value.set_name("value"); value.set_name("value");
if (options.recordHistory.value()) if (options.col<Database::RecordHistory>())
value.set_inner("true"); value.set_inner("true");
else else
value.set_inner("false"); value.set_inner("false");
@@ -164,18 +164,18 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session,
if (field->get_tag("var") == "max_history_length" && if (field->get_tag("var") == "max_history_length" &&
value && !value->get_inner().empty()) 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" && else if (field->get_tag("var") == "record_history" &&
value && !value->get_inner().empty()) 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()); Bridge* bridge = biboumi_component.find_user_bridge(owner.bare());
if (bridge) 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(); command_node.delete_all_children();
XmlSubNode note(command_node, "note"); XmlSubNode note(command_node, "note");
@@ -211,8 +211,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
ports["type"] = "text-multi"; ports["type"] = "text-multi";
ports["label"] = "Ports"; ports["label"] = "Ports";
ports["desc"] = "List of ports to try, without TLS. Defaults: 6667."; ports["desc"] = "List of ports to try, without TLS. Defaults: 6667.";
auto vals = utils::split(options.ports.value(), ';', false); for (const auto& val: utils::split(options.col<Database::Ports>(), ';', false))
for (const auto& val: vals)
{ {
XmlSubNode ports_value(ports, "value"); XmlSubNode ports_value(ports, "value");
ports_value.set_inner(val); ports_value.set_inner(val);
@@ -224,8 +223,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
tls_ports["type"] = "text-multi"; tls_ports["type"] = "text-multi";
tls_ports["label"] = "TLS ports"; tls_ports["label"] = "TLS ports";
tls_ports["desc"] = "List of ports to try, with TLS. Defaults: 6697, 6670."; tls_ports["desc"] = "List of ports to try, with TLS. Defaults: 6697, 6670.";
vals = utils::split(options.tlsPorts.value(), ';', false); for (const auto& val: utils::split(options.col<Database::TlsPorts>(), ';', false))
for (const auto& val: vals)
{ {
XmlSubNode tls_ports_value(tls_ports, "value"); XmlSubNode tls_ports_value(tls_ports, "value");
tls_ports_value.set_inner(val); tls_ports_value.set_inner(val);
@@ -237,7 +235,7 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
verify_cert["label"] = "Verify certificate"; verify_cert["label"] = "Verify certificate";
verify_cert["desc"] = "Whether or not to abort the connection if the server’s TLS certificate is invalid"; 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"); XmlSubNode verify_cert_value(verify_cert, "value");
if (options.verifyCert.value()) if (options.col<Database::VerifyCert>())
verify_cert_value.set_inner("true"); verify_cert_value.set_inner("true");
else else
verify_cert_value.set_inner("false"); verify_cert_value.set_inner("false");
@@ -246,10 +244,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
fingerprint["var"] = "fingerprint"; fingerprint["var"] = "fingerprint";
fingerprint["type"] = "text-single"; fingerprint["type"] = "text-single";
fingerprint["label"] = "SHA-1 fingerprint of the TLS certificate to trust."; 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"); XmlSubNode fingerprint_value(fingerprint, "value");
fingerprint_value.set_inner(options.trustedFingerprint.value()); fingerprint_value.set_inner(options.col<Database::TrustedFingerprint>());
} }
#endif #endif
@@ -258,10 +256,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
pass["type"] = "text-private"; pass["type"] = "text-private";
pass["label"] = "Server password"; pass["label"] = "Server password";
pass["desc"] = "Will be used in a PASS command when connecting"; 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"); 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"); 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["type"] = "text-single";
after_cnt_cmd["desc"] = "Custom IRC command sent after the connection is established with the server."; after_cnt_cmd["desc"] = "Custom IRC command sent after the connection is established with the server.";
after_cnt_cmd["label"] = "After-connection IRC command"; 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"); 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") if (Config::get("realname_customization", "true") == "true")
@@ -281,20 +279,20 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
username["var"] = "username"; username["var"] = "username";
username["type"] = "text-single"; username["type"] = "text-single";
username["label"] = "Username"; username["label"] = "Username";
if (!options.username.value().empty()) if (!options.col<Database::Username>().empty())
{ {
XmlSubNode username_value(username, "value"); XmlSubNode username_value(username, "value");
username_value.set_inner(options.username.value()); username_value.set_inner(options.col<Database::Username>());
} }
XmlSubNode realname(x, "field"); XmlSubNode realname(x, "field");
realname["var"] = "realname"; realname["var"] = "realname";
realname["type"] = "text-single"; realname["type"] = "text-single";
realname["label"] = "Realname"; realname["label"] = "Realname";
if (!options.realname.value().empty()) if (!options.col<Database::Realname>().empty())
{ {
XmlSubNode realname_value(realname, "value"); 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["type"] = "text-single";
encoding_out["desc"] = "The encoding used when sending messages to the IRC server."; encoding_out["desc"] = "The encoding used when sending messages to the IRC server.";
encoding_out["label"] = "Out encoding"; encoding_out["label"] = "Out encoding";
if (!options.encodingOut.value().empty()) if (!options.col<Database::EncodingOut>().empty())
{ {
XmlSubNode encoding_out_value(encoding_out, "value"); 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"); XmlSubNode encoding_in(x, "field");
@@ -314,10 +312,10 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
encoding_in["type"] = "text-single"; encoding_in["type"] = "text-single";
encoding_in["desc"] = "The encoding used to decode message received from the IRC server."; encoding_in["desc"] = "The encoding used to decode message received from the IRC server.";
encoding_in["label"] = "In encoding"; encoding_in["label"] = "In encoding";
if (!options.encodingIn.value().empty()) if (!options.col<Database::EncodingIn>().empty())
{ {
XmlSubNode encoding_in_value(encoding_in, "value"); 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; std::string ports;
for (const auto& val: values) for (const auto& val: values)
ports += val->get_inner() + ";"; ports += val->get_inner() + ";";
options.ports = ports; options.col<Database::Ports>() = ports;
} }
#ifdef BOTAN_FOUND #ifdef BOTAN_FOUND
@@ -351,31 +349,31 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
std::string ports; std::string ports;
for (const auto& val: values) for (const auto& val: values)
ports += val->get_inner() + ";"; ports += val->get_inner() + ";";
options.tlsPorts = ports; options.col<Database::TlsPorts>() = ports;
} }
else if (field->get_tag("var") == "verify_cert" && value else if (field->get_tag("var") == "verify_cert" && value
&& !value->get_inner().empty()) && !value->get_inner().empty())
{ {
auto val = to_bool(value->get_inner()); auto val = to_bool(value->get_inner());
options.verifyCert = val; options.col<Database::VerifyCert>() = val;
} }
else if (field->get_tag("var") == "fingerprint" && value && else if (field->get_tag("var") == "fingerprint" && value &&
!value->get_inner().empty()) !value->get_inner().empty())
{ {
options.trustedFingerprint = value->get_inner(); options.col<Database::TrustedFingerprint>() = value->get_inner();
} }
#endif // BOTAN_FOUND #endif // BOTAN_FOUND
else if (field->get_tag("var") == "pass" && else if (field->get_tag("var") == "pass" &&
value && !value->get_inner().empty()) 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" && else if (field->get_tag("var") == "after_connect_command" &&
value && !value->get_inner().empty()) value && !value->get_inner().empty())
options.afterConnectionCommand = value->get_inner(); options.col<Database::AfterConnectionCommand>() = value->get_inner();
else if (field->get_tag("var") == "username" && else if (field->get_tag("var") == "username" &&
value && !value->get_inner().empty()) value && !value->get_inner().empty())
@@ -383,24 +381,24 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
auto username = value->get_inner(); auto username = value->get_inner();
// The username must not contain spaces // The username must not contain spaces
std::replace(username.begin(), username.end(), ' ', '_'); std::replace(username.begin(), username.end(), ' ', '_');
options.username = username; options.col<Database::Username>() = username;
} }
else if (field->get_tag("var") == "realname" && else if (field->get_tag("var") == "realname" &&
value && !value->get_inner().empty()) 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" && else if (field->get_tag("var") == "encoding_out" &&
value && !value->get_inner().empty()) 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" && else if (field->get_tag("var") == "encoding_in" &&
value && !value->get_inner().empty()) 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(); command_node.delete_all_children();
XmlSubNode note(command_node, "note"); 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["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["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"; encoding_out["label"] = "Out encoding";
if (!options.encodingOut.value().empty()) if (!options.col<Database::EncodingOut>().empty())
{ {
XmlSubNode encoding_out_value(encoding_out, "value"); 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"); 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["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["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"; encoding_in["label"] = "In encoding";
if (!options.encodingIn.value().empty()) if (!options.col<Database::EncodingIn>().empty())
{ {
XmlSubNode encoding_in_value(encoding_in, "value"); 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"); XmlSubNode persistent(x, "field");
@@ -466,7 +464,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
{ {
XmlSubNode value(persistent, "value"); XmlSubNode value(persistent, "value");
value.set_name("value"); value.set_name("value");
if (options.persistent.value()) if (options.col<Database::Persistent>())
value.set_inner("true"); value.set_inner("true");
else else
value.set_inner("false"); 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" && if (field->get_tag("var") == "encoding_out" &&
value && !value->get_inner().empty()) 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" && else if (field->get_tag("var") == "encoding_in" &&
value && !value->get_inner().empty()) value && !value->get_inner().empty())
options.encodingIn = value->get_inner(); options.col<Database::EncodingIn>() = value->get_inner();
else if (field->get_tag("var") == "persistent" && else if (field->get_tag("var") == "persistent" &&
value) 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; return true;
} }
+7 -9
View File
@@ -18,8 +18,6 @@
#include <biboumi.h> #include <biboumi.h>
#include <uuid/uuid.h>
#ifdef SYSTEMD_FOUND #ifdef SYSTEMD_FOUND
# include <systemd/sd-daemon.h> # include <systemd/sd-daemon.h>
#endif #endif
@@ -648,9 +646,9 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza)
limit = 100; limit = 100;
} }
const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); 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_archived_message(line, to.full(), from.full(), query_id);
} }
this->send_iq_result_full_jid(id, from.full(), to.full()); 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; 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) const std::string& queryid)
{ {
Stanza message("message"); Stanza message("message");
@@ -671,22 +669,22 @@ void BiboumiComponent::send_archived_message(const db::MucLogLine& log_line, con
result["xmlns"] = MAM_NS; result["xmlns"] = MAM_NS;
if (!queryid.empty()) if (!queryid.empty())
result["queryid"] = queryid; result["queryid"] = queryid;
result["id"] = log_line.uuid.value(); result["id"] = log_line.col<Database::Uuid>();
XmlSubNode forwarded(result, "forwarded"); XmlSubNode forwarded(result, "forwarded");
forwarded["xmlns"] = FORWARD_NS; forwarded["xmlns"] = FORWARD_NS;
XmlSubNode delay(forwarded, "delay"); XmlSubNode delay(forwarded, "delay");
delay["xmlns"] = DELAY_NS; 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"); XmlSubNode submessage(forwarded, "message");
submessage["xmlns"] = CLIENT_NS; submessage["xmlns"] = CLIENT_NS;
submessage["from"] = from + "/" + log_line.nick.value(); submessage["from"] = from + "/" + log_line.col<Database::Nick>();
submessage["type"] = "groupchat"; submessage["type"] = "groupchat";
XmlSubNode body(submessage, "body"); XmlSubNode body(submessage, "body");
body.set_inner(log_line.body.value()); body.set_inner(log_line.col<Database::Body>());
} }
this->send_stanza(message); this->send_stanza(message);
} }
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <database/database.hpp>
#include <xmpp/xmpp_component.hpp> #include <xmpp/xmpp_component.hpp>
#include <xmpp/jid.hpp> #include <xmpp/jid.hpp>
@@ -96,7 +96,7 @@ public:
#ifdef USE_DATABASE #ifdef USE_DATABASE
bool handle_mam_request(const Stanza& stanza); 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); 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_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); bool handle_room_configuration_form(const XmlNode& query, const std::string& from, const Jid& to, const std::string& id);
+22 -24
View File
@@ -8,24 +8,22 @@ TEST_CASE("Database")
{ {
#ifdef USE_DATABASE #ifdef USE_DATABASE
Database::open(":memory:"); Database::open(":memory:");
Database::set_verbose(false);
SECTION("Basic retrieve and update") SECTION("Basic retrieve and update")
{ {
auto o = Database::get_irc_server_options("zouzou@example.com", "irc.example.com"); 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 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"); 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 // b does not yet exist in the db, the object is created but not yet
// inserted // inserted
CHECK(1 == Database::count<db::IrcServerOptions>()); CHECK(1 == Database::count(Database::irc_server_options));
b.update(); b.save(Database::db);
CHECK(2 == Database::count<db::IrcServerOptions>()); CHECK(2 == Database::count(Database::irc_server_options));
CHECK(b.pass == ""); CHECK(b.col<Database::Pass>() == "");
CHECK(b.pass.value() == "");
} }
SECTION("channel options") SECTION("channel options")
@@ -33,16 +31,16 @@ TEST_CASE("Database")
Config::set("db_name", ":memory:"); Config::set("db_name", ":memory:");
auto o = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo"); auto o = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo");
CHECK(o.encodingIn == ""); CHECK(o.col<Database::EncodingIn>() == "");
o.encodingIn = "ISO-8859-1"; o.col<Database::EncodingIn>() = "ISO-8859-1";
o.update(); o.save(Database::db);
auto b = Database::get_irc_channel_options("zouzou@example.com", "irc.example.com", "#foo"); 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") 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 server{"irc.example.com"};
const std::string chan1{"#foo"}; const std::string chan1{"#foo"};
@@ -51,43 +49,43 @@ TEST_CASE("Database")
GIVEN("An option defined for the channel but not the server") GIVEN("An option defined for the channel but not the server")
{ {
c.encodingIn = "channelEncoding"; c.col<Database::EncodingIn>() = "channelEncoding";
c.update(); c.save(Database::db);
WHEN("we fetch that option") WHEN("we fetch that option")
{ {
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the channel option") 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") GIVEN("An option defined for the server but not the channel")
{ {
s.encodingIn = "serverEncoding"; s.col<Database::EncodingIn>() = "serverEncoding";
s.update(); s.save(Database::db);
WHEN("we fetch that option") WHEN("we fetch that option")
{ {
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the server option") 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") GIVEN("An option defined for both the server and the channel")
{ {
s.encodingIn = "serverEncoding"; s.col<Database::EncodingIn>() = "serverEncoding";
s.update(); s.save(Database::db);
c.encodingIn = "channelEncoding"; c.col<Database::EncodingIn>() = "channelEncoding";
c.update(); c.save(Database::db);
WHEN("we fetch that option") WHEN("we fetch that option")
{ {
auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1); auto r = Database::get_irc_channel_options_with_server_default(owner, server, chan1);
THEN("we get the channel option") 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") WHEN("we fetch that option, with no channel specified")
{ {
auto r = Database::get_irc_channel_options_with_server_default(owner, server, ""); auto r = Database::get_irc_channel_options_with_server_default(owner, server, "");
THEN("we get the server option") THEN("we get the server option")
CHECK(r.encodingIn == "serverEncoding"); CHECK(r.col<Database::EncodingIn>() == "serverEncoding");
} }
} }
} }
+1 -1
View File
@@ -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='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='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_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", "/iq/commands:command/commands:actions/commands:next",
), ),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid")) after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))